我正在尝试从线程转储文件中获取线程名称。 线程名称通常包含在"双引号"在每个线程转储的第一行。 它可能看起来很简单如下:
"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
如何在这里使用。我的同事试图解释我,但我仍然无法完全理解它。谁能解释一下?
答案 0 :(得分:9)
"(.*)"
正则表达式涉及大量的回溯,因为它找到第一个"
,然后抓取整个字符串并回溯查找最接近字符串结尾的"
。由于您的引用子字符串更接近开头,因此这个 lazy 量词"(.*?)"
使得正则表达式引擎查找最接近的*?
,因此跟"
的回溯更多。在找到第一个"
之后。
否定的字符类解决方案"([^"]*)"
是3中最好的,因为它不必抓取所有,只需要"
以外的所有字符。但是,要停止任何回溯并使表达式最终有效,您可以使用所有格量词。
如果您需要匹配"
+ no quotes here
+ "
等字符串,请使用
"([^"]*+)"
甚至您不需要在这种情况下匹配尾随引用:
"([^"]*+)
请参阅regex demo
事实上,我无法猜测我们如何口头描述这个正则表达式。
后者"([^"]*+)
正则表达式可以描述为
"
- 找到字符串左侧的第一个"
符号([^"]*+)
- 尽可能多地匹配并捕获除"
以外的第1组零个或多个符号,并且一旦引擎找到双引号,就立即返回匹配,而不回溯。 有关quantifiers from Rexegg.com的更多信息:
A*
零或更多As尽可能多(贪婪),如果引擎需要回溯(温顺),则放弃角色A*?
零或更多As,尽可能少地允许整体模式匹配(懒惰)
A*+
零或更多As尽可能多(贪婪),如果引擎试图回溯(占有),则不放弃字符
如您所见,?
不是一个单独的量词,它是另一个量词的一部分。
我建议阅读更多关于为什么Lazy Quantifiers are Expensive和Negated Class Solution非常安全快速地处理您的输入字符串(您只需匹配引号后跟非引号然后最终引用) )。
.*?
,.*
和[^"]*+
量词之间的差异"(.*)"
解决方案的工作方式如下:从左到右检查每个符号,查找"
,一旦找到,就抓住整个字符串直到结尾并检查每个符号是否等于{ {1}}。因此,在输入字符串中,它会回溯160次。"(.*?)"
解决方案的工作方式如下:引擎找到第一个"
,然后在模式中前进,并针对{{1}尝试下一个标记("
)在"
中。这失败了,因此引擎回溯并允许T
将其匹配扩展一个项目,以使其与THREAD1
匹配。再一次,发动机在模式中前进。它现在针对.*?
中的T
尝试"
。这会失败,因此引擎会回溯并允许H
展开并匹配THREAD1
。 The process then repeats itself—the engine advances, fails, backtracks, allows the lazy .*?
to expand its match by one item, advances, fails and so on. For each character matched by the .*?
, the engine has to backtrack. From a computing standpoint, this process of matching one item, advancing, failing, backtracking, expanding is "expensive". 由于下一个.*?
不远,所以回溯步数远远少于贪婪匹配。
"([^"]*+)"
的占有量词解决方案的工作原理如下:引擎找到最左边的H
,然后抓取所有不是"
的字符,直到第一个"
1}}。否定的字符类"
贪婪地匹配零个或多个不是双引号的字符。因此,我们保证点星永远不会跳过第一次遇到的"
。这是一些更直接有效的匹配方式之间的匹配方式。请注意,在此解决方案中,我们完全信任量化[^"]*+
的{{1}}。即使它很贪婪,"
也不会与*
互相排斥,因为它与[^"]
互斥。这是正则表达式样式指南contrast principle中的[see source]。 请注意,占有量词不会让正则表达式引擎回溯到子表达式,一旦匹配,[^"]
之间的符号就会变成一个不能被“重新排序”的硬块,因为会遇到一些“不便”。正则表达式引擎,它将无法将任何字符从这个文本块中移出。
对于当前表达式,它并没有产生很大的不同。