正则表达式通配符匹配

时间:2012-05-09 16:51:48

标签: java regex

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

我需要一个正则表达式,允许使用通配符字符a.k.a。*?搜索这些单词。

一些例子:

  • 如果用户搜索m?st*,则会匹配例如mastermistermistery
  • 如果用户搜索*ind(以ind结尾的任何字词),则会匹配windbindblind或{{1} }。

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

我的问题是:如何将用户输入的内容(例如*)转换为正则表达式?

我搜索了网页(显然包括这个网站),我找到的所有内容都是试图教我太多的教程或者有些相似的问题,但还不足以为我自己的问题提供答案。

我能想到的是,我必须用m?st*替换?。因此.变为m?st*。但是,我不知道用{。

替换m.st*的内容

非常感谢任何帮助。谢谢。

PS:我对正则表达式完全不熟悉。我知道它们有多么强大,但我也知道它们很难学。所以我从来没有花时间去做它......

9 个答案:

答案 0 :(得分:15)

除非你想要一些有趣的行为,否则我建议你使用\w代替.

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

所以我会将?替换为\w并将*替换为\w*

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

答案 1 :(得分:7)

看看这个图书馆: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方式:

<dependency>
  <groupId>com.yevdo</groupId>
  <artifactId>jwildcard</artifactId>
  <version>1.2</version>
  <type>pom</type>
</dependency>

答案 2 :(得分:6)

?替换为.,将*替换为.*

答案 3 :(得分:6)

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

  1. 将所有special characters ([{\ ^ - = $!|]})。+ 添加到\前面,以便将它们匹配为字符并且不会提供用户体验意外。你也可以把它包含在 \ Q (开始引用)和 \ E (结束它)中。另见关于安全性的段落。
  2. 将*通配符替换为 \ S *
  3. 替换?带 \ S?
  4. 的通配符
  5. 可选:使用 ^ 预先添加模式 - 这将强制与开头完全匹配。
  6. 可选:将 $ 附加到模式 - 这将强制与结尾完全匹配。

    \ S - 代表非空格字符,会发生零次或多次。

  7. 如果您要在*或 + 之后匹配字符,请考虑using reluctant (non-greedy) quantifiers。这可以通过在*或 + 之后添加来完成: \ 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
    

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

答案 4 :(得分:2)

  1. 全部替换'?' '\ w'
  2. 的字符
  3. 将所有'*'字符替换为'\ w *'
  4. '*'运算符重复前一项'。' (任何字符)0次或更多次。

    这假设没有一个词包含'。','*'和'?'。

    这是一个很好的参考

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

答案 5 :(得分:1)

正如您所发现的那样,

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

所以等同于.*的含义就是将这两者放在一起:{{1}}。这意味着“任何字符零次或多次”。

请参阅Regex Tutorial on repetition operators

答案 6 :(得分:1)

*替换为.*(正则表达式相当于“0或更多任何字符”)。

答案 7 :(得分: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);
}

答案 8 :(得分: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的特殊字符列表。