改进/修复C样式块注释的正则表达式

时间:2009-01-20 19:53:48

标签: c# c regex parsing comments

我正在编写(在C#中)一个简单的解析器来处理看起来很像经典C的脚本语言。

在我拥有的一个脚本文件中,我用来识别/ *阻止注释* /的正则表达式将进入某种无限循环,占用100%的CPU。

我正在使用的正则表达式是:

/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/

有关为什么会被锁定的任何建议?

或者,我可以使用的另一个正则表达式是什么?

更多信息:

  • 使用面向.NET 3.5的C#3.0工作;
  • 我正在使用Regex.Match(string,int)方法在字符串的特定索引处开始匹配;
  • 我让程序运行了一个多小时,但比赛没有完成;
  • 传递给Regex构造函数的选项是RegexOptions.MultilineRegexOptions.IgnorePatternWhitespace;
  • 正则表达式适用于我的453个测试文件中的452个。

5 个答案:

答案 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]*?\*\/