Java 正则表达式通配符匹配

Java 正则表达式通配符匹配,java,regex,Java,Regex,我有一个大约12万个英语单词的列表(基本上是语言中的每个单词) 我需要一个正则表达式,允许使用通配符搜索这些单词,也称为*和? 举几个例子: 如果用户搜索m?st*,它将匹配例如master或mister或mistery 如果用户搜索*ind(任何以ind结尾的单词),它将匹配wind或bind或blind或grind 现在,大多数用户(尤其是那些不熟悉正则表达式的用户)都知道,?正好替换了1个字符,而*则替换了0个、1个或更多字符。我绝对想在此基础上构建我的搜索功能 我的问题是:如何将用户

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

我需要一个正则表达式,允许使用通配符搜索这些单词,也称为
*

举几个例子:

  • 如果用户搜索
    m?st*
    ,它将匹配例如
    master
    mister
    mistery
  • 如果用户搜索
    *ind
    (任何以
    ind
    结尾的单词),它将匹配
    wind
    bind
    blind
    grind
现在,大多数用户(尤其是那些不熟悉正则表达式的用户)都知道,
正好替换了1个字符,而
*
则替换了0个、1个或更多字符。我绝对想在此基础上构建我的搜索功能

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

我在网上搜索(显然包括这个网站),我能找到的都是试图教我太多东西的教程,或者是一些类似的问题,但不足以回答我自己的问题

我所能想到的就是我必须用
替换
。所以
m?st*
变成
m.st*
。但是,我不知道用什么来代替
*

任何帮助都将不胜感激。多谢各位


附言:我对正则表达式一无所知。我知道他们有多强大,但我也知道他们很难学习。所以我从来没有花时间去做…

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

因此,与您的
*
意思相同的是将这两个词放在一起:
*
。这意味着“任何字符零次或多次”


请参阅。

*
替换为
*
(regex等价于“任何字符的0或更多”)。

替换为
并将
*
替换为
*
除非您想要一些有趣的行为,否则我建议您使用
\w
而不是
/code>>

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

所以我会用
\w
替换
,用
\w*
替换
*

另外,如果希望
*
至少匹配一个字符,请将其替换为
\w+
。这意味着
ben*
将匹配
bend
bend
,但不匹配
ben
——这取决于您的需求

  • 将所有“?”字符替换为“\w”
  • 将所有“*”字符替换为“\w*”
  • “*”运算符将上一项“.”(任何字符)重复0次或更多次

    这假设所有单词都不包含“.”、“*”和“?”

    这是一个很好的参考


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

  • 在所有([{^-=$!|]})的前面加上\-,这样它们将作为字符匹配,不会让用户体验出人意料。您还可以将其包含在\Q(开始引用)和\E(结束引用)中。另见关于安全的段落
  • 将*通配符替换为\S*
  • 替换?带有\S的通配符?
  • 可选:使用^预结束模式-这将强制与开头完全匹配
  • 可选:将$附加到模式-这将强制执行与结尾的精确匹配

    \S-表示非空格字符,该字符出现零次或多次

  • 考虑在*或+之后是否有要匹配的字符。这可以通过在*或+之后添加来实现:\S*?\S*+?

    考虑安全性:用户将向您发送代码以运行(因为regex也是一种代码,并且用户字符串用作regex)。您应该避免将未scaped regex传递给应用程序的任何其他部分,并且只用于过滤通过其他方式检索的数据。因为若您这样做,用户可以通过提供不同的带有通配符字符串的正则表达式来影响代码的速度,这可能会在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
    
    我建议不要使用。*因为它可以匹配任何东西,而且通常都用空格分隔。

    这就是我使用的:

    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();
    }
    
    stringwildcardtoregex(stringwildcardstring){
    //12是任意的,您可以根据需要进行调整
    //在一个模式中需要多少特殊字符。
    StringBuilder sb=新的StringBuilder(wildcardString.length()+12);
    某人附加(“^”);
    对于(int i=0;i=0){
    某人附加(“\\”);
    sb.附加(c);
    }否则{
    sb.附加(c);
    }
    }
    某人追加(“$”);
    使某人返回字符串();
    }
    

    来自的特殊字符列表。

    查看此库:

    它通过正则表达式引号包装所有非通配符特定部分,因此不需要特殊字符处理: 此通配符:

    "mywil?card*"
    
    将转换为此正则表达式字符串:

    "\Qmywil\E.\Qcard\E.*"
    
    如果要将通配符转换为正则表达式字符串,请使用:

    JWildcard.wildcardToRegex("mywil?card*");
    
    如果要检查匹配的目录
    JWildcard.wildcardToRegex("mywil?card*");
    
    JWildcard.matches("mywild*", "mywildcard");
    
    JWildcard.wildcardToRegex(wildcard, rules, strict);
    
    compile 'com.yevdo:jwildcard:1.4'
    
    <dependency>
      <groupId>com.yevdo</groupId>
      <artifactId>jwildcard</artifactId>
      <version>1.4</version>
    </dependency>