【问题标题】:Removing C++ Comment From Source Code从源代码中删除 C++ 注释
【发布时间】:2011-05-23 13:42:01
【问题描述】:

我有一些带有/* */// 样式cmets 的c++ 代码。我想有一种方法可以自动将它们全部删除。显然,使用带有一些正则表达式的编辑器(例如ultraedit)搜索/**/// 应该可以完成这项工作。但是,仔细观察,一个完整的解决方案并不是那么简单,因为如果序列 /* 或 // 在另一个注释、字符串文字或字符文字中,它们可能不代表评论。例如

printf(" \" \" " "  /* this is not a comment and is surrounded by an unknown number of double-quotes */");

是双引号内的注释序列。而且,确定一个字符串是否在一对有效的双引号内并不是一项简单的任务。虽然这

// this is a single line comment /* <--- this does not start a comment block 
// this is a second comment line with an */ within

是其他 cmets 中的注释序列。

考虑到字符串文字和注释,是否有更全面的方法从 C++ 源代码中删除 cmets?例如,我们可以指示预处理器在不执行 #include 指令的情况下删除 cmets 吗?

【问题讨论】:

  • 根据您的 IDE,可能已经有办法以相当自动化的方式执行此操作。
  • 出于兴趣,为什么要删除 cmets?为什么不直接删除空格呢?
  • 我希望这不是某种扭曲的优化尝试……当你建议使用预处理器时,我真的很害怕。
  • 标签regex是否意味着你想单独使用正则表达式?
  • 别忘了注意延续://这是一个注释\
    ,它运行到下一行

标签: c++ regex comments


【解决方案1】:
#include <iostream>
#include<fstream>
using namespace std;

int main() {
    ifstream fin;
    ofstream fout;
    fin.open("input.txt");
    fout.open("output.txt");
    char ch;
    while(!fin.eof()){
        fin.get(ch);
        if(ch=='/'){
            fin.get(ch);
            if(ch=='/' )
            {   //cout<<"Detected\n";
                fin.get(ch);
                while(!(ch=='\n'||ch=='\0'))
                {
                //cout<<"while";
                fin.get(ch);
                }
            }
            if(ch=='*')
            {
                fin.get(ch);
                while(!(ch=='*')){
                    fin.get(ch);
                }
                fin.get(ch);
                if(ch=='/'){
                //  cout<<"Detected Multi-Line\n";
                    fin.get(ch);
                }

            }
        }
        fout<<ch;
    }
    return 0;
}

【讨论】:

    【解决方案2】:

    请有人对我自己的问题的答案投票。

    感谢Martin York 的 想法,我发现在 Visual Studio 中,解决方案看起来非常简单(有待进一步测试)。只需将所有预处理器指令重命名为其他内容(可以使用无效的 c++ 语法)并将 cl.exe 与 /P

    一起使用
    cl target.cpp /P
    

    它会产生一个target.i。它包含源减去 cmets。只需将以前的指令重命名回来即可。可能您需要删除 cl.exe 生成的 #line 指令。

    这是可行的,因为根据 MSDN,翻译的阶段是这样的:

    字符映射 源文件中的字符映射到内部源表示。在这个阶段,三字母序列被转换为单字符的内部表示。

    线拼接 所有以反斜杠 () 结尾并紧跟换行符的行都与源文件中的下一行连接起来,形成物理行的逻辑行。除非它为空,否则源文件必须以不带反斜杠的换行符结尾。

    标记化 源文件分为预处理标记和空白字符。源文件中的注释分别替换为一个空格字符。保留换行符。

    预处理 执行预处理指令并将宏扩展到源文件中。 #include 语句从任何包含文本的前三个翻译步骤开始调用翻译。

    字符集映射 所有源字符集成员和转义序列都被转换为它们在执行字符集中的等价物。对于 Microsoft C 和 C++,源字符集和执行字符集都是 ASCII。

    字符串连接 所有相邻的字符串和宽字符串文字都被连接起来。例如,“String”、“concatenation”变成“String concatenation”。

    翻译 所有标记都经过语法和语义分析;这些标记被转换成目标代码。

    联动 解析所有外部引用以创建可执行程序或动态链接库

    预处理阶段之前的标记化期间删除评论。所以只要确保在预处理阶段,没有可用的 用于处理(删除所有指令),其输出应该只是前三个阶段处理的那些。

    对于用户定义的 .h 文件,使用 /FI 选项手动包含它们。生成的 .i 文件将是 .cpp 和 .h 的组合。没有 cmets。每个片段前面都有一个带有正确文件名的#line。因此,编辑器很容易将它们分开。如果我们不想手动拆分它们,可能我们需要使用某些编辑器的宏/脚本工具来自动完成。

    所以,现在,我们不必关心任何预处理器指令。更好的是处理了续行字符(反斜杠)。

    例如

    // vc8.cpp : Defines the entry point for the console application.
    //
    
    -#include "stdafx.h"
    -#include <windows.h>
    -#define NOERR
    -#ifdef NOERR
      /* comment here */
     whatever error line is ok
    -#else
      some error line if NOERR not defined
          // comment here
    -#endif
    void pr() ;
    int _tmain(int argc, _TCHAR* argv[])
    {
        pr();
        return 0;
    }
    
    /*comment*/
    
    void pr() {
        printf(" /* "); /* comment inside string " */
        // comment terminated by \
        continue a comment line
        printf(" "); /** " " string inside comment */
        printf/* this is valid comment within line continuation */\
    ("some weird lines \
    with line continuation");
    }
    

    cl.exe vc8.cpp /P之后,它变成了这个,然后可以在恢复指令(并删除#line)后再次提供给cl.exe

    #line 1 "vc8.cpp"
    
    
    
    -#include "stdafx.h"
    -#include <windows.h>
    -#define NOERR
    -#ifdef NOERR
    
     whatever error line is ok
    -#else
      some error line if NOERR not defined
    
    -#endif
    void pr() ;
    int _tmain(int argc, _TCHAR* argv[])
    {
        pr();
        return 0;
    }
    
    
    
    void pr() {
        printf(" /* "); 
    
    
        printf(" "); 
        printf\
    ("some weird lines \
    with line continuation");
    }
    

    【讨论】:

      【解决方案3】:

      C 预处理器可以删除 cmets。

      编辑:

      我已经更新,以便我们可以使用 MACROS 来扩展 #if 语句

      > cat t.cpp
      /*
       * Normal comment
       */
      // this is a single line comment /* <--- this does not start a comment block 
      // this is a second comment line with an */ within
      #include <stdio.h>
      
      #if __SIZEOF_LONG__ == 4
      int bits = 32;
      #else
      int bits = 16;
      #endif
      
      int main()
      {
          printf(" \" \" " " /* this is not a comment and is surrounded by an unknown number of double-quotes */");
          /*
           * comment with a single // line comment enbedded.
           */
          int x;
          // A single line comment /* Normal enbedded */ Comment
      }
      

      因为我们希望 #if 语句正确扩展,我们需要一个定义列表。
      这是相对微不足道的。 cpp -E -dM.

      然后我们通过预处理器将#defines 和原始文件传回,但这次阻止包含扩展。

      > cpp -E -dM t.cpp > /tmp/def
      > cat /tmp/def t.cpp | sed -e s/^#inc/-#inc/ | cpp - | sed s/^-#inc/#inc/
      # 1 "t.cpp"
      # 1 "<built-in>"
      # 1 "<command-line>"
      # 1 "t.cpp"
      
      
      
      
      
      
      #include <stdio.h>
      
      
      int bits = 32;
      
      
      
      
      int main()
      {
          printf(" \" \" " " /* this is not a comment and is surrounded by an unknown number of double-quotes */");    
      
      
      
          int x;
      
      }
      

      【讨论】:

      • 问题是,我们仍然需要知道使用哪个#ifdef,而这又可能需要在#include 文件中定义一些#define。所以我认为我们仍然需要将所有#include 指令提供给预处理器以使其正常工作。
      • 确实是非常巧妙的解决方案。但是是否有与 Visual Studio 等效的解决方案?
      • @JavaMan:是的,安装 cygwin。
      【解决方案4】:

      正则表达式并不是用来解析语言的,它充其量只是一种令人沮丧的尝试。

      为此,您实际上需要一个成熟的解析器。您可能希望考虑 Clang,重写是 Clang 库套件的明确目标,并且已经实现了现有的重写器,您可以从中获得灵感。

      【讨论】:

        【解决方案5】:

        我们的SD C++ Formatter 可以选择漂亮地打印源文本并删除所有 cmets。它使用我们完整的 C++ 前端来解析文本,因此它不会被空格、换行符、字符串文字或预处理器问题所迷惑,也不会因格式更改而破坏代码。

        如果您要删除 cmets,您可能试图混淆源代码。 Formatter 也有一个混淆版本。

        【讨论】:

          【解决方案6】:

          您可以使用基于规则的解析器(例如 boost::spirit)为 cmets 编写语法规则。您将需要根据您的编译器决定是否处理嵌套的 cmets。删除 cmets 的语义操作应该非常简单。

          【讨论】:

          • 这不是首发,因为您需要为整个语言编写规则。这样做与尝试编写正则表达式相同,而且要正确完成此操作,语言要复杂得多。
          猜你喜欢
          • 2015-01-09
          • 1970-01-01
          • 1970-01-01
          • 2011-02-03
          • 2016-07-27
          • 1970-01-01
          • 2015-05-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多