Python代码片段来删除C和C ++注释

我正在寻找从string中删除C和C ++注释的Python代码。 (假定string包含一个完整的C源文件。)

我意识到我可以.match()子string与正则expression式,但是这并不解决嵌套/* ,或有一个/* */里面。

理想情况下,我宁愿一个非天真的实现,正确处理尴尬的情况。

我不知道你是否熟悉基于UNIX的(但是Windows可用的)文本parsing程序sed,但是我在这里find了一个sed脚本,它将从文件中删除C / C ++注释。 这很聪明; 例如,如果在string声明中发现,它将忽略“//”和“/ *”。从Python内部可以使用下面的代码:

 import subprocess from cStringIO import StringIO input = StringIO(source_code) # source_code is a string with the source code. output = StringIO() process = subprocess.Popen(['sed', '/path/to/remccoms3.sed'], input=input, output=output) return_code = process.wait() stripped_code = output.getvalue() 

在这个程序中, source_code是持有C / C ++源代码的variables,并且最终stripped_code将保留删除注释的C / C ++代码。 当然,如果你在磁盘上有这个文件,你可以使inputoutputvariables成为指向这些文件的文件句柄(在读模式下input ,在写模式下output )。 remccoms3.sed是上述链接中的文件,应该保存在磁盘上可读的位置。 sed在Windows上也是可用的,并且默认安装在大多数GNU / Linux发行版和Mac OS X上。

这可能比纯Python解决scheme更好; 没有必要重新发明轮子。

这处理C ++风格的注释,C风格的注释,string和简单的嵌套。

 def comment_remover(text): def replacer(match): s = match.group(0) if s.startswith('/'): return " " # note: a space and not an empty string else: return s pattern = re.compile( r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', re.DOTALL | re.MULTILINE ) return re.sub(pattern, replacer, text) 

需要包含string,因为里面的注释标记不会发表评论。

编辑: re.sub没有采取任何标志,所以必须先编译模式。

编辑2:添加字符文字,因为它们可以包含引号,否则会被识别为string分隔符。

Edit3:修正了法律expression式int/**/x=5; 会变成intx=5; 它不会编译,用空格而不是空stringreplace注释。

C(和C ++)注释不能嵌套。 正则expression式运作良好:

 //.*?\n|/\*.*?\*/ 

这需要“单行”标志( Re.S ),因为C注释可以跨越多行。

 def stripcomments(text): return re.sub('//.*?\n|/\*.*?\*/', '', text, flags=re.S) 

这个代码应该工作。

/编辑:请注意,我的上面的代码实际上是作出关于行尾的假设! 此代码不能在Mac文本文件上工作。 但是,这可以相对容易地修改:

 //.*?(\r\n?|\n)|/\*.*?\*/ 

这个正则expression式应该适用于所有的文本文件,而不pipe它们的行结束(包括Windows,Unix和Mac行结尾)。

/编辑:MizardX和Brian(在评论中)提出了有关string处理的有效评论。 我完全忘记了这一点,因为上面的正则expression式是从一个parsing模块中获取的,它对string有额外的处理。 MizardX的解决scheme应该工作得很好,但它只处理双引号的string。

不要忘了在C中,在处理注释之前消除了反斜线新行,并且在之前处理了三angular形(因为?? /是反斜杠的三字形)。 我有一个名为SCC的C程序(带C / C ++注释),这里是testing代码的一部分…

 " */ /* SCC has been trained to know about strings /* */ */"! "\"Double quotes embedded in strings, \\\" too\'!" "And \ newlines in them" "And escaped double quotes at the end of a string\"" aa '\\ n' OK aa "\"" aa "\ \n" This is followed by C++/C99 comment number 1. // C++/C99 comment with \ continuation character \ on three source lines (this should not be seen with the -C fla The C++/C99 comment number 1 has finished. This is followed by C++/C99 comment number 2. /\ /\ C++/C99 comment (this should not be seen with the -C flag) The C++/C99 comment number 2 has finished. This is followed by regular C comment number 1. /\ *\ 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 */ 

这没有说明三字母。 请注意,您可以在一行的末尾有多个反斜杠,但是行拼接并不关心有多less个反斜杠,但是后续的处理可能会这样。 等写一个正则expression式来处理所有这些情况将是非平凡的(但这是不可能的)不同。

这篇文章提供了对Markus Jarderot的代码进行编码的改进版,该代码由atikat描述,在Markus Jarderot的发表评论中。 (感谢提供原始代码,这为我节省了很多工作。)

为了更全面地描述改进:改进保持行号完好。 (这是通过保持C / C ++注释被replace的string中的换行符保持不变)。

这个版本的C / C ++注释删除function适用于当您要为包含行号(例如,对原始文本有效的行号)的用户生成错误消息(例如parsing错误)时。

 import re def removeCCppComment( text ) : def blotOutNonNewlines( strIn ) : # Return a string containing only the newline chars contained in strIn return "" + ("\n" * strIn.count('\n')) def replacer( match ) : s = match.group(0) if s.startswith('/'): # Matched string is //...EOL or /*...*/ ==> Blot out all non-newline chars return blotOutNonNewlines(s) else: # Matched string is '...' or "..." ==> Keep unchanged return s pattern = re.compile( r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"', re.DOTALL | re.MULTILINE ) return re.sub(pattern, replacer, text) 

正则expression式的情况在某些情况下会下降,比如string文字包含与注释语法匹配的子序列。 你真的需要一个parsing树来处理这个问题。

你可以利用py ++来parsingC ++的GCC源代码。

Py ++不会重新发明轮子。 它使用GCC C ++编译器来parsingC ++源文件。 更确切地说,工具链看起来像这样:

源代码被传递给GCC-XML GCC-XML将其传递给GCC C ++编译器GCC-XML从GCC的内部表示生成一个C ++程序的XML描述。 Py ++使用pygccxml包来读取GCC-XML生成的文件。 底线 – 你可以肯定,所有你的声明是正确的读取。

或者可能不是。 不pipe怎样,这不是一个简单的parsing。

@基于RE的解决scheme – 你不可能find一个正确处理所有可能的“尴尬”情况的RE,除非你限制input(例如没有macros)。 对于一个防弹解决scheme,你真的没有select比利用真正的语法。

对不起,这不是一个Python解决scheme,但你也可以使用一个理解如何删除注释的工具,比如你的C / C ++预处理器。 这是GNU CPP如何做的 。

 cpp -fpreprocessed foo.c 

还有一个非python的答案:使用程序stripcmt :

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

以下为我工作:

 from subprocess import check_output class Util: def strip_comments(self,source_code): process = check_output(['cpp', '-fpreprocessed', source_code],shell=False) return process if __name__ == "__main__": util = Util() print util.strip_comments("somefile.ext") 

这是subprocess和cpp预处理器的组合。 对于我的项目,我有一个名为“Util”的实用程序类,它保留了我使用/需要的各种工具。

你并不需要一个完美的parsing树来完成这个任务,但是你确实需要相当于编译器前端产生的令牌stream。 这样的标记stream必须处理所有的奇怪,比如行续注释开始,注释开始string,三angular标准化等等。如果你有令牌stream,删除注释很容易。 (我有一个工具,可以产生正确的令牌stream,就像猜测真正的parsing器的前端是什么,产生一个真正的parsing树:)。

这些令牌被正则expression式单独识别的事实表明,原则上,你可以写一个正则expression式来挑选评论的词位。 分词器设置正则expression式的真正复杂性(至less是我们写的)意味着你不能在实践中做到这一点; 单独写他们是很难的。 如果你不想完美地做到这一点,那么上面的大多数RE解决scheme都很好。

现在,除非你正在构build一个代码混淆器,否则为什么你想要除去我的条评论。 在这种情况下,你必须完全正确。

最近我遇到了这个问题,当时我拿了一个教授要求我们从我们的源代码中提取javadoc,然后提交给他进行代码审查的课程。 我们必须多次这样做,但是我们不能永久移除javadoc,因为我们还需要生成javadoc html文件。 这是我做的一个小python脚本。 由于javadoc以/ **开始并以* /结尾,因此脚本会查找这些标记,但可以修改脚本以满足您的需要。 它还处理单行块注释和块注释结束的情况,但在块注释结束的同一行仍有未注释的代码。 我希望这有帮助!

警告:此脚本修改传入的文件的内容并将其保存到原始文件。 在别的地方备份是明智的

 #!/usr/bin/python """ A simple script to remove block comments of the form /** */ from files Use example: ./strip_comments.py *.java Author: holdtotherod Created: 3/6/11 """ import sys import fileinput for file in sys.argv[1:]: inBlockComment = False for line in fileinput.input(file, inplace = 1): if "/**" in line: inBlockComment = True if inBlockComment and "*/" in line: inBlockComment = False # If the */ isn't last, remove through the */ if line.find("*/") != len(line) - 3: line = line[line.find("*/")+2:] else: continue if inBlockComment: continue sys.stdout.write(line)