如何使用Perl从文件中删除多行C注释?

7
有人可以帮我提供一个正则表达式来删除文件中的多行注释和单行注释吗?
例如:
                  " WHOLE "/*...*/" HAS TO BE STRIPED OFF....."

1.   /* comment */
2.   /* comment1 */  code   /* comment2 */ #both /*comment1*/ and /*comment2*/ 
                                             #has to striped off and rest should 
                                                 #remain.
3.   /*.........
       .........
       .........
       ......... */

我真的很感激如果你能帮忙做这件事....提前谢谢。

1
作为一个经验法则,我发现当你试图以编程方式操作像C、XML、SQL等语言时,你应该真正考虑使用解析器而不是正则表达式。我强烈建议学习解析器生成器,如yacc、javacc等。对于我作为软件开发人员来说,这带来了巨大的回报。 - zimbu668
@zimbu668 这种情况使用解析器非常过头了。这里没有嵌套或复杂的结构,只有简单的注释。 - Shipof123
6个回答

17

来自perlfaq6的"How do I use a regular expression to strip C style comments from a file?":


虽然这个问题实际上是可以解决的,但比你想象的要难得多。例如,下面这个一行代码:

perl -0777 -pe 's{/\*.*?\*/}{}gs' foo.c

这种方法在很多情况下都有效,但并非所有情况都适用。你知道,对于某些类型的C程序来说,这种方法过于简单了,特别是那些在引号字符串中似乎有注释的程序。为此,你需要像Jeffrey Friedl创建并后来由Fred Curtis修改的类似这样的东西。

$/ = undef;
$_ = <>;
s#/\*[^*]*\*+([^/*][^*]*\*+)*/|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^/"'\\]*)#defined $2 ? $2 : ""#gse;
print;

当然,使用/x修饰符可以更清晰地编写代码,添加空格和注释。以下是由Fred Curtis提供的扩展版本。

s{
   /\*         ##  Start of /* ... */ comment
   [^*]*\*+    ##  Non-* followed by 1-or-more *'s
   (
     [^/*][^*]*\*+
   )*          ##  0-or-more things which don't start with /
               ##    but do end with '*'
   /           ##  End of /* ... */ comment

 |         ##     OR  various things which aren't comments:

   (
     "           ##  Start of " ... " string
     (
       \\.           ##  Escaped char
     |               ##    OR
       [^"\\]        ##  Non "\
     )*
     "           ##  End of " ... " string

   |         ##     OR

     '           ##  Start of ' ... ' string
     (
       \\.           ##  Escaped char
     |               ##    OR
       [^'\\]        ##  Non '\
     )*
     '           ##  End of ' ... ' string

   |         ##     OR

     .           ##  Anything other char
     [^/"'\\]*   ##  Chars which doesn't start a comment, string or escape
   )
 }{defined $2 ? $2 : ""}gxse;

稍微修改一下,也可以移除C++注释,可能跨越多行并使用续行符:
 s#/\*[^*]*\*+([^/*][^*]*\*+)*/|//([^\\]|[^\n][\n]?)*?\n|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^/"'\\]*)#defined $3 ? $3 : ""#gse;

Brian,这种功能几乎可以添加到Perl中,因为它似乎被反复询问。至少我这么认为。 - Paul Nathan
这就是为什么我们需要像yacc、flex、bison、ANTLR等工具。这是需要完整的解析器,而不是正则表达式的东西。 - Adam Rosenfield
2
@Paul:该功能已经在Perl中了。Perl是一种通用语言。我们不想为每个任务添加内置功能。这是模块的工作。 - brian d foy

11

在Perl中,通常可以使用CPAN:Regexp::Common::Comment应该能够帮助你。我发现唯一使用所描述的注释的语言是Nickle,但也许PHP注释也可以(//也可以开始单行注释)。

请注意,在任何情况下,使用正则表达式来剥离注释都是危险的,完整的解析器对于这门语言来说要少得多风险。例如,正则表达式解析器可能会被像print "/*"; 这样的内容困扰。


6

这是一个常见问题解答:

perldoc -q comment

perlfaq6中找到:

如何使用正则表达式从文件中去除C语言风格的注释?

虽然这是可以做到的,但比你想象的要难得多。例如,这个一行代码……


您可以链接到 http://faq.perl.org(始终是最新版本)或 perldoc.perl.org 上的 perlfaqs。这样,那些寻找答案的人就可以在谷歌上获得更好的搜索结果。 :) - brian d foy

1

还有一个非 Perl 的解决方案:使用程序 stripcmt

StripCmt 是一个简单的实用程序,用于从 C、C++ 和 Java 源文件中删除注释。按照 Unix 文本处理程序的伟大传统,它可以作为 FIFO(先进先出)过滤器运行或在命令行中接受参数。


另一个选项就是简单地使用 cpp - Michaël

0

删除 /* */ 注释(包括多行)

s/\/\*.*?\*\///gs

我发布这篇文章是因为它很简单,但我相信它会在嵌入式注释等方面出现问题。
/* sdafsdfsdf /*sda asd*/ asdsdf */

但由于它们相对较少见,我更喜欢简单的正则表达式。


-3

包括测试:

use strict;
use warnings;
use Test::More qw(no_plan);
sub strip_comments {
  my $string=shift;
  $string =~ s#/\*.*?\*/##sg; #strip multiline C comments
  return $string;
}
is(strip_comments('a/* comment1 */  code   /* comment2 */b'),'a  code   b');
is(strip_comments('a/* comment1 /* comment2 */b'),'ab');
is(strip_comments("a/* comment1\n\ncomment */ code /* comment2 */b"),'a code b');

3
在字符串中出现的“/”或“/”会导致混乱。例如,字符串“This /* string”不包括注释开头。 - Richard
2
除了不能处理字符串中的注释字符(甚至是多字符常量),它也不能处理反斜杠-换行拼接,这允许开头的斜杠后面跟着反斜杠、换行符,然后是星号。此外,它也不能处理C++注释(这些注释也可以有反斜杠-换行拼接)。它也不能处理三字符组——唯一相关的是“??/”,表示反斜杠。这有多重要取决于您的代码需要多么牢固。 - Jonathan Leffler
mirod的回答更好。 - Chris Huang-Leaver
2
替换注释为空字符串也是不正确的。当标记意外拼接时,这将改变代码的语义。C标准要求在第3转换阶段将注释替换为单个空格字符。 - Jens

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接