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

Bev*_*van 10 c c# regex parsing comments

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

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

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

/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/
Run Code Online (Sandbox Code Playgroud)

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

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

更多信息:

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

Ala*_*ore 16

我在你的正则表达式中遇到的一些问题:

|[\r\n]你的正则表达式中不需要序列; 否定的字符类[^*]匹配除了*包括行分隔符在内的所有内容.它只是.(点)元字符与那些不匹配.

一旦你进入评论,你必须寻找的唯一一个字符是星号; 只要你没有看到其中一个,你就可以吞噬你想要的任意数量的角色.这意味着[^*]当你可以使用它时使用是没有意义的[^*]+.事实上,你可以把它放在一个原子组中 - (?>[^*]+)因为一旦你匹配它们,你就没有任何理由放弃任何那些非星号.

过滤掉无关的垃圾,你最外面的parens里面的最后一个选择是\*+[^*/],这意味着"一个或多个星号,后跟一个不是星号或斜线的字符".这将始终与注释末尾的星号相匹配,并且它将始终必须再次放弃,因为下一个字符是斜杠.事实上,如果有20个星号导致最后的斜线,那么正则表达式的那部分将与它们全部匹配,那么它将一个接一个地给它们全部.然后最后的部分 - \*+/将匹配它们保持.

为了获得最佳性能,我会使用这个正则表达式:

/\*(?>(?:(?>[^*]+)|\*(?!/))*)\*/
Run Code Online (Sandbox Code Playgroud)

这将非常快速地匹配格式良好的注释,但更重要的是,如果它开始匹配不是有效注释的内容,它将尽快失败.


David提供,这是一个匹配任何嵌套级别的嵌套注释的版本:

(?s)/\*(?>/\*(?<LEVEL>)|\*/(?<-LEVEL>)|(?!/\*|\*/).)+(?(LEVEL)(?!))\*/
Run Code Online (Sandbox Code Playgroud)

它使用.NET的平衡组,因此它不适用于任何其他风格.为了完整起见,这是另一个版本(来自RegexBuddy的库),它使用Perl,PCRE和Oniguruma/Onigmo支持的递归组语法:

/\*(?>[^*/]+|\*[^/]|/[^*])*(?>(?R)(?>[^*/]+|\*[^/]|/[^*])*)*\*/
Run Code Online (Sandbox Code Playgroud)


rid*_*ner 14

不不不!没有其他人阅读掌握正则表达式(第3版)!?在此,Jeffrey Friedl检查了这个确切的问题,并以此为例(第272-276页)来说明他的"展开循环"技术.他对大多数正则表达式引擎的解决方案是这样的:

/\*[^*]*\*+(?:[^*/][^*]*\*+)*/

但是,如果正则表达式引擎被优化以处理惰性量词(如Perl的那样),则最有效的表达式更简单(如上所述):

/\*.*?\*/

(当然应用等效的's'"点匹配所有"修饰符.)请注意,我不使用.NET,所以我不能说哪个版本对于该引擎更快.