【问题标题】:How can I delete all /* */ comments from a C source file?如何从 C 源文件中删除所有 /* */ 注释?
【发布时间】:2009-11-11 11:15:25
【问题描述】:

我有一个从其他地方复制的 C 文件,但它有很多如下的 cmets:

int matrix[20];
/* generate data */
for (index = 0 ;index < 20; index++)
matrix[index] = index + 1;
/* print original data */
for (index = 0; index < 5 ;index++)

如何删除/**/ 所包含的所有cmets。有时,cmets 由 4-5 行组成,我需要删除所有这些行。

基本上,我需要删除/**/ 之间的所有文本,甚至\n 也可以介于两者之间。请使用sedawkperl 之一帮助我完成此操作。

【问题讨论】:

  • 我喜欢“我有一个 C 文件,但它有很多 cmets”中的“但是”这个词。
  • 毫无疑问,有些 cmets 是疯了。但是要摆脱 *all cmets 吗?

标签: regex perl unix sed awk


【解决方案1】:

为什么不直接使用 c 预处理器来做这件事呢?为什么要将自己限制在本土的正则表达式中?

[编辑] 这种方法还可以干净地处理 Barts printf(".../*...") 场景

例子:

[File: t.c]
/* This is a comment */
int main () {
    /* 
     * This
     * is 
     * a
     * multiline
     * comment
     */
    int f = 42;
    /*
     * More comments
     */
    return 0;
}

.

$ cpp -P t.c
int main () {







    int f = 42;



    return 0;
}

或者您可以删除空格并压缩所有内容

$ cpp -P t.c | egrep -v "^[ \t]*$"
int main () {
    int f = 42;
    return 0;
}

重新发明轮子没用,是吗?

[编辑] 如果您想通过这种方法扩展包含的文件和宏,cpp 会为此提供标志。考虑:

[文件:t.c]

#include <stdio.h>
int main () {
    int f = 42;
    printf("   /*  ");
    printf("   */  ");
    return 0;
}

.

$ cpp -P -fpreprocessed t.c | grep -v "^[ \t]*$"
#include <stdio.h>
int main () {
    int f = 42;
    printf("   /*  ");
    printf("   */  ");
    return 0;
}

有一点需要注意的是,可以避免宏扩展,但宏的原始定义已从源代码中删除。

【讨论】:

  • 预处理器有一个(可能不受欢迎的)“副作用”:它还处理宏、包含包含的文件等等......
  • 你可以通过-fpreprocessed摆脱宏扩展。我会更新以提及这一点
  • -1。如果您希望在删除 cmets 后编译源代码,这不是一个轻微警告。
  • 这个警告可以修复: perl -wpe 's/^\s*#define/#include#define/' your-file.c |cpp -P - -fpreprocessed|perl -wpe ' s/#include#define/#include/ ---- 这会将任何#defines 转换为(有些无效)通过预处理器的#includes,以便稍后转换回正确的#defines。 (如果您同意,请将此添加到答案本身)。
  • 即使这样也很好用:grep -v -E '^#' tutorial.thrift |cpp -P
【解决方案2】:

perlfaq6。这是一个相当复杂的场景。

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

一个警告 - 一旦你这样做了,你有一个测试场景来证明你刚刚移除了 cmets 并且没有任何有价值的东西吗?如果您正在运行如此强大的正则表达式,我会确保进行某种测试(即使您只是记录之前/之后的行为)。

【讨论】:

  • 只需检查编译创建的二进制文件是否相同(模时间戳或其他构建标识)。
  • 这可能是最简单的解决方案
  • 同意,除非我在过滤后进行单元测试以验证其正确性,否则我永远不会对我关心的代码执行此操作。
【解决方案3】:

看看strip_comments routine in Inline::Filters

sub strip_comments {
    my ($txt, $opn, $cls, @quotes) = @_;
    my $i = -1;
    while (++$i < length $txt) {
    my $closer;
        if (grep {my $r=substr($txt,$i,length($_)) eq $_; $closer=$_ if $r; $r}
        @quotes) {
        $i = skip_quoted($txt, $i, $closer);
        next;
        }
        if (substr($txt, $i, length($opn)) eq $opn) {
        my $e = index($txt, $cls, $i) + length($cls);
        substr($txt, $i, $e-$i) =~ s/[^\n]/ /g;
        $i--;
        next;
        }
    }
    return $txt;
}

【讨论】:

    【解决方案4】:

    除非您了解后果,否则请不要使用 cpp

    $ cat t.c
    #include <stdio.h>
    
    #define MSG "Hello World"
    
    int main(void) {
        /* ANNOY: print MSG using the puts function */
        puts(MSG);
        return 0;
    }
    

    现在,让我们通过cpp 运行它:

    $ cpp -P t.c -fpreprocessed
    
    
    #include <stdio.h>
    
    
    
    int main(void) {
    
    
        puts(MSG);
        return 0;
    }
    

    很明显,这个文件不再编译了。

    【讨论】:

    • 好吧,反正不是在你添加-fpreprocessed 标志之后
    • @Hasturkun 如果不加 -fpreprocessed,#include &lt;stdio.h&gt; 会被扩展。
    • 我试过这个: perl -wpe 's/^\s*#define/#include#define/' your-file.c |cpp -P - -fpreprocessed|perl -wpe 's/ #include#define/#include/ ---- 这会将通过预处理器的任何#defines 转换为(有些无效)#includes,以便稍后转换回正确的#defines。
    【解决方案5】:

    考虑:

    printf("... /* ...");
    int matrix[20];
    printf("... */ ...");
    

    换句话说:我不会在此任务中使用正则表达式,除非您正在执行 replace-once 并且肯定不会发生上述情况。

    【讨论】:

      【解决方案6】:

      您必须为此使用 C 预处理器并结合其他工具来临时禁用特定的预处理器功能,例如扩展 #defines 或 #includes,所有其他方法在极端情况下都会失败。这适用于所有情况:

      [ $# -eq 2 ] && arg="$1" || arg=""
      eval file="\$$#"
      sed 's/a/aA/g;s/__/aB/g;s/#/aC/g' "$file" |
                gcc -P -E $arg - |
                sed 's/aC/#/g;s/aB/__/g;s/aA/a/g'
      

      将其放入 shell 脚本并使用您要解析的文件的名称调用它,可选地以“-ansi”之类的标志作为前缀以指定要应用的 C 标准。

      【讨论】:

      • 我怀疑是因为其他发布的解决方案看起来更简单,但是虽然这个解决方案一直有效,但其他解决方案只会在某些时候有效,任何尝试它的人还没有遇到他们选择的情况解决方案失败(或没有注意到失败)。啊,看起来我是在原始问题发布并接受答案 3 年后发布的,所以这可能是一个主要因素!
      • 我记得很久以前做过这样的事情。昨天我又需要它来做一些快速的事情,我知道其他答案不会涵盖所有情况。我希望我能再次投票!
      【解决方案7】:

      在命令行上试试这个(将“文件名”替换为需要处理的文件列表):

      perl -i -wpe 'BEGIN{undef $/} s!/\*.*?\*/!!sg' file-names
      

      该程序就地更改文件(用更正的输出覆盖原始文件)。如果您只想要输出而不更改原始文件,请省略“-i”开关。

      说明:

      perl -- call the perl interpreter
      -i      switch to 'change-in-place' mode.
      -w      print warnings to STDOUT (if there are any)
       p      read the files and print $_ for each record; like while(<>){ ...; print $_;}
       e      process the following argument as a program (once for each input record)
      
      BEGIN{undef $/} --- process whole files instead of individual lines.
      s!      search and replace ...
        /\*     the starting /* marker
        .*?     followed by any text (not gredy search)
        \*/     followed by the */ marker
      !!      replace by the empty string (i.e. remove comments)  
        s     treat newline characters \n like normal characters (remove multi-line comments)
         g    repeat as necessary to process all comments.
      
      file-names   list of files to be processed.
      

      【讨论】:

      • @brian 接受:这只是一个大概的解决方案。
      【解决方案8】:

      当我想要一些简短的 CSS 时,我会使用这个:

      awk -vRS='*/' '{gsub(/\/\*.*/,"")}1' FILE
      

      这不会处理注释分隔符出现在字符串中的情况,但它比解决方案简单得多。显然它不是防弹的,也不是什么都适合,但你比 SO 上的学究更清楚你是否能忍受它。

      我相信this one 防弹的。

      【讨论】:

        【解决方案9】:

        尝试以下递归方式查找和删除 Java 脚本类型 cmets、XML 类型 Comments 和单行 cmets

        /* This is a multi line js comments.
        
        Please remove me*/
        

        find pages/ -name "*.*" 中的 f;做 perl -i -wpe 'BEGIN{undef $/} s!/*.*?*/!!sg' $f;完成

        <!-- This is a multi line xml comments.
        
        Please remove me -->
        

        find pages/ -name "*.*" 中的 f;做 perl -i -wpe 'BEGIN{undef $/} s!!!sg' $f;完成

        //This is single line comment Please remove me.
        

        find pages/ -name "*.*" 中的 f;做 sed -i 's///.*//' $f;完成

        注意:pages 是一个根目录,上面的脚本也会在根目录和子目录中的所有文件中查找和删除。

        【讨论】:

          【解决方案10】:

          使用 gawk 的非常简单的示例。请在实施之前进行多次测试。当然它不关心其他注释样式 // (在 C++ 中??)

          $ more file
          int matrix[20];
          /* generate data */
          for (index = 0 ;index < 20; index++)
          matrix[index] = index + 1;
          /* print original data */
          for (index = 0; index < 5 ;index++)
          /*
          function(){
           blah blah
          }
          */
          float a;
          float b;
          
          $ awk -vRS='*/' '{ gsub(/\/\*.*/,"")}1' file
          int matrix[20];
          
          
          for (index = 0 ;index < 20; index++)
          matrix[index] = index + 1;
          
          
          for (index = 0; index < 5 ;index++)
          
          
          float a;
          float b;
          

          【讨论】:

          • 由于某种原因,这在我的机器上不起作用:(cat test int matrix[20]; /* generate data */ for (index = 0 ;index &lt; 20; index++) matrix[index] = index + 1; /* print original data */,输出为awk -vRS='*/' '{ gsub(/\/\*.*/,"")}1' test int matrix[20]; / generate data / for (index = 0 ;index &lt; 20; index++) matrix[index] = index + 1; / print original data /
          • 对不起,评论太乱了,我没注意到你有输出。好吧,它对我有用。我看到你还有 /generate data/ 和 /print original data/。从我的输出中可以看出,它对我有用。
          • 如果还是不行,可以试试下面的perl解决方案
          猜你喜欢
          • 2011-02-03
          • 1970-01-01
          • 2011-12-25
          • 2011-05-23
          • 2014-05-16
          • 2014-08-10
          • 2018-11-07
          • 2015-02-25
          • 1970-01-01
          相关资源
          最近更新 更多