我正在寻找从字符串中删除C和C ++注释的Python代码。 (假设字符串包含整个C源文件。)
我意识到我可以使用正则表达式来匹配.match()子串,但这不能解决嵌套/*
或//
内的/* */
。
理想情况下,我更喜欢一种能够正确处理尴尬案例的非天真实现。
答案 0 :(得分:80)
它处理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没有带任何标志,所以必须先编译模式。
Edit2:添加了字符文字,因为它们可能包含引号,否则会被识别为字符串分隔符。
编辑3:修复了合法表达式int/**/x=5;
将成为intx=5;
且无法编译的情况,方法是将注释替换为空格而不是空字符串。< / p>
答案 1 :(得分:25)
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的解决方案应该可以很好地工作,但它只处理双引号字符串。
答案 2 :(得分:7)
我不知道您是否熟悉sed
,基于UNIX(但可用Windows)的文本解析程序,但我发现了一个sed脚本here,它将删除来自文件的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解决方案更好;无需重新发明轮子。
答案 3 :(得分:6)
不要忘记在C中,在处理注释之前消除了反斜杠换行符,并且在此之前处理了三字符(因为?? /是反斜杠的三字符)。我有一个名为SCC的C程序(条带C / C ++注释),这是测试代码的一部分......
" */ /* 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 */
这并不是说明三字母。请注意,在行的末尾可以有多个反斜杠,但是行拼接并不关心有多少反斜杠,但后续处理可能会。等等。编写单个正则表达式以处理所有这些情况将是非平凡的(但这是不可能的)。
答案 4 :(得分:6)
这篇帖子提供了一个编码出来的版本,改进了Markus Jarderot的代码,由atikat在Markus Jarderot发表的评论中描述。 (感谢两者提供原始代码,这为我节省了很多工作。)
更全面地描述改进:改进使线路编号完好无损。 (这是通过在替换C / C ++注释的字符串中保持换行符完整来完成的。)
如果您想为用户生成包含行号的错误消息(例如解析错误)(即对原始文本有效的行号),则此版本的C / C ++注释删除功能非常适用。
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)
答案 5 :(得分:4)
正则表达式案例在某些情况下会失败,例如字符串文字包含与注释语法匹配的子序列。你真的需要一个解析树来处理这个问题。
答案 6 :(得分:3)
您可以利用py++来解析使用GCC的C ++源代码。
Py ++不会重新发明轮子。它 使用GCC C ++编译器来解析C ++ 源文件。更准确地说, 工具链看起来像这样:
源代码传递给GCC-XML GCC-XML将它传递给GCC C ++编译器 GCC-XML生成XML描述 来自GCC内部的C ++程序 表示。 Py ++使用pygccxml 包读取GCC-XML生成 文件。底线 - 你可以 当然,你所有的声明都是 正确阅读。
或者,也许不是。无论如何,这不是一个简单的解析。
基于@RE的解决方案 - 除非您限制输入(例如,没有宏),否则您不太可能找到正确处理所有可能“尴尬”情况的RE。对于一个防弹解决方案,你真的别无选择,只能利用真正的语法。
答案 7 :(得分:1)
我很抱歉这不是Python解决方案,但你也可以使用一个了解如何删除注释的工具,比如你的C / C ++预处理器。以下是GNU CPP does it。
的方法cpp -fpreprocessed foo.c
答案 8 :(得分:1)
还有一个非python答案:使用程序stripcmt:
StripCmt是一个简单的实用程序 在C中删除C,C ++中的注释, 和Java源文件。在盛大 Unix文本处理的传统 程序,它可以作为一个 FIFO(先进先出)滤波器或 接受命令行上的参数。
答案 9 :(得分:1)
以下对我有用:
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")
这是子进程和cpp预处理器的组合。对于我的项目,我有一个名为“Util”的实用程序类,我保留了我使用/需要的各种工具。
答案 10 :(得分:0)
你真的不需要一个解析树来完美地完成这个任务,但实际上你需要的令牌流等同于编译器前端产生的令牌流。这样的令牌流必须处理所有奇怪的事情,例如行继续注释开始,字符串中的注释开始,三字符规范化等。如果您有令牌流,则删除注释很容易。 (我有一个工具可以产生完全相同的令牌流,因为,猜猜是什么,一个真正的解析器的前端产生一个真正的解析树:)。
令牌被正则表达式单独识别的事实表明,原则上你可以编写一个正则表达式来挑选注释词。令牌化器的正则表达式的真正复杂性(至少是我们写的那个)表明你不能在实践中这样做;单独写它们很难。如果你不想完美地做到这一点,那么,上面的大多数RE解决方案都很好。
现在,除非您正在构建代码混淆器,否则为什么您希望条带注释超出我的范围。在这种情况下,你必须完全正确。
答案 11 :(得分:0)
我已经使用pygments解析该字符串,然后忽略了所有来自其注释的标记。在pygments列表上的所有词法分析器(包括Javascript,SQL和C Like)上,都可以像超级按钮一样工作。
from pygments import lex
from pygments.token import Token as ParseToken
def strip_comments(replace_query, lexer):
generator = lex(replace_query, lexer)
line = []
lines = []
for token in generator:
token_type = token[0]
token_text = token[1]
if token_type in ParseToken.Comment:
continue
line.append(token_text)
if token_text == '\n':
lines.append(''.join(line))
line = []
if line:
line.append('\n')
lines.append(''.join(line))
strip_query = "\n".join(lines)
return strip_query
使用类似C的语言
from pygments.lexers.c_like import CLexer
strip_comments("class Bla /*; complicated // stuff */ example; // out",CLexer())
# 'class Bla example; \n'
使用SQL语言:
from pygments.lexers.sql import SqlLexer
strip_comments("select * /* this is cool */ from table -- more comments",SqlLexer())
# 'select * from table \n'
使用类似Java语言的语言
from pygments.lexers.javascript import JavascriptLexer
strip_comments("function cool /* not cool*/(x){ return x++ } /** something **/ // end",JavascriptLexer())
# 'function cool (x){ return x++ } \n'
由于此代码仅删除注释,因此将保留任何奇怪的值。因此,这是一个非常强大的解决方案,甚至可以处理无效的输入。
答案 12 :(得分:-1)
我最近遇到了这个问题,当时我上课时教授要求我们从源代码中删除javadoc,然后再将其提交给他进行代码审查。我们不得不多次这样做,但我们不能永久删除javadoc,因为我们还需要生成javadoc html文件。这是我做的一个小蟒蛇脚本。由于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)