Node.JS Regex引擎在大输入时失败

时间:2013-05-16 06:44:01

标签: java python regex node.js v8

问题有点复杂,谷歌搜索并没有真正帮助。我将尽力只介绍它的相关方面。

我有大约以下格式的大型文档:

示例输入

ABC is a word from one line of this document. It is followed by
some random line
PQR which happens to be another word.
This is just another line
I have to fix my regular expression.
Here GHI appears in the middle.
This may be yet another line.
VWX is a line
this is the last line 

我正在尝试根据以下内容删除文本部分:

  • 来自以下任何一个:
    • ABC
    • DEF
    • GHI
  • 要么(保留这个词):
    • PQR
    • STU
    • VWX

组成“From”的单词可以出现在一行中(看GHI)。但是为了移除,需要移除整条生产线。 (需要删除包含GHI的整行,如下面的示例输出所示)

示例输出

PQR which happens to be another word.
This is just another line
I have to fix my regular expression.
VWX is a line
this is the last line 

上面的例子对我来说实际上似乎很容易,直到我针对非常大的输入文件(49KB)运行它

我尝试了什么

我目前使用的正则表达式是(不区分大小写和多行修饰符):

^.*\b(abc|def|ghi)\b(.|\s)*?\b(pqr|stu|vwx)\b

问题

上面的正则表达式可以很好地处理小文本文件。但是在大文件上失败/崩溃引擎。我已经尝试过以下方面:

  • V8(Node.js):挂起
  • 犀牛:挂起
  • Python:挂起
  • Java:StackoverflowError(此问题末尾发布的堆栈跟踪)
  • IonMonkey(Firefox):工作!

实际输入:

  • 我的原始输入:http://ideone.com/W4sZmB
  • 我的正则表达式(为清晰起见,分为多行):

    ^.*\\b(patient demographics|electronically signed|md|rn|mspt|crnp|rt)\\b
     (.|\\s)*?
     \\b(history of present illness|hpi|chief complaint|cc|reason for consult|patientis|inpatient is|inpatientpatient|pt is|pts are|start end frequency user)\\b
    

问题:

  • 我的正则表达是否正确?是否可以进一步优化以避免此问题?
  • 如果它是正确的,为什么其他引擎无限挂起?堆栈跟踪的一部分如下:

堆栈追踪:

Exception in thread "main" java.lang.StackOverflowError
    at java.util.regex.Pattern$GroupTail.match(Pattern.java:4218)
    at java.util.regex.Pattern$BranchConn.match(Pattern.java:4078)
    at java.util.regex.Pattern$CharProperty.match(Pattern.java:3345)
    at java.util.regex.Pattern$Branch.match(Pattern.java:4114)
    at java.util.regex.Pattern$GroupHead.match(Pattern.java:4168)
    at java.util.regex.Pattern$LazyLoop.match(Pattern.java:4357)
    at java.util.regex.Pattern$GroupTail.match(Pattern.java:4227)
    at java.util.regex.Pattern$BranchConn.match(Pattern.java:4078)

PS:我在这个问题上添加了几个标签,因为我已经在这些环境中尝试了这个问题并且实验失败了。

3 个答案:

答案 0 :(得分:3)

问题是(。| \ s)*因为任何空格字符都会匹配,它将允许它同时关闭两个选项。这使它变得指数级增大。

你可以在ruby中看到这个正则表达式的问题

str = "b" + "a" * 200 + "cbab"

/b(a|a)*b/.match str

这需要永远,而基本相同的

/ba*b/.match str

快速匹配。

您可以仅使用.*.与换行符(.|\n)*不匹配来解决此问题

答案 1 :(得分:0)

我很想尝试简化re。目前,说实话并非非常复杂但是如何:

\b(abc|def|ghi)\b.*\b(pqr|stu|vwx)\b

这是不是仍然可以做你想要的但是线条锚的开头和中间不必要的可选元素?可能没有任何区别,但可能值得尝试。

答案 2 :(得分:0)

我认为你的问题可能在于,随着文件变得越来越长,你可以匹配from和to的对,大约是nxm / 2.这意味着你会得到越来越多的指数越来越多的结果源文件。如果文件以ABC开头并以VWX结尾,则其中一个匹配将是整个文件。

为了给正则表达式引擎提供较少的匹配,我的第一种方法是分别仅对(abc|def|ghi)(pqr|stu|vwx)进行正则表达式。返回结果后,您可以查看匹配中的每个结果并尝试找到要阻止的第一个匹配项。一些用于实现此目的的伪代码将是

from = regex.match(file, '(abc|def|ghi)')
to = regex.match(file, '(pqr|stu|vwx)')
for each match in from:
  for index in to:
    if index > match:
      add index, match to results
      break
for each result:
  parse backwards to the beginning of the line
  edit the file to remove the matching text

虽然这为自己创造了更多的工作,但这意味着正则表达式解析器不必将整个n kB文件同时保存在内存中,并且可以更有效地解析小块。