如何用Python去除C和C++注释?
我在找一段Python代码,可以从一个字符串中去掉C和C++的注释。(假设这个字符串包含了整个C源文件。)
我知道可以用正则表达式来匹配子字符串,但这样做不能解决注释嵌套的问题,比如/*
里面还有//
的情况。
理想情况下,我希望有一个更聪明的实现,能够正确处理这些复杂的情况。
13 个回答
C(和C++)的注释不能嵌套,也就是说你不能在一个注释里面再写一个注释。用正则表达式来处理这个问题效果不错:
//.*?\n|/\*.*?\*/
这里需要使用“单行”标志(Re.S
),因为C的注释可以跨越多行。
def stripcomments(text):
return re.sub('//.*?\n|/\*.*?\*/', '', text, flags=re.S)
这段代码应该能正常工作。
/编辑:注意我上面的代码实际上假设了行结束符!这段代码在Mac的文本文件上可能无法工作。不过,这个问题可以相对简单地解决:
//.*?(\r\n?|\n)|/\*.*?\*/
这个正则表达式应该能在所有文本文件上正常工作,不管它们的行结束符是什么(包括Windows、Unix和Mac的行结束符)。
/编辑:MizardX和Brian(在评论中)提到了关于字符串处理的有效意见。我完全忘记了这一点,因为上面的正则表达式是从一个有额外字符串处理功能的解析模块中提取的。MizardX的解决方案应该很好,但它只处理双引号的字符串。
这段代码处理了C++风格的注释、C风格的注释、字符串以及它们之间简单的嵌套关系。
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)
字符串需要被包含在内,因为字符串里的注释标记不会开始一个注释。
编辑:re.sub没有使用任何标志,所以必须先编译模式。
编辑2:添加了字符字面量,因为它们可能包含引号,这些引号会被识别为字符串的分隔符。
编辑3:修复了一个情况:合法的表达式int/**/x=5;
会变成intx=5;
,这样是无法编译的。通过用空格替换注释,而不是用空字符串,解决了这个问题。
我不知道你是否听说过 sed
,这是一个基于UNIX的文本处理程序(在Windows上也可以用),我找到了一段 这里 的sed脚本,可以用来从文件中去掉C/C++的注释。这个脚本非常聪明,比如说它会忽略在字符串声明中的'//'和'/*'等注释符号。从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++的源代码,而最后 stripped_code
会存放去掉注释后的C/C++代码。当然,如果你有文件在磁盘上,你可以让 input
和 output
变量指向那些文件(input
用于读取,output
用于写入)。remccoms3.sed
是上面链接中的文件,应该保存在一个可以读取的位置。sed
在Windows上也可以使用,并且大多数GNU/Linux发行版和Mac OS X上默认就安装了。
这可能比纯Python的解决方案要好,因为没有必要重新发明轮子。