Java中的通配符匹配

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

*.wav // matches .wav (*, a) // matches (, a) 

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

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

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

使用简单的正则表达式

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

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

  • |的左侧 匹配任何不是明星的字符
  • 右侧将所有星星捕获到第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, "."); 

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

  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 

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

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

API文档可以在以下url找到: 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具有提供此function的类,并且还支持反斜杠作为转义字符。 ? 匹配单个字符, 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的配对。 占*? 通配符令牌,这种反斜杠的情况可以这种方式解决:

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

在“使用简单正则表达式”示例的替换function中将添加两个新行以适应新的搜索模式。 代码仍然是“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