我正在编写(在C#中)一个简单的解析器来处理看起来很像经典C的脚本语言。
在我拥有的一个脚本文件中,我用来识别/ *阻止注释* /的正则表达式将进入某种无限循环,占用100%的CPU。
我正在使用的正则表达式是:
/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/
有关为什么会被锁定的任何建议?
或者,我可以使用的另一个正则表达式是什么?
更多信息:
RegexOptions.Multiline
和RegexOptions.IgnorePatternWhitespace
; 答案 0 :(得分:16)
我在你的正则表达式中看到的一些问题:
正则表达式中不需要|[\r\n]
个序列;像[^*]
这样的否定字符类匹配除*
之外的所有内容,包括行分隔符。它只是.
(点)元字符与那些不匹配。
进入评论后,您需要查找的唯一字符是星号;只要你没有看到其中一个,你就可以吞噬你想要的任意数量的角色。这意味着当您可以使用[^*]
时,使用[^*]+
毫无意义。事实上,你可以把它放在原子组中 - (?>[^*]+)
- 因为一旦你匹配它们,你就没有任何理由放弃任何那些非星号。
过滤掉无关的垃圾,最外层的最后一个替代品是\*+[^*/]
,这意味着“一个或多个星号,后跟一个不是星号或斜线的字符”。这将始终与注释末尾的星号相匹配,并且它将始终必须再次放弃,因为下一个字符是斜杠。事实上,如果有20个星号导致最后的斜线,那么正则表达式的那部分将与它们全部匹配,那么它将一个接一个地给它们全部。然后最后一部分 - \*+/
- 将匹配它们以保持。
为了获得最佳性能,我会使用这个正则表达式:
/\*(?>(?:(?>[^*]+)|\*(?!/))*)\*/
这会非常快速地匹配格式良好的评论,但更重要的是,如果它开始匹配不是有效评论的内容,它将尽快失败。
由David提供,这是一个与任何嵌套级别的嵌套注释匹配的版本:
(?s)/\*(?>/\*(?<LEVEL>)|\*/(?<-LEVEL>)|(?!/\*|\*/).)+(?(LEVEL)(?!))\*/
它使用.NET的平衡组,因此它不适用于任何其他风格。为了完整起见,这是另一个版本(来自RegexBuddy的库),它使用Perl,PCRE和Oniguruma / Onigmo支持的递归组语法:
/\*(?>[^*/]+|\*[^/]|/[^*])*(?>(?R)(?>[^*/]+|\*[^/]|/[^*])*)*\*/
答案 1 :(得分:14)
不不不!没有其他人阅读掌握正则表达式(第3版)!?在此,Jeffrey Friedl检查了这个确切的问题,并以此为例(第272-276页)来说明他的“展开循环”技术。他对大多数正则表达式引擎的解决方案是这样的:
/\*[^*]*\*+(?:[^*/][^*]*\*+)*/
但是,如果正则表达式引擎被优化以处理惰性量词(如Perl的那样),那么最有效的表达式就更简单了(如上所述):
/\*.*?\*/
(当然使用等效的's'“点匹配所有”修饰符。) 请注意,我不使用.NET,所以我不能说哪个版本的引擎速度更快。
答案 2 :(得分:2)
您可能想要尝试单行而不是多行,然后您不必担心\ r \ n。启用该功能后,以下内容为我提供了一个简单的测试,其中包含跨越多行的注释:
/\*.*?\*/
答案 3 :(得分:1)
我认为你的表达过于复杂。应用于大型弦,许多替代品意味着大量的回溯。我想这是你看到的性能影响的来源。
如果基本假设是匹配从"/*"
到遇到第一个"*/"
的所有内容,那么一种方法就是这样(像往常一样,正则表达式不适用于嵌套结构,所以嵌套块注释不起作用):
/\*(.(?!\*/))*.?\*/ // run this in single line (dotall) mode
基本上这表示:"/*"
,后跟"*/"
后面跟着的任何内容,后跟"*/"
。
或者,您可以使用更简单的方法:
/\*.*?\*/ // run this in single line (dotall) mode
像这样的非贪婪匹配有可能在边缘情况下出错 - 目前我无法想到这个表达式可能失败的地方,但我不完全确定。
答案 4 :(得分:1)
我现在正在使用这个
\/\*[\s\S]*?\*\/