正则表达式通配符匹配
我有一个大约12万个英语单词的列表(基本上是该语言中的每个单词)。
我需要一个正则表达式,允许使用通配符字符搜索这些单词,也就是*
和?
。
几个例子:
- 如果用户搜索
m?st*
,它将匹配例如master
或mister
或mistery
。 - 如果用户搜索
*ind
(任何以ind
结尾的单词),它将匹配wind
或bind
或blind
或grind
。
现在,大多数用户(特别是那些不熟悉正则表达式的用户)知道?
是1个字符的替代,而*
是0,1个或更多字符的替代。 我绝对想基于此构建我的搜索function。
我的问题是:如何将用户输入的内容(例如m?st*
)转换为正则表达式?
我在网上搜索(显然包括这个网站),我找到的所有内容都是试图教我太多的教程或者有些相似的问题,但还不足以为我自己的问题提供答案。
我能弄清楚的是我必须更换?
用.
。 所以m?st*
变成m.st*
。 但是,我不知道要替换什么*
。
任何帮助将不胜感激。 谢谢。
PS:我对正则表达式完全陌生。 我知道它们有多么强大,但我也知道它们很难学。 所以我从来没有花时间去做它……
除非你想要一些有趣的行为,否则我建议你使用\w
而不是.
.
匹配您可能不希望它执行的空格和其他非单词符号。
所以我会替换?
用\w
替换*
用\w*
此外,如果您希望*
匹配至少一个字符,请将其替换为\w+
。 这意味着ben*
会匹配bend
和bending
而不是ben
– 这取决于你,只取决于你的要求。
看看这个库: https : //github.com/alenon/JWildcard
它通过正则表达式引号包装所有非通配符特定部分,因此不需要特殊的字符处理:此通配符:
"mywil?card*"
将转换为此正则表达式字符串:
"\Qmywil\E.\Qcard\E.*"
如果您希望将通配符转换为正则表达式字符串,请使用:
JWildcard.wildcardToRegex("mywil?card*");
如果您想直接检查匹配,可以使用:
JWildcard.matches("mywild*", "mywildcard");
默认通配符规则是“?” – >“。”,“ ” – >“。 ”,但如果您愿意,可以通过简单地定义新规则来更改默认行为。
JWildcard.wildcardToRegex(wildcard, rules, strict);
您可以使用来自Bintray JCenter的maven或gradle直接下载或下载: https ://bintray.com/yevdo/jwildcard/jwildcard
Gradle方式:
compile 'com.yevdo:jwildcard:1.2'
Maven方式:
com.yevdo jwildcard 1.2 pom
替换?
用.
和*
与.*
。
这是一种将通配符转换为正则表达式的方法:
- 将所有特殊字符 ([{\ ^ – = $!|]})。+与\前置 – 以便它们作为字符匹配,并且不会使用户遇到意外情况。 你也可以将它包含在\ Q (开始引用)和\ E (结束它)中。 另见关于安全性的段落。
- 用* S *替换*通配符
- 替换? 带\ S的通配符?
- 可选:使用^预先添加模式 – 这将强制与开头完全匹配。
-
可选:将$附加到模式 – 这将强制与结束完全匹配。
\ S – 表示非空格字符,发生零次或多次。
如果在*或+之后有匹配的字符,请考虑使用不情愿(非贪婪)量词 。 这可以通过添加来完成? 在*或+之后这样: \ S *? 和\ S * +?
考虑安全性 :用户将发送代码运行(因为正则表达式也是一种代码,用户字符串用作正则表达式)。 您应该避免将未转义的正则表达式传递给应用程序的任何其他部分,并且仅用于过滤通过其他方式检索的数据。 因为如果您这样做,用户可以通过提供带有通配符字符串的不同正则表达式来影响代码的速度 – 这可以用于DoS攻击。
示例显示类似模式的执行速度:
seq 1 50000000 > ~/1 du -sh ~/1 563M time grep -P '.*' ~/1 &>/dev/null 6.65s time grep -P '.*.*.*.*.*.*.*.*' ~/1 &>/dev/null 12.55s time grep -P '.*..*..*..*..*.*' ~/1 &>/dev/null 31.14s time grep -P '\S*.\S*.\S*.\S*.\S*\S*' ~/1 &>/dev/null 31.27s
我建议不要使用。*因为它可以匹配任何东西,通常用空格分隔。
- 全部替换 ‘?’ ‘\ w’的字符
- 用’\ w *’替换所有’*’字符
‘*’运算符重复前一项’。’ (任何字符)0次或更多次。
这假设没有一个单词包含’。’,’*’和’?’。
这是一个很好的参考
将*
替换为.*
(正则表达式相当于“0或更多任何字符”)。
.
正如您所发现的那样,是一个匹配任何一个字符的表达式。 在您的搜索时间内,您无疑也会偶然发现*
,这是一个重复运算符,在表达式连续匹配前面的表达式后连续使用零次或多次 。
所以相当于*
的含义就是将这两者放在一起: .*
。 这意味着“任何字符零次或多次”。
请参阅有关重复运算符的正则表达式教程 。
function matchWild(wild,name) { if (wild == '*') return true; wild = wild.replace(/\./g,'\\.'); wild = wild.replace(/\?/g,'.'); wild = wild.replace(/\\/g,'\\\\'); wild = wild.replace(/\//g,'\\/'); wild = wild.replace(/\*/g,'(.+?)'); var re = new RegExp(wild,'i'); return re.test(name); }
这是我用的:
String wildcardToRegex(String wildcardString) { // The 12 is arbitrary, you may adjust it to fit your needs depending // on how many special characters you expect in a single pattern. StringBuilder sb = new StringBuilder(wildcardString.length() + 12); sb.append('^'); for (int i = 0; i < wildcardString.length(); ++i) { char c = wildcardString.charAt(i); if (c == '*') { sb.append(".*"); } else if (c == '?') { sb.append('.'); } else if ("\\.[]{}()+-^$|".indexOf(c) >= 0) { sb.append('\\'); sb.append(c); } else { sb.append(c); } } sb.append('$'); return sb.toString(); }
来自https://stackoverflow.com/a/26228852/1808989的特殊字符列表。