28

我有一个大约 12 万个英语单词的列表(基本上是语言中的每个单词)。

我需要一个正则表达式,它允许使用通配符(aka*?.

几个例子:

  • 如果用户搜索m?st*,它将匹配例如masterormistermistery
  • 如果用户搜索*ind(任何以 结尾的单词ind),它将匹配windorbindblindor grind

现在,大多数用户(尤其是那些不熟悉正则表达式的用户)都知道这?是替换正好 1 个字符,而*替换是替换 0、1 个或更多字符。我绝对想在此基础上构建我的搜索功能。

我的问题是:如何将用户类型(m?st*例如)转换为正则表达式?

我搜索了网络(显然包括这个网站),我能找到的只是那些试图教给我太多东西的教程或有些相似的问题,但不足以为我自己的问题提供答案。

我能想到的只是我必须?.. 就这样m?st*变成了m.st*。但是,我不知道用什么代替*

任何帮助将不胜感激。谢谢你。

PS:我对正则表达式完全陌生。我知道它们有多么强大,但我也知道它们可能很难学习。所以我只是从来没有花时间去做...

4

9 回答 9

23

除非您想要一些有趣的行为,否则我建议您使用\w而不是.

.匹配空格和其他非单词符号,您可能不希望它这样做。

所以我会替换?\w替换*\w*

此外,如果您想*匹配至少一个字符,请将其替换为\w+。这将意味着ben*匹配bendbending不匹配ben- 这取决于您,这取决于您的要求。

于 2012-05-09T16:58:10.827 回答
9

看看这个库: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

渐变方式:

compile 'com.yevdo:jwildcard:1.4'

Maven方式:

<dependency>
  <groupId>com.yevdo</groupId>
  <artifactId>jwildcard</artifactId>
  <version>1.4</version>
</dependency>
于 2018-03-27T04:53:03.103 回答
8

替换?.和。*_.*

于 2012-05-09T16:54:17.163 回答
6

这是一种将通配符转换为正则表达式的方法:

  1. 在所有特殊字符 ([{\^-=$!|]}).+前面加上 \ -,这样它们就可以作为字符匹配,不会让用户体验出乎意料。您也可以将它包含在\Q(开始引号)和\E(结束它)中。另请参阅有关安全性的段落。
  2. 将 * 通配符替换为\S*
  3. 代替 ?带\S的通配符?
  4. 可选:在模式前面加上^ - 这将强制与开头完全匹配。
  5. 可选:将$附加到模式 - 这将强制与结尾完全匹配。

    \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

我建议不要使用 .* ,因为它可以匹配任何东西,并且通常用空格分隔。

于 2014-12-12T23:22:08.933 回答
2
  1. 全部替换 '?' 带有 '\w' 的字符
  2. 将所有 '*' 字符替换为 '\w*'

'*' 运算符重复上一项 '.' (任何字符)0 次或多次。

这假定没有任何单词包含“.”、“*”和“?”。

这是一个很好的参考

http://www.regular-expressions.info/reference.html

于 2012-05-09T17:03:24.593 回答
2

.正如您所发现的,是一个匹配任何一个字符的表达式。在您搜索的几个小时中,您无疑也偶然发现了*,这是一个重复运算符,当在一个表达式与前面的表达式连续匹配零次或多次后使用时。

所以相当于你的意思*是将这两者放在一起:.*. 这意味着“任何字符零次或多次”。

请参阅有关重复运算符的 Regex 教程

于 2012-05-09T16:53:25.083 回答
1

替换*.*(相当于“0 个或多个任意字符”的正则表达式)。

于 2012-05-09T16:53:30.463 回答
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);
}
于 2014-03-22T07:36:00.083 回答
0

这就是我使用的:

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的特殊字符列表。

于 2016-06-05T00:06:16.220 回答