正则表达式从C#剥离行注释

时间:2010-08-19 17:10:06

标签: c# .net regex

我正在编写一个例程来从某些C#代码中去除行注释。我查看了网站上的其他示例,但没有找到我正在寻找的完全答案。

我可以使用RegexOptions.Singleline的正则表达式完整地匹配块注释(/ * comment * /):

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

我可以使用RegexOptions.Multiline的正则表达式完整地匹配行注释(//注释):

(//((?!\*/).)*)(?!\*/)[^\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

另外,如果我在行表达式中没有两次* / positive前瞻,那么它匹配

// do not remove nested comments *

真的不想要。

我想要的是一个表达式,它将字符(从//开始)匹配到行尾,但*/之间包含// }和行尾。

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

6 个答案:

答案 0 :(得分:80)

两个正则表达式(用于块和行注释)都有错误。如果你想我可以描述错误,但我觉得如果我写新的错误可能会更有成效,特别是因为我打算写一个与之匹配的单一错误。

问题是,每当你有/*//以及文字字符串相互“干扰”时,它总是首先以优先权开始。这非常方便,因为这正是正则表达式的工作方式:首先找到第一个匹配。

所以让我们定义一个匹配这四个标记中每一个的正则表达式:

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

要回答标题中的问题(条带评论),我们需要:

  • 用空格
  • 替换块注释
  • 用换行符替换行注释(因为正则表达式使用换行符)
  • 将文字字符串保留在原来的位置。

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提供的所有示例和我能想到的各种其他案例上运行此代码,它就像一个魅力。如果您能提供失败的示例,我很乐意为您调整代码。

答案 1 :(得分:7)

在实现之前,您需要先为其创建测试用例

  1. 简单评论/ * * /,//,///
  2. 多行注释/ *此\ n \ n \ n \ n \ n \ n * * /
  3. 代码行后的注释var a =“apple”; // test或/ * test * /
  4. 评论中的评论/ *这是一个测试 /,或者//这个/ 是一个测试* /
  5. 看似评论的简单非评论,并出现在引号中var comment =“/ *这是一个测试* /”,或者var url =“http://stackoverflow.com”;
  6. 复杂的非评论看起来像评论:var abc = @“this / * \ n是quote \ n * /”中的注释,“和/ *或* /和”之间有空格或没有空格
  7. 可能还有更多案例。

    如果你拥有了所有这些,那么你可以为每个人创建一个解析规则,或者对其中的一些进行分组。

    单独使用正则表达式来解决这个问题可能会非常困难且容易出错,难以测试,并且很难由您和其他程序员维护。

答案 2 :(得分:5)

您可以使用如下表达式对代码进行标记:

@(?:"[^"]*")+|"(?:[^"\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

答案 3 :(得分:1)

我在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%]*)\>)

当我测试它时,它似乎删除了所有//注释和/ *注释* /,因为它应该留在引号内。

没有对它进行过多次测试,但似乎工作得很好(即使它是一个可怕的可怕的正则表达式)。

答案 4 :(得分:0)

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

除了从代码中删除注释,空格和换行符之外,目前它还能够压缩局部变量名称并进行另一次缩小。

答案 5 :(得分:-1)

对于块注释(/ * ... * /),您可以使用此exp:

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

它也适用于多行注释。