正则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用例
- 简单的评论/ * * /,//,///
- 多行注释/ *这\ nis \ na \ ntest * /
- 代码行后的注释var a =“apple”; //testing或/ *testing* /
- 评论/评论* /这是一个testing/,或/ /这是/testing* /
- 简单的非注释看起来像评论,并出现在引号var comment =“/ *这是一个testing* /”,或var url =“ http://stackoverflow.com ”;
- 复杂的非注释看起来像评论: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名称并进行其他缩小。