从C / C ++代码中删除注释


76

有没有一种简便的方法可以在不进行任何预处理的情况下从C / C ++源文件中删除注释。(即,我认为您可以使用gcc -E,但这将扩展宏。)我只希望除去注释的源代码,没有其他更改。

编辑:

偏爱现有工具。我不想自己用正则表达式编写代码,我预见代码中会出现太多惊喜。


5
使用简单的词法分析器和解析器实际上是一个很好的练习!
格雷格·休吉尔

57
对于使用非常复杂的词法分析器和解析器,这实际上是一个很好的练习。

4
@Pascal:我不相信Dobbs博士和gcc同意:error: pasting "/" and "/" does not give a valid preprocessing token-这是可以预期的,因为注释的删除发生在预处理之前
Christoph

2
@Neil:对不起,但是没有。解析器处理语句的结构。从语言的角度来看,注释是不包含任何较大结构的单个标记。它与空格字符没有什么不同(事实上,在翻译的第三阶段,每个注释都将被单个空格字符代替)。至于将预处理器构建到编译器中,说明要简单得多:预处理器通常会产生很大的输出,因此将其有效地传递给编译器可以大大提高编译速度。
杰里·科芬

7
@Neil:也许那是最好的-您似乎只是在没有支持证据的情况下重复相同的断言。您甚至都没有指出您认为正确解析注释所需的语义分析,只是重复了一次(标准不仅不需要,而且甚至不允许)。您可以替换三边字母,拼接线,然后将源分成标记和空白序列(包括注释)。如果您尝试考虑更多的语义,那说明您做错了……
Jerry Coffin 2010年

Answers:


108

在您的源文件上运行以下命令:

gcc -fpreprocessed -dD -E test.c

感谢KennyTM找到正确的标志。这是完整性的结果:

test.c:

#define foo bar
foo foo foo
#ifdef foo
#undef foo
#define foo baz
#endif
foo foo
/* comments? comments. */
// c++ style comments

gcc -fpreprocessed -dD -E test.c

#define foo bar
foo foo foo
#ifdef foo
#undef foo
#define foo baz
#endif
foo foo

3
我认为Mike期望的结果是#define foo bar\nfoo foo foo
Pascal Cuoq 2010年

3
@Pascal:也可以运行gcc -fpreprocessed -dM -E test.c以获取#define-s,但是它们不在原始位置。
kennytm 2010年

14
我在gcc选项中添加了-P,以消除在删除函数开始注释时有时会出现的怪异行标记。
达娜·罗宾逊

2
我还需要添加-P以获得可用的输出。
詹姆斯·约翰斯顿

1
我只是尝试了一下,然后将其插入#included文件,并用空白行替换了注释行,而不是删除注释。FWIW sed和gcc的组合对我来说一直很完美,请参见stackoverflow.com/a/13062682/1745001
Ed Morton

15

这取决于您的评论有多不正确。我有一个scc剥离C和C ++注释的程序。我也有一个测试文件,我尝试了GCC(在MacOS X上为4.2.1)并使用了当前选择的答案中的选项-而且GCC在某些令人费解的注释中似乎做得并不完美。测试用例。

注意:这不是现实生活中的问题-人们不会编写如此可怕的代码。

考虑一下测试用例(子集-总共135行中的36行):

/\
*\
Regular
comment
*\
/
The regular C comment number 1 has finished.

/\
\/ This is not a C++/C99 comment!

This is followed by C++/C99 comment number 3.
/\
\
\
/ But this is a C++/C99 comment!
The C++/C99 comment number 3 has finished.

/\
\* This is not a C or C++ comment!

This is followed by regular C comment number 2.
/\
*/ This is a regular C comment *\
but this is just a routine continuation *\
and that was not the end either - but this is *\
\
/
The regular C comment number 2 has finished.

This is followed by regular C comment number 3.
/\
\
\
\
* C comment */

在我的Mac上,GCC(gcc -fpreprocessed -dD -E subset.c)的输出为:

/\
*\
Regular
comment
*\
/
The regular C comment number 1 has finished.

/\
\/ This is not a C++/C99 comment!

This is followed by C++/C99 comment number 3.
/\
\
\
/ But this is a C++/C99 comment!
The C++/C99 comment number 3 has finished.

/\
\* This is not a C or C++ comment!

This is followed by regular C comment number 2.
/\
*/ This is a regular C comment *\
but this is just a routine continuation *\
and that was not the end either - but this is *\
\
/
The regular C comment number 2 has finished.

This is followed by regular C comment number 3.
/\
\
\
\
* C comment */

“ scc”的输出为:

The regular C comment number 1 has finished.

/\
\/ This is not a C++/C99 comment!

This is followed by C++/C99 comment number 3.
/\
\
\
/ But this is a C++/C99 comment!
The C++/C99 comment number 3 has finished.

/\
\* This is not a C or C++ comment!

This is followed by regular C comment number 2.

The regular C comment number 2 has finished.

This is followed by regular C comment number 3.

“ scc -C”(可识别双斜杠注释)的输出为:

The regular C comment number 1 has finished.

/\
\/ This is not a C++/C99 comment!

This is followed by C++/C99 comment number 3.

The C++/C99 comment number 3 has finished.

/\
\* This is not a C or C++ comment!

This is followed by regular C comment number 2.

The regular C comment number 2 has finished.

This is followed by regular C comment number 3.

现在可以在GitHub上获得SCC的源代码

尽管Git版本创建于2017年1月18日(在美国/太平洋时区),但SCC的当前版本为6.60(日期为2016-06-12)。该代码可从GitHub上的https://github.com/jleffler/scc-snapshots获得。您还可以找到先前发行版(4.03、4.04、5.05)和两个预发行版(6.16、6.50)的快照—均已标记release/x.yz

该代码仍然主要是在RCS下开发的。我仍在研究如何使用子模块或类似的机制来处理常见的库文件,例如stderr.cstderr.h(也可以在https://github.com/jleffler/soq中找到)。

SCC版本6.60试图了解C ++ 11,C ++ 14和C ++ 17的构造,例如二进制常量,数字标点,原始字符串和十六进制浮点数。默认为C11模式操作。(请注意,上述-C标志的含义在答案正文中描述的4.0x版本和当前最新版本的6.60之间切换。)


5
相信我乔纳森,他们会的。我清除了代码,并注释了2000行代码。我简直不敢相信人类会如何编写这种凌乱的代码。
Halil Kaskavalci 2012年

您可以发布此程序并在此处提供链接吗?(如果它是自由/免费软件)
Totor 2013年

@Totor:它是免费/自由(默认为GPL v3)软件。给我发送电子邮件,我将发送给我(我的电子邮件地址在我的个人资料中)。我根本没有任何地方可以定期发布类似的代码(可悲的,不是!)。
乔纳森·莱夫勒

@JonathanLeffler为什么不将代码发布在类似GitHub的东西上?
Mads Hansen

@JonathanLeffler你能把它放到gists.github.com上吗?我需要它。
noɥʇʎԀʎzɐɹƆ

8

gcc -fpreprocessed -dD -E对我不起作用,但是此程序可以实现:

#include <stdio.h>

static void process(FILE *f)
{
 int c;
 while ( (c=getc(f)) != EOF )
 {
  if (c=='\'' || c=='"')            /* literal */
  {
   int q=c;
   do
   {
    putchar(c);
    if (c=='\\') putchar(getc(f));
    c=getc(f);
   } while (c!=q);
   putchar(c);
  }
  else if (c=='/')              /* opening comment ? */
  {
   c=getc(f);
   if (c!='*')                  /* no, recover */
   {
    putchar('/');
    ungetc(c,f);
   }
   else
   {
    int p;
    putchar(' ');               /* replace comment with space */
    do
    {
     p=c;
     c=getc(f);
    } while (c!='/' || p!='*');
   }
  }
  else
  {
   putchar(c);
  }
 }
}

int main(int argc, char *argv[])
{
 process(stdin);
 return 0;
}

5
不处理三部曲。
OmnipotentEntity

7

有一个stripcmt程序可以执行以下操作:

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

(根据hlovdal的回答:有关此Python代码的问题


1
该代码仍然存在一些错误。例如,它不能处理类似的代码int /* comment // */ main()
pynexj 2014年

并在处理类似注释的评论时遇到错误// comment out next line \
sleepsort 2014年

我的回答处理了这些情况。它完美的作品,只要/*//*/不分成两行。
qeatzy

4

这是一个Perl脚本,用于删除//单行和/ *多行* /注释

  #!/usr/bin/perl

  undef $/;
  $text = <>;

  $text =~ s/\/\/[^\n\r]*(\n\r)?//g;
  $text =~ s/\/\*+([^*]|\*(?!\/))*\*+\///g;

  print $text;

它需要您的源文件作为命令行参数。将脚本保存到文件中,例如说remove_comments.pl并使用以下命令调用它:perl -w remove_comments.pl [您的源文件]

希望对您有所帮助


2
似乎无法处理兔子孔下方包含"/*""//"等的字符串。
akavel

3

我也有这个问题。我发现此工具(Cpp-Decomment)对我有用。但是,它忽略注释行是否延伸到下一行。例如:

// this is my comment \
comment continues ...

在这种情况下,我无法在程序中找到方法,因此只能搜索被忽略的行并手动进行修复。我相信会有一个选择,或者您可以更改程序的源文件来这样做。


2

因为您使用C,所以您可能想使用对C来说“自然”的东西。您可以使用C预处理器来删除注释。下面给出的示例与GCC中的C预处理器一起使用。它们也应该与其他C处理器相同或相似的方式工作。

对于C,使用

cpp -dD -fpreprocessed -o output.c input.c

它还可用于从JSON删除注释,例如:

cpp -P -o - - <input.json >output.json

如果你的C预处理不能直接访问,你可以尝试更换cpp使用cc -E,这就要求C编译器,告诉它的预处理阶段之后停止。如果您的C编译器二进制文件不是cc,则可以用ccC编译器二进制文件的名称替换,例如clang。请注意,并非所有预处理器都支持-fpreprocessed


1

我使用标准的C库编写了一个C程序,大约200行,其中删除了C源代码文件的注释。 qeatzy / removecomments

行为

  1. 跨越多行或占据整行的C样式注释被清零。
  2. 行中间的C样式注释保持不变。例如,void init(/* do initialization */) {...}
  3. 占据整行的C ++样式注释被清零。
  4. 通过检查"和尊重C字符串文字\"
  5. 处理行继续。如果前一行以结尾\,则当前行是前一行的一部分。
  6. 行号保持不变。归零的行或部分行变为空。

测试与分析

我使用包含很多注释的最大cpython源代码进行了测试。在这种情况下,它可以正确,快速完成工作,比gcc快2-5

time gcc -fpreprocessed -dD -E Modules/unicodeobject.c > res.c 2>/dev/null
time ./removeccomments < Modules/unicodeobject.c > result.c

用法

/path/to/removeccomments < input_file > output_file

0

我相信如果您使用一条陈述,就可以轻松地从C中删除注释

perl -i -pe ‘s/\\\*(.*)/g’ file.c This command Use for removing * C style comments 
perl -i -pe 's/\\\\(.*)/g' file.cpp This command Use for removing \ C++ Style Comments

此命令的唯一问题是它不能删除包含多行的注释。但是通过使用此regEx,您可以轻松实现多行注释的逻辑


0

最近,我写了一些Ruby代码来解决这个问题。我考虑了以下例外情况:

  • 字符串注释
  • 在一行上多行注释,修复贪婪匹配。
  • 多行多行

这是代码

它使用以下代码预处理每行,以防这些注释出现在字符串中。如果它出现在您的代码中,那就很不幸。您可以将其替换为更复杂的字符串。

  • MUL_REPLACE_LEFT =“ MUL_REPLACE_LEFT
  • MUL_REPLACE_RIGHT =“ MUL_REPLACE_RIGHT
  • SIG_REPLACE =“ SIG_REPLACE

用法: ruby -w inputfile outputfile


-1

我知道已经晚了,但是我想我应该分享我的代码和我第一次尝试编写编译器。

注意:这不"\*/"包括多行注释中的内容,例如/\*...."*/"...\*。再说一次,gcc 4.8.1也没有。

void function_removeComments(char *pchar_sourceFile, long long_sourceFileSize)
{
    long long_sourceFileIndex = 0;
    long long_logIndex = 0;

    int int_EOF = 0;

    for (long_sourceFileIndex=0; long_sourceFileIndex < long_sourceFileSize;long_sourceFileIndex++)
    {
        if (pchar_sourceFile[long_sourceFileIndex] == '/' && int_EOF == 0)
        {
            long_logIndex = long_sourceFileIndex;  // log "possible" start of comment

            if (long_sourceFileIndex+1 < long_sourceFileSize)  // array bounds check given we want to peek at the next character
            {
                if (pchar_sourceFile[long_sourceFileIndex+1] == '*') // multiline comment
                {
                    for (long_sourceFileIndex+=2;long_sourceFileIndex < long_sourceFileSize; long_sourceFileIndex++)
                    {
                        if (pchar_sourceFile[long_sourceFileIndex] == '*' && pchar_sourceFile[long_sourceFileIndex+1] == '/')
                        {
                            // since we've found the end of multiline comment
                            // we want to increment the pointer position two characters
                            // accounting for "*" and "/"
                            long_sourceFileIndex+=2;  

                            break;  // terminating sequence found
                        }
                    }

                    // didn't find terminating sequence so it must be eof.
                    // set file pointer position to initial comment start position
                    // so we can display file contents.
                    if (long_sourceFileIndex >= long_sourceFileSize)
                    {
                        long_sourceFileIndex = long_logIndex;

                        int_EOF = 1;
                    }
                }
                else if (pchar_sourceFile[long_sourceFileIndex+1] == '/')  // single line comment
                {
                    // since we know its a single line comment, increment file pointer
                    // until we encounter a new line or its the eof 
                    for (long_sourceFileIndex++; pchar_sourceFile[long_sourceFileIndex] != '\n' && pchar_sourceFile[long_sourceFileIndex] != '\0'; long_sourceFileIndex++);
                }
            }
        }

        printf("%c",pchar_sourceFile[long_sourceFileIndex]);
     }
 }

我对您的“不处理”评论感到好奇。我无法弄清您认为无法解决的问题。注意,一旦/*处理完毕,下一个无间隔的字符序列将*/终止注释;注释中没有转义机制-这可能就是您GCC也不处理它​​的意思。您的代码存在问题"/* Magritte notes: Ceci n'est pas une commentaire */"(因为它是字符串文字,而不是注释,但是他在谈论管道而不是注释)。
乔纳森·莱夫勒

-3
#include<stdio.h>
{        
        char c;
        char tmp = '\0';
        int inside_comment = 0;  // A flag to check whether we are inside comment
        while((c = getchar()) != EOF) {
                if(tmp) {
                        if(c == '/') {
                                while((c = getchar()) !='\n');
                                tmp = '\0';
                                putchar('\n');
                                continue;
                        }else if(c == '*') {
                                inside_comment = 1;
                                while(inside_comment) {
                                        while((c = getchar()) != '*');
                                        c = getchar();
                                        if(c == '/'){
                                                tmp = '\0';
                                                inside_comment = 0;
                                        }
                                }
                                continue;
                        }else {
                                putchar(c);
                                tmp = '\0';
                                continue;
                        }
                }
                if(c == '/') {
                        tmp = c;
                } else {
                        putchar(c);
                }
        }
        return 0;
}

该程序在以下两个条件下运行://和/ / ..... /


6
几个问题。1.你失踪了int main(void)。2.它不处理字符串文字和字符常量中的注释定界符。3.删除单个/字符(尝试在其自己的源代码上运行)。
基思·汤普森
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.