正则表达式从 C# 中删除行注释
Posted
技术标签:
【中文标题】正则表达式从 C# 中删除行注释【英文标题】:Regex to strip line comments from C# 【发布时间】:2011-04-01 06:13:45 【问题描述】:我正在开发一个例程,从一些 C# 代码中去除块 或 行 cmets。我查看了网站上的其他示例,但没有找到我正在寻找的确切答案。
我可以使用带有 RegexOptions.Singleline 的正则表达式来完整匹配块 cmets(/* 注释 */):
(/\*[\w\W]*\*/)
我可以使用带有 RegexOptions.Multiline 的正则表达式来完整匹配行 cmets(// 注释):
(//((?!\*/).)*)(?!\*/)[^\r\n]
注意:我使用的是[^\r\n]
而不是$
,因为$
在匹配中也包含\r
。
但是,这并没有完全按我想要的方式工作。
这是我要匹配的测试代码:
// 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";
块表达式匹配
/* remove block comments
else
return "FIXED";
// do not remove nested comments */
这很好,但是行表达式匹配
// remove whole line comments
// remove partial line comments
和
// do not remove nested comments
另外,如果我在行表达式中没有两次 */ 肯定前瞻,它匹配
// do not remove nested comments *
我真的不想要。
我想要的是一个表达式,它将匹配从//
开始到行尾的字符,但 not 在//
和行尾之间是否包含*/
。
另外,为了满足我的好奇心,谁能解释为什么我需要两次前瞻? (//((?!\*/).)*)[^\r\n]
和 (//(.)*)(?!\*/)[^\r\n]
都会包含 *,但 (//((?!\*/).)*)(?!\*/)[^\r\n]
和 (//((?!\*/).)*(?!\*/))[^\r\n]
不会。
【问题讨论】:
你是否也考虑过string foo = "http://***.com;"
的情况
您的 /* ... */
模式由于贪婪而过度匹配,例如考虑/* comment1 */ not-a-comment! /* comment2 */
。
您可以考虑使用 C# 解析器:***.com/questions/81406/parser-for-c
LOL...对于这个问题,使用成熟的 C# 解析器绝对是矫枉过正。
一个绝对无价的设计、理解和测试 RegEx 的工具是 expresso:ultrapico.com/Expresso.htm。
【参考方案1】:
您的两个正则表达式(用于块和行 cmets)都有错误。如果你愿意,我可以描述这些错误,但我觉得如果我写新的可能会更有效率,特别是因为我打算写一个匹配两者的一个。
问题是,每次/*
和//
以及文字字符串相互“干扰”时,总是首先开始的那个优先。这非常方便,因为这正是正则表达式的工作原理:首先找到第一个匹配项。
所以让我们定义一个正则表达式来匹配这四个标记中的每一个:
var blockComments = @"/\*(.*?)\*/";
var lineComments = @"//(.*?)\r?\n";
var strings = @"""((\\[^\n]|[^""\n])*)""";
var verbatimStrings = @"@(""[^""]*"")+";
要回答标题中的问题(strip cmets),我们需要:
用空替换块 cmets 用换行符替换 cmets 行(因为正则表达式会吃掉换行符) 将文字字符串保留在原处。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 提供的所有示例以及我能想到的各种其他案例上运行了这段代码,它就像一个魅力。如果你能提供一个失败的例子,我很乐意为你调整代码。
【讨论】:
我不需要提取 cmets,只需将它们从我的源脚本中删除即可。我试过你的代码,效果很好。理想情况下,如果该行仅包含 cmets,我想完全删除任何行。例如注释所在的位置没有空行。但是,这不是要求,只是格式偏好。谢谢。 @Welton:好吧,你可以在之后对结果运行Regex.Replace(@"^(\s*\r?\n)2,", Environment.Newline, RegexOptions.Multiline)
,但这会删除没有也有评论的空白双行.
我看到你测试过这个:csharp.pastebin.com/0aqBdFE5 但是当你有这样的东西时:string input = "1 + 2 //cmets";由于三元运算符中的Environment.Newline,它失败了它给你结果“1 + 2 \r\n”
@juFo:当我尝试你的输入时,它失败了:它实际上留下了评论。(这是意料之中的,因为正则表达式需要一个换行符。)我已经解决了这个问题:@ 987654322@
非常优雅的解决方案。根据您的解决方案,我在此处为删除 SQL cmets 做了类似的事情:***.com/a/33947706/3606250【参考方案2】:
您可以使用如下表达式标记代码:
@(?:"[^"]*")+|"(?:[^"\n\\]+|\\.)*"|'(?:[^'\n\\]+|\\.)*'|//.*|/\*(?s:.*?)\*/
它也会匹配一些无效的转义/结构(例如'foo'
),但可能会匹配所有感兴趣的有效标记(除非我忘记了什么),因此适用于有效代码。
在替换中使用它并捕获您想要保留的部分将为您提供所需的结果。即:
static string StripComments(string code)
var re = @"(@(?:""[^""]*"")+|""(?:[^""\n\\]+|\\.)*""|'(?:[^'\n\\]+|\\.)*')|//.*|/\*(?s:.*?)\*/";
return Regex.Replace(code, re, "$1");
Example app:
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
【讨论】:
我试试看。谢谢。 等等,为什么我在被问、回答和接受后 2 年才回答这个问题?给出几乎相同的答案?它是怎么出现在我的名单上的?一定是有什么bug什么的,我不做这样的事情。 (笑) 我发现这对我来说是完美的答案(C#),但是正则表达式不适用于 javascript。【参考方案3】:在你实现它之前,你需要先为它创建测试用例
-
简单的 cmets /* */, //, ///
多行 cmets /* This\nis\na\ntest*/
代码行后的注释 var a = "apple"; // 测试或 /* 测试 */
cmets 中的注释 /* This // is a test /, or // This / is a test */
看起来像 cmets 并出现在引号中的简单非 cmets var comment= "/* This is a test*/", or var url = "http://***.com";
复杂的非 cmets 看起来像 cmets:var abc = @" this /* \n 是引号中的注释\n*/",在 " 和 /* 或 */ 和 " 之间有或没有空格
可能还有更多案例。
一旦你拥有了所有这些,你就可以为它们中的每一个创建一个解析规则,或者对其中的一些进行分组。
仅使用正则表达式解决这个问题可能会非常困难且容易出错,难以测试,并且您和其他程序员也难以维护。
【讨论】:
Holystream,我确实有你提到的一些测试用例,但不是全部。我上面的示例涵盖了 1(部分)、2、3 和 4。5 和 6 是我没有考虑过的好点。 Holystream,我相信你做得比现在更难。使用正则表达式匹配两种注释样式非常容易——事实上,C#(和 C++)词法分析器可能会这样做。这与 html 之类的东西形成对比,后者很难与正则表达式匹配,因为 HTML 标记可以嵌套,而且它们有太多不同的种类。 @Timwi:实际上,.NET 使用词法分析器。注释符号只是标记。 en.wikipedia.org/wiki/Lexical_analysis @Timwi:你能给我一个适用于上述情况的例子吗?我很想知道通过这些测试用例的正则表达式。 /*(.*?)*/|//.*?\r?\n 很多测试用例都失败了。 @Holystream:您在我的回答中尝试过正则表达式吗?您似乎已从中删除了两个反斜杠。如果我的正则表达式失败,请提供一个失败的具体示例,并评论我的答案而不是这个答案。谢谢!【参考方案4】:我在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%]*)\>)
当我测试它时,它似乎删除了所有 // cmets 和 /* cmets */ ,而将引号内的那些留在后面。
尚未对其进行大量测试,但似乎运行良好(尽管它是一条可怕的正则表达式)。
【讨论】:
好的.. 经过一些测试后,我注意到包含减号 (-) 和多个多行 cmets 的 cmets 存在问题(/* 评论 / 不评论 / 再次评论*/)。但如果有人想解决这个问题,我认为这是一个很好的解决方案。【参考方案5】:对于块注释(/* ... */)你可以使用这个exp:
/\*([^\*/])*\*/
它也适用于多行 cmets。
【讨论】:
请问为什么要降级这个答案?【参考方案6】:另请参阅我的 C# 代码压缩项目:CSharp-Minifier
除了从代码中删除 cmets、空格和换行符之外,目前它能够压缩局部变量名称并进行其他缩小。
【讨论】:
以上是关于正则表达式从 C# 中删除行注释的主要内容,如果未能解决你的问题,请参考以下文章