我可以进一步提高此正则表达式的性能

时间:2015-11-23 11:07:58

标签: java regex performance

我正在尝试从线程转储文件中获取线程名称。 线程名称通常包含在"双引号"在每个线程转储的第一行。 它可能看起来很简单如下:

"THREAD1" daemon prio=10 tid=0x00007ff6a8007000 nid=0xd4b6 runnable [0x00007ff7f8aa0000]

或者大到如下:

"[STANDBY] ExecuteThread: '43' for queue: 'weblogic.kernel.Default (self-tuning)'" daemon prio=10 tid=0x00007ff71803a000 nid=0xd3e7 in Object.wait() [0x00007ff7f8ae1000]

我写的正则表达式很简单:"(.*)"。它将双引号内的所有内容作为一组捕获。然而,它会导致严重的回溯,因此需要很多步骤,如here所示。在口头上,我们可以解释这个正则表达式为"捕获任何包含在双引号中的内容作为一个组"

所以我提出了另一个执行相同的正则表达式:"([^\"])"。在口头上,我们可以将此正则表达式描述为"捕获任意数量的非双引号字符,这些字符包含在双引号" 中。我没有发现任何快速正则表达式。它不执行任何回溯,因此它需要最少的步骤here

我把这个告诉了我的同事。他想出了另一个:"(.*?)"。我没弄明白它是如何工作的。与第一个相比,它执行的回溯要少得多,但是比第二个慢一点here。 但是

  • 我不明白为什么回溯会提前停止。
  • 我理解?是一个量词,意思是once or not at all。但是我不明白once or not at all如何在这里使用。
  • 事实上,我无法猜测我们如何口头描述这个正则表达式。

我的同事试图解释我,但我仍然无法完全理解它。谁能解释一下?

1 个答案:

答案 0 :(得分:9)

简要说明和解决方案

"(.*)"正则表达式涉及大量的回溯,因为它找到第一个",然后抓取整个字符串并回溯查找最接近字符串结尾的"。由于您的引用子字符串更接近开头,因此这个 lazy 量词"(.*?)"使得正则表达式引擎查找最接近的*?,因此跟"的回溯更多。在找到第一个"之后。

否定的字符类解决方案"([^"]*)"是3中最好的,因为它不必抓取所有,只需要"以外的所有字符。但是,要停止任何回溯并使表达式最终有效,您可以使用所有格量词

如果您需要匹配" + no quotes here + "等字符串,请使用

"([^"]*+)"

甚至您不需要在这种情况下匹配尾随引用:

"([^"]*+)

请参阅regex demo

  

事实上,我无法猜测我们如何口头描述这个正则表达式。

后者"([^"]*+)正则表达式可以描述为

  • " - 找到字符串左侧的第一个"符号
  • ([^"]*+) - 尽可能多地匹配并捕获除"以外的第1组零个或多个符号,并且一旦引擎找到双引号,就立即返回匹配,而不回溯。

量词

有关quantifiers from Rexegg.com的更多信息:

  

A*零或更多As尽可能多(贪婪),如果引擎需要回溯(温顺),则放弃角色   A*?零或更多As,尽可能少地允许整体模式匹配(懒惰)
  A*+零或更多As尽可能多(贪婪),如果引擎试图回溯(占有),则不放弃字符

如您所见,?不是一个单独的量词,它是另一个量词的一部分。

我建议阅读更多关于为什么Lazy Quantifiers are ExpensiveNegated Class Solution非常安全快速地处理您的输入字符串(您只需匹配引号后跟非引号然后最终引用) )。

.*?.*[^"]*+量词之间的差异

  • 贪婪"(.*)"解决方案的工作方式如下:从左到右检查每个符号,查找",一旦找到,就抓住整个字符串直到结尾并检查每个符号是否等于{ {1}}。因此,在输入字符串中,它会回溯160次。

enter image description here

由于下一个.*?不远,所以回溯步数远远少于贪婪匹配。

enter image description here

  • 带有否定字符类"([^"]*+)"的占有量词解决方案的工作原理如下:引擎找到最左边的H,然后抓取所有不是"的字符,直到第一个" 1}}。否定的字符类"贪婪地匹配零个或多个不是双引号的字符。因此,我们保证点星永远不会跳过第一次遇到的"。这是一些更直接有效的匹配方式之间的匹配方式。请注意,在此解决方案中,我们完全信任量化[^"]*+的{​​{1}}。即使它很贪婪,"也不会与*互相排斥,因为它与[^"]互斥。这是正则表达式样式指南contrast principle中的[see source]

请注意,占有量词不会让正则表达式引擎回溯到子表达式,一旦匹配,[^"]之间的符号就会变成一个不能被“重新排序”的硬块,因为会遇到一些“不便”。正则表达式引擎,它将无法将任何字符从这个文本块中移出。

对于当前表达式,它并没有产生很大的不同。

enter image description here