正则expression式从C#中去除行注释

我正在处理一些例程,以从一些C#代码中去除块行注释。 我已经看了网站上的其他例子,但还没有find我正在寻找的确切答案。

我可以使用这个正则expression式与RegexOptions.Singleline完全匹配块注释(/ * comment * /):

(/\*[\w\W]*\*/)

我可以使用这个正则expression式与RegexOptions.Multiline完全匹配行注释(//注释):

(//((?!\*/).)*)(?!\*/)[^\r\n]

注意:我使用[^\r\n]而不是$因为$在匹配中也包含\r

但是,这并不像我想要的那样工作。

这是我匹配的testing代码:

 // remove whole line comments bool broken = false; // remove partial line comments if (broken == true) { return "BROKEN"; } /* remove block comments else { return "FIXED"; } // do not remove nested comments */ bool working = !broken; return "NO COMMENT"; 

块expression式匹配

 /* remove block comments else { return "FIXED"; } // do not remove nested comments */ 

这是好的,但行expression式匹配

 // remove whole line comments // remove partial line comments 

 // do not remove nested comments 

另外,如果我在行expression式中没有* / positive lookahead两次,它就匹配

 // do not remove nested comments * 

真的不想要。

我想要的是一个匹配字符的expression式,以//开始,到行尾,但不包含//和行尾之间的*/

另外,为了满足我的好奇心,任何人都可以解释为什么我需要向前看两次? (//((?!\*/).)*)[^\r\n](//(.)*)(?!\*/)[^\r\n]都会包含* , (//((?!\*/).)*(?!\*/))[^\r\n]不会。

你的两个正则expression式(块和行注释)都有错误。 如果你愿意,我可以描述这些错误,但是如果我写出新的错误,我觉得这可能会更有效率,尤其是因为我打算编写一个与之相匹配的错误。

事情是,每当你有/*/*和文字串互相“干涉”时,始终是第一个优先的。 这非常方便,因为这正是正则expression式的工作原理:首先find第一个匹配项。

那么让我们来定义一个正则expression式来匹配这四个令牌中的每一个:

 var blockComments = @"/\*(.*?)\*/"; var lineComments = @"//(.*?)\r?\n"; var strings = @"""((\\[^\n]|[^""\n])*)"""; var verbatimStrings = @"@(""[^""]*"")+"; 

为了回答标题(带注释)中的问题,我们需要:

  • 将块注释replace为无
  • 用换行符replace行注释(因为正则expression式换行)
  • 将string保留在原来的位置。

Regex.Replace可以使用MatchEvaluator函数轻松完成此操作:

 string noComments = Regex.Replace(input, blockComments + "|" + lineComments + "|" + strings + "|" + verbatimStrings, me => { if (me.Value.StartsWith("/*") || me.Value.StartsWith("//")) return me.Value.StartsWith("//") ? Environment.NewLine : ""; // Keep the literal strings return me.Value; }, RegexOptions.Singleline); 

我在所有Holystream提供的例子上运行了这个代码,以及其他我可以想到的其他例子,它的function就像一个魅力。 如果你可以提供一个失败的例子,我很乐意为你调整代码。

在你实现这个之前,你需要先为它创buildtesting用例

  1. 简单的评论/ * * /,//,///
  2. 多行注释/ *这\ nis \ na \ ntest * /
  3. 代码行后的注释var a =“apple”; //testing或/ *testing* /
  4. 评论/评论* /这是一个testing/,或/ /这是/testing* /
  5. 简单的非注释看起来像评论,并出现在引号var comment =“/ *这是一个testing* /”,或var url =“ http://stackoverflow.com ”;
  6. 复杂的非注释看起来像评论:var abc = @“this / * \ n是在引用\ n * /”中的注释,在“和/ *或* /和”之间有或没有空格,

那里可能有更多的情况。

一旦你拥有了所有这些,那么你可以为它们中的每一个创build一个parsing规则,或者对它们中的一些进行分组。

只用正则expression式解决这个问题可能会非常困难和容易出错,很难testing,而且很难被你和其他程序员维护。

你可以使用如下expression式来标记代码:

 @(?:"[^"]*")+|"(?:[^"\n\\]+|\\.)*"|'(?:[^'\n\\]+|\\.)*'|//.*|/\*(?s:.*?)\*/ 

它也会匹配一些无效的转义/结构(例如'foo' ),但是可能会匹配所有有效的令牌(除非我忘记了某些东西),因此可以很好地处理有效的代码。

使用它来replace和捕捉你想保留的部分将会给你想要的结果。 即:

 static string StripComments(string code) { var re = @"(@(?:""[^""]*"")+|""(?:[^""\n\\]+|\\.)*""|'(?:[^'\n\\]+|\\.)*')|//.*|/\*(?s:.*?)\*/"; return Regex.Replace(code, re, "$1"); } 

示例应用 :

 using System; using System.Text.RegularExpressions; namespace Regex01 { class Program { static string StripComments(string code) { var re = @"(@(?:""[^""]*"")+|""(?:[^""\n\\]+|\\.)*""|'(?:[^'\n\\]+|\\.)*')|//.*|/\*(?s:.*?)\*/"; return Regex.Replace(code, re, "$1"); } static void Main(string[] args) { var input = "hello /* world */ oh \" '\\\" // ha/*i*/\" and // bai"; Console.WriteLine(input); var noComments = StripComments(input); Console.WriteLine(noComments); } } } 

输出:

 hello /* world */ oh " '\" // ha/*i*/" and // bai hello oh " '\" // ha/*i*/" and 

我在http://gskinner.com/RegExr/ (名为“.Net Comments aspx”)

 (//[\t|\s|\w|\d|\.]*[\r\n|\n])|([\s|\t]*/\*[\t|\s|\w|\W|\d|\.|\r|\n]*\*/)|(\<[!%][ \r\n\t]*(--([^\-]|[\r\n]|-[^\-])*--[ \r\n\t%]*)\>) 

当我testing它似乎删除所有/ /评论和/ *评论* /应该留下里面的引号。

还没有testing过很多,但似乎工作得很好(即使它是一个可怕的怪异的正则expression式)。

对于块注释(/ * … * /)你可以使用这个exp:

/\*([^\*/])*\*/

它也将与多行注释一起工作。

另请参阅我的C#代码缩小项目: CSharp-Minifier

除了从代码中删除注释,空格和换行之外,目前它能够压缩局部variables名称并进行其他缩小。