来源于: http://blog.ostermiller.org/find-comment
原文为英文,以下翻译结果为有道翻译及个人修改一些语法错误和语句不通顺。
使用正则表达式在源代码中查找注释
许多文本编辑器都具有高级查找(和替换)特性。当我在编程时,我喜欢使用带有正则表达式搜索和替换的编辑器。这个特性允许基于复杂的模式而不是仅仅基于文字来查找文本。有时,我想检查源代码中的每个注释,并编辑它们或删除它们。我发现很难编写一个能够找到C风格注释(以/*开头,以*/结尾的注释)的正则表达式,因为我的文本编辑器没有实现正则表达式的“非贪婪匹配”特性。
第一次实验
当第一次尝试这个问题时,大多数人会考虑正则表达式:
/\*.*\*/
这似乎是最自然的方法。/\*查找注释的开头(注意,文字*需要转义,因为*在正则表达式中有特殊的含义),.*查找任意字符的任意数量,而\*/查找表达式的末尾。
这种方法的第一个问题是。*不匹配新行。例如:
/* First comment
first comment—line two*/
/* Second comment */
第二次实验
这可以很容易地克服更换。使用[^](在一些正则表达式包中)或更一般地使用(.|[\r\n]):
/\*(.|[\r\n])*\*/
这会出现第二个更严重的问题——表达式匹配过多。正则表达式是贪婪的,它们匹配尽可能多的东西。如果文件里有两个注释的情况。这个正则表达式将匹配它们以及两者之间的任何东西:
start_code();
/* First comment */
more_code();
/* Second comment */
end_code();
第三次实验
要解决这个问题,正则表达式必须匹配更少的字符。我们不能使用 . ,需要限制表达式中可以包含的字符类型:
/\*([^*]|[\r\n])*\*/
但是这种简单的方法不能匹配任何带有*的注释:
/*
* Common multi-line comment style.
*/
/* Second comment */
第四次实验
那么问题来了。我们如何匹配一个文本 * 而不匹配注释结束的 * ? 解决方案是仍然匹配任何不是*的字符,但也接受一个*和它后面的任何字符,前提是它后面没有 / :
/\*([^*]|[\r\n]|(\*([^/]|[\r\n])))*\*/
这种方法效果更好,但在某些情况下也会匹配太多。它将匹配 * 的任何偶数。甚至可能匹配到结束注释的 *。
start_code();
/****
* Common multi-line comment style.
****/
more_code(); PS: 这句也会被匹配
/*
* Another common multi-line comment style.
*/
end_code();
第五次实验
如果我们接受任意数量的*,后面跟着除 * 或 /以外的任何东西,我们之前的方法就会起作用:
/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*/
现在正则表达式又不能接受足够的值。它比以前工作得更好了,但仍然有种情况不行。它不接受以多个*结尾的注释。
/****
* Common multi-line comment style.
****/
PS: ↑匹配失败 ↓匹配成功
/****
* Another common multi-line comment style.
*/
最终解决方案
现在我们只需要修改注释结尾允许任意数量的*:
/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/
接下来对正则进行扩充,让它支持对 // 单行注释的匹配:
(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*)
工具 | 用法 | 备注 |
---|---|---|
NEDIT | (/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|(//.*) | [^] 不包括新行 |
GREP | (/\*([^*]|(\*+[^*/]))*\*+/)|(//.*) grep -E "(/\*([^*]|(\*+[^*/]))*\*+/)|(//.*)" <files> | 不支持多行注释,将打印出每一行完全包含注释的内容 |
PERL | /((?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:\/\/.*))/ perl -e "$/=undef;print<>=~/((?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:\/\/.*))/g;" < <file> | 打印出所有一起运行的注释。(?:符号必须用于非捕获括号。每个/必须转义,因为它分隔了表达式。$/=undef 用于使文件不会像grep那样逐行匹配。 |
JAVA |
System.out.println(sourcecode.replaceAll("(?:/\\*(?:[^*]|(?:\\*+[^*/]))*\\*+/)|(?://.*)","")); | 打印出删除注释的字符串源代码的内容。(?:符号必须用于非捕获括号。每个\必须在Java字符串中转义。 |
后边还有一段说非贪婪匹配的,懒得弄得,想要了解的小伙伴可以去原文查看;
PS: 这个正则目前基本能匹配单行注释和多行注释,但是目前发现一种情况会错误匹配,字符串的内容如果有注释是不行的,其它问题暂时还没发现:
std::string test = "//测试字符串";
点个赞吧;