大数据的高效正则表达式,如果字符串包含一个单词

Posted

技术标签:

【中文标题】大数据的高效正则表达式,如果字符串包含一个单词【英文标题】:Efficient Regular Expression for big data, if a String contains a word 【发布时间】:2014-08-31 16:43:50 【问题描述】:

我有一个可以运行但速度极慢的代码。此代码确定字符串是否包含关键字。我的要求需要高效地处理我将在数千个文档中搜索的数百个关键字。

如何才能有效地查找关键字(不会错误地返回包含关键字的单词)?

例如:

String keyword="ac"; 
String document"..."  //few page long file

如果我使用:

if(document.contains(keyword) )
//do something

如果文档包含“帐户”之类的单词,它也会返回true;

所以我尝试使用正则表达式如下:

String pattern = "(.*)([^A-Za-z]"+ keyword +"[^A-Za-z])(.*)";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(document);
if(m.find())
   //do something

总结:

这是摘要:希望对其他人有用:

    我的正则表达式可以工作,但非常不切实际 处理大数据。 (它没有终止) @anubhava 完善了正则表达式。很容易 理解和执行。它设法终止了,这是一个很大的 事物。但还是有点慢。 (大约 240 秒) @Tomalak 解决方案的实现和理解有点复杂,但它 是最快的解决方案。所以脱帽致敬。(18 秒)

所以@Tomalak 解决方案比@anubhava 快约 15 倍。

【问题讨论】:

你每次都在看文件吗? 灾难性回溯是您要寻找的关键字。此外,"if a string contains a word" 使用indexOf() 进行检查,而不是使用正则表达式。 所有文档和关键字都在内存中。 @Tomalak 我刚刚使用了 "," , " " , ";" , "\n" , "" , "\"" , "'" , " " 作为集合中的分隔符。并将结果提高 4 秒 :D。所以这次花了 14 秒。 另见此要点:gist.github.com/Tomalak/31c1c55c1c79430be5c7 【参考方案1】:

不要认为您需要在您的正则表达式中包含.*

试试这个正则表达式:

String pattern = "\\b"+ Pattern.quote(keyword) + "\\b";

这里\\b 用于单词边界。如果关键字可以包含特殊字符,请确保它们不在单词的开头或结尾,否则单词边界将无法匹配。

如果您的关键字包含特殊的正则表达式字符,您也必须使用Pattern.quote

编辑:如果您的关键字由空格分隔,您可以使用此正则表达式。

String pattern = "(?<=\\s|^)"+ Pattern.quote(keyword) + "(?=\\s|$)";

【讨论】:

(如果关键字可以包含这些字符,请确保它们不在单词的开头或结尾,否则单词边界将无法匹配)。 @anubhava 我试过你的解决方案。它的速度要快得多。我稍后会更新你:) 感谢@Tomalak:我强烈推荐它并在代码中添加。 @anubhava 非常感谢,结果非常好。它仍然很慢,但至少我完成了:D。我想如果我在云上运行它,我现在可以将时间消耗减少到实时。 为了乐趣和利益,我将链接到这个关于 Unicode 和 \b(除其他外)在 Java 中的百科全书答案。 ***.com/questions/4304928/…【参考方案2】:

在 Java 中查找子字符串的最快方法是使用 String.indexOf()

要实现“仅整个单词”匹配,您需要添加一些逻辑来检查可能匹配前后的字符,以确保它们是非单词字符:

public class IndexOfWordSample 
    public static void main(String[] args) 
        String input = "There are longer strings than this not very long one.";
        String search = "long";
        int index = indexOfWord(input, search);

        if (index > -1) 
            System.out.println("Hit for \"" + search + "\" at position " + index + ".");
         else 
            System.out.println("No hit for \"" + search + "\".");
        
    

    public static int indexOfWord(String input, String word) 
        String nonWord = "^\\W?$", before, after;               
        int index, before_i, after_i = 0;

        while (true) 
            index = input.indexOf(word, after_i);
            if (index == -1 || word.isEmpty()) break;

            before_i = index - 1;
            after_i = index + word.length();
            before = "" + (before_i > -1 ? input.charAt(before_i) : "");            
            after = "" + (after_i < input.length() ? input.charAt(after_i) : "");

            if (before.matches(nonWord) && after.matches(nonWord)) 
                return index;
            
        
        return -1;
    

这将打印:

在第 44 位命中“多头”。

这应该比纯正则表达式方法执行得更好。

想想^\W?$ 是否已经符合您对“非单词”字符的期望。正则表达式在这里是一种折衷,如果您的输入字符串包含许多“几乎”匹配项,则可能会降低性能。

为了提高速度,放弃正则表达式并使用Character class,检查它为beforeafter 提供的许多属性(如isAlphabetic 等)的组合。

我创建了一个带有 alternative implementation that does that 的 Gist。

【讨论】:

这是个好伙伴,我几乎得到了相同的结果,但使用它更快。 (在处理大数据时几乎是完美的) 我添加了一些信息,以使这更快。

以上是关于大数据的高效正则表达式,如果字符串包含一个单词的主要内容,如果未能解决你的问题,请参考以下文章

如果所述子字符串包含另一个单词,则正则表达式匹配子字符串

用于检查字符串是不是不应包含某些单词的正则表达式,但如果这些单词前面有“to”或“for”,则这些单词是可以的

不包含多个特定单词的字符串的正则表达式

java 正则表达式匹配字符串,包含没有数字的单词,并且可以选择用逗号分隔

pyspark字符串匹配多个精确单词正则表达式的有效方法

用正则表达式regexp进行高级搜索数据