繁体   English   中英

Java中的通配符匹配

[英]Wildcard matching in Java

我正在编写一个简单的调试程序,它将简单的字符串作为输入,这些字符串可以包含星号以指示通配符匹配

*.wav  // matches <anything>.wav
(*, a) // matches (<anything>, a)

我想我会简单地采用该模式,转义其中的任何正则表达式特殊字符,然后将任何\\*替换回.* 然后使用正则表达式匹配器。

但是我找不到任何 Java function 来转义正则表达式。 我能找到的最佳匹配是Pattern.quote ,但是它只是将\Q\E放在字符串的开头和结尾。

Java 中是否有任何东西可以让您简单地进行通配符匹配,而无需从头开始实现算法?

逃避一切 - 不会有任何伤害。

    String input = "*.wav";
    String regex = ("\\Q" + input + "\\E").replace("*", "\\E.*\\Q");
    System.out.println(regex); // \Q\E.*\Q.wav\E
    System.out.println("abcd.wav".matches(regex)); // true

或者您可以使用字符类:

    String input = "*.wav";
    String regex = input.replaceAll(".", "[$0]").replace("[*]", ".*");
    System.out.println(regex); // .*[.][w][a][v]
    System.out.println("abcd.wav".matches(regex)); // true

通过将字符放入字符类来“转义”字符更容易,因为在字符类中几乎所有字符都会失去任何特殊含义。 除非您期待奇怪的文件名,否则这将起作用。

使用简单的正则表达式

这种方法的好处之一是除了*之外我们可以轻松添加令牌(请参阅底部添加令牌 )。

搜索: [^*]+|(\\*)

  • |的左侧 匹配任何不是明星的字符
  • 右侧将所有星星捕获到第1组
  • 如果组1为空:替换为\\Q + Match + E
  • 如果设置了组1:替换为.*

这是一些工作代码(参见在线演示的输出)。

输入: audio*2012*.wav

输出: \\Qaudio\\E.*\\Q2012\\E.*\\Q.wav\\E

String subject = "audio*2012*.wav";
Pattern regex = Pattern.compile("[^*]+|(\\*)");
Matcher m = regex.matcher(subject);
StringBuffer b= new StringBuffer();
while (m.find()) {
    if(m.group(1) != null) m.appendReplacement(b, ".*");
    else m.appendReplacement(b, "\\\\Q" + m.group(0) + "\\\\E");
}
m.appendTail(b);
String replaced = b.toString();
System.out.println(replaced);

添加令牌

假设我们还想转换通配符? ,用点代表单个字符。 我们只是在正则表达式中添加一个捕获组,并将其从左侧的matchall中排除:

搜索: [^*?]+|(\\*)|(\\?)

在替换函数中我们添加如下内容:

else if(m.group(2) != null) m.appendReplacement(b, "."); 

Apache Commons-IO库中有一个小实用工具方法:org.apache.commons.io.FilenameUtils #wildcardMatch(),您可以使用它而不需要复杂的正则表达式。

API文档可以在以下网址找到: https//commons.apache.org/proper/commons-io/javadocs/api-2.5/org/apache/commons/io/FilenameUtils.html#wildcardMatch(java.lang.String,% 20java.lang.String)

您还可以使用引号转义字符: \\\\Q and \\\\E - 它们之间的所有内容都被视为文字,并且不被视为要评估的正则表达式的一部分。 因此,此代码应该工作:

    String input = "*.wav";
    String regex = "\\Q" + input.replace("*", "\\E.*?\\Q") + "\\E";

    // regex = "\\Q\\E.*?\\Q.wav\\E"

请注意,您的*通配符也可能只与使用\\ w的单词字符匹配,具体取决于您希望通配符的行为方式(?)

Lucene具有提供此功能的类,并且还支持反斜杠作为转义字符。 ? 匹配单个字符, 1匹配0个或更多字符, \\转义后续字符。 支持Unicode代码点。 假设速度很快,但我没有测试过。

CharacterRunAutomaton characterRunAutomaton;
boolean matches;
characterRunAutomaton = new CharacterRunAutomaton(WildcardQuery.toAutomaton(new Term("", "Walmart")));
matches = characterRunAutomaton.run("Walmart"); // true
matches = characterRunAutomaton.run("Wal*mart"); // false
matches = characterRunAutomaton.run("Wal\\*mart"); // false
matches = characterRunAutomaton.run("Waldomart"); // false
characterRunAutomaton = new CharacterRunAutomaton(WildcardQuery.toAutomaton(new Term("", "Wal*mart")));
matches = characterRunAutomaton.run("Walmart"); // true
matches = characterRunAutomaton.run("Wal*mart"); // true
matches = characterRunAutomaton.run("Wal\\*mart"); // true
matches = characterRunAutomaton.run("Waldomart"); // true
characterRunAutomaton = new CharacterRunAutomaton(WildcardQuery.toAutomaton(new Term("", "Wal\\*mart")));
matches = characterRunAutomaton.run("Walmart"); // false
matches = characterRunAutomaton.run("Wal*mart"); // true
matches = characterRunAutomaton.run("Wal\\*mart"); // false
matches = characterRunAutomaton.run("Waldomart"); // false

适应DOS / Windows路径时的正则表达式

实现Quotation转义字符\\Q\\E可能是最好的方法。 但是,由于反斜杠通常用作DOS / Windows文件分隔符,因此路径中的“ \\E ”序列可能会影响\\Q\\E的配对。 *? 通配符令牌,这种反斜杠的情况可以这种方式解决:

搜索: [^*?\\\\]+|(\\*)|(\\?)|(\\\\)

在“使用简单正则表达式”示例的替换功能中将添加两个新行以适应新的搜索模式。 代码仍然是“Linux友好的”。 作为一种方法,它可以这样写:

public String wildcardToRegex(String wildcardStr) {
    Pattern regex=Pattern.compile("[^*?\\\\]+|(\\*)|(\\?)|(\\\\)");
    Matcher m=regex.matcher(wildcardStr);
    StringBuffer sb=new StringBuffer();
    while (m.find()) {
        if(m.group(1) != null) m.appendReplacement(sb, ".*");
        else if(m.group(2) != null) m.appendReplacement(sb, ".");     
        else if(m.group(3) != null) m.appendReplacement(sb, "\\\\\\\\");
        else m.appendReplacement(sb, "\\\\Q" + m.group(0) + "\\\\E");
    }
    m.appendTail(sb);
    return sb.toString();
}

用于演示此方法实现的代码可以这样写:

String s = "C:\\Temp\\Extra\\audio??2012*.wav";
System.out.println("Input: "+s);
System.out.println("Output: "+wildcardToRegex(s));

这将是生成的结果:

Input: C:\Temp\Extra\audio??2012*.wav
Output: \QC:\E\\\QTemp\E\\\QExtra\E\\\Qaudio\E..\Q2012\E.*\Q.wav\E
  // The main function that checks if two given strings match. The pattern string  may contain
  // wildcard characters
  default boolean matchPattern(String pattern, String str) {

    // If we reach at the end of both strings, we are done
    if (pattern.length() == 0 && str.length() == 0) return true;

    // Make sure that the characters after '*' are present in str string. This function assumes that
    // the pattern string will not contain two consecutive '*'
    if (pattern.length() > 1 && pattern.charAt(0) == '*' && str.length() == 0) return false;

    // If the pattern string contains '?', or current characters of both strings match
    if ((pattern.length() > 1 && pattern.charAt(0) == '?')
        || (pattern.length() != 0 && str.length() != 0 && pattern.charAt(0) == str.charAt(0)))
      return matchPattern(pattern.substring(1), str.substring(1));

    // If there is *, then there are two possibilities
    // a: We consider current character of str string
    // b: We ignore current character of str string.
    if (pattern.length() > 0 && pattern.charAt(0) == '*')
      return matchPattern(pattern.substring(1), str) || matchPattern(pattern, str.substring(1));
    return false;
  }

  public static void main(String[] args) {
    test("w*ks", "weeks"); // Yes
    test("we?k*", "weekend"); // Yes
    test("g*k", "gee"); // No because 'k' is not in second
    test("*pqrs", "pqrst"); // No because 't' is not in first
    test("abc*bcd", "abcdhghgbcd"); // Yes
    test("abc*c?d", "abcd"); // No because second must have 2 instances of 'c'
    test("*c*d", "abcd"); // Yes
    test("*?c*d", "abcd"); // Yes
  }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM