大数据的高效正则表达式,如果字符串包含一个单词
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,检查它为before
和after
提供的许多属性(如isAlphabetic
等)的组合。
我创建了一个带有 alternative implementation that does that 的 Gist。
【讨论】:
这是个好伙伴,我几乎得到了相同的结果,但使用它更快。 (在处理大数据时几乎是完美的) 我添加了一些信息,以使这更快。以上是关于大数据的高效正则表达式,如果字符串包含一个单词的主要内容,如果未能解决你的问题,请参考以下文章
用于检查字符串是不是不应包含某些单词的正则表达式,但如果这些单词前面有“to”或“for”,则这些单词是可以的