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

3

我有一些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

在其他注释中嵌套了注释序列。

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


根据您使用的集成开发环境,可能已经有一种相当自动化的方法来完成此操作。 - John Dibling
7
出于好奇,你为什么想要删除评论?为什么不彻底地去除空格呢? - Dominic Rodger
3
希望这不是一种扭曲的优化尝试... 当你建议使用预处理器时,我真的很害怕。 - Cody Gray
标签“regex”是否意味着您只想使用正则表达式来完成它? - bezmax
1
// 这是一条注释 // 它跨越到下一行 - Gene Bushuyev
实际上,我正在开发一个内部简单工具(使用Ultra-Edit脚本),允许我的团队向我的C++源代码添加简单的注释(就像Java注释一样)。我使用宏来完成这项工作,但发现块注释会干扰它们,因此希望在处理带注释的源代码之前先删除所有注释。 - JavaMan
6个回答

3

C预处理器可以删除注释。

编辑:

我已更新,以便我们可以使用宏来扩展#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命令。
然后,我们将#define和原始文件再次通过预处理器进行管道处理,但这次要防止包含文件被展开。
> 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指令提供给预处理器以使其正常工作。 - JavaMan
确实是非常巧妙的解决方案。但是是否有Visual Studio等效的解决方案呢? - JavaMan
@JavaMan:是的,请安装Cygwin。 - Martin York

2

是否可以给自己的问题投票?

感谢Martin York的建议,我发现在Visual Studio中,解决方案看起来非常简单(需要进一步测试)。只需将所有预处理指令重命名为其他内容(不是有效的c++语法也可以),然后使用带有/P参数的cl.exe即可。

cl target.cpp /P

它生成一个target.i,并包含源代码去除注释的部分。只需将先前的指令重命名即可。可能需要删除cl.exe生成的#line指令。
这是因为根据MSDN,翻译的阶段如下: 字符映射 将源文件中的字符映射到内部源表示形式。在此阶段,三字符序列将被转换为单个字符的内部表示形式。 行拼接 以反斜杠()结尾且紧接着换行符的所有行都与源文件中的下一行连接起来,从物理行形成逻辑行。除非为空,否则源文件必须以不带反斜杠的换行符结尾。 标记化 将源文件分解为预处理标记和空格字符。源文件中的注释将替换为一个空格字符。换行符保留。 预处理 执行预处理指令并将宏展开到源文件中。#include语句在任何包含的文本上调用前面三个翻译步骤的翻译。 字符集映射 将所有源字符集成员和转义序列转换为其在执行字符集中的等效项。对于Microsoft C和C++,源和执行字符集都是ASCII。 字符串连接 所有相邻的字符串和宽字符串文字都将被连接。例如,"String " "concatenation"变为"String concatenation"。 翻译 所有标记都被语法和语义地分析;这些标记转换为目标代码。 链接 解析所有外部引用以创建可执行程序或动态链接库
标记化之前删除注释。因此,在预处理阶段期间,请确保没有任何内容可供处理(删除所有指令),其输出应该只是由前三个阶段处理的内容。
至于用户定义的.h文件,请使用/FI选项手动包含它们。生成的.i文件将是.cpp和.h的组合。没有注释。每个片段都以正确的文件名前缀#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");
}

2
我们的SD C++格式化程序有一个选项,可以对源代码进行漂亮的打印并删除所有注释。它使用我们完整的C++前端来解析文本,因此不会被空格、换行符、字符串字面值或预处理器问题所困扰,也不会因其格式更改而破坏代码。
如果您正在删除评论,您可能正在试图混淆源代码。格式化程序还有一个混淆版本。

1

您可以使用基于规则的解析器(例如boost::spirit)编写注释的语法规则。根据您的编译器,您需要决定是否处理嵌套的注释。移除注释的语义操作应该非常直观。


这是行不通的,因为你需要编写整个语言的规则。用这种方式做就像尝试编写正则表达式一样,而语言过于复杂,无法正确完成此任务。 - Martin York

1

正则表达式并不适合用于解析语言,最多只是令人沮丧的尝试。

对于此类问题,你实际上需要一个完整的解析器。你可以考虑使用 Clang,重新编写代码是 Clang 库套件明确追求的目标,而且已经有许多现成的重写器可供参考。


-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;
}

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