Python正则表达式问题:去除多行注释但保留换行符

4 投票
5 回答
8930 浏览
提问于 2025-04-15 11:29

我正在解析一个源代码文件,想要去掉所有的行注释(也就是以“//”开头的部分)和多行注释(也就是以“/*”开头,以“*/”结尾的部分)。不过,如果多行注释里面有至少一行换行符(\n),我希望输出的结果只保留一个换行符。

比如,下面这段代码:

qwe /* 123
456 
789 */ asd

应该变成:

qwe
asd

而不是“qweasd”或者:

qwe

asd

那么,最好的方法是什么呢?谢谢!


补充说明:这是测试用的示例代码:

comments_test = "hello // comment\n"+\
                "line 2 /* a comment */\n"+\
                "line 3 /* a comment*/ /*comment*/\n"+\
                "line 4 /* a comment\n"+\
                "continuation of a comment*/ line 5\n"+\
                "/* comment */line 6\n"+\
                "line 7 /*********\n"+\
                "********************\n"+\
                "**************/\n"+\
                "line ?? /*********\n"+\
                "********************\n"+\
                "********************\n"+\
                "********************\n"+\
                "********************\n"+\
                "**************/\n"+\
                "line ??"

期望的结果是:

hello 
line 2 
line 3  
line 4
line 5
line 6
line 7
line ??
line ??

5 个回答

1

这样说吧:

re.sub(r'\s*/\*(.|\n)*?\*/\s*', '\n', s, re.DOTALL).strip()

这个代码会处理开头的空格、/*,然后是任何文本和换行,直到遇到第一个*\为止,之后的空格也会被处理。

这个方法稍微改动了sykora的例子,而且在内部处理时也不会贪心。你可能还想了解一下多行选项。

5

你甚至需要问这个问题,说明正则表达式(REs)并不是解决这个问题的最佳选择,这一点应该很明显。给出的解决方案也不是特别容易理解,哈哈。

从可读性的角度来看,写一个相对简单的解析器会更好。

很多时候,人们试图用正则表达式来显得“聪明”(我不是在贬低这个想法),觉得一行代码很优雅,但最后得到的只是难以维护的一堆字符。我宁愿要一个有详细注释的20行代码,这样我一眼就能看懂。

12
  • (^)? 这个表达式会匹配如果评论是从一行的开头开始的,前提是使用了 MULTILINE 标志。
  • [^\S\n] 这个表达式会匹配任何空白字符,但不包括换行符。我们不想在评论单独占一行时匹配到换行。
  • /\*(.*?)\*/ 这个表达式会匹配多行评论并捕获内容。它是懒惰匹配,所以不会匹配到两个或多个评论。DOTALL 标志让 . 可以匹配换行符。
  • //[^\n] 这个表达式会匹配单行评论。不能使用 . 是因为有 DOTALL 标志。
  • ($)? 这个表达式会匹配如果评论在一行的结尾停止,前提是使用了 MULTILINE 标志。
comment_re = re.compile(
    r'(^)?[^\S\n]*/(?:\*(.*?)\*/[^\S\n]*|/[^\n]*)($)?',
    re.DOTALL | re.MULTILINE
)

def comment_replacer(match):
    start,mid,end = match.group(1,2,3)
    if mid is None:
        # single line comment
        return ''
    elif start is not None or end is not None:
        # multi line comment at start or end of a line
        return ''
    elif '\n' in mid:
        # multi line comment with line break
        return '\n'
    else:
        # multi line comment without line break
        return ' '

def remove_comments(text):
    return comment_re.sub(comment_replacer, text)

例子:

>>> s = ("qwe /* 123\n"
         "456\n"
         "789 */ asd /* 123 */ zxc\n"
         "rty // fgh\n")
>>> print '"' + '"\n"'.join(
...     remove_comments(s).splitlines()
... ) + '"'
"qwe"
"asd zxc"
"rty"
>>> comments_test = ("hello // comment\n"
...                  "line 2 /* a comment */\n"
...                  "line 3 /* a comment*/ /*comment*/\n"
...                  "line 4 /* a comment\n"
...                  "continuation of a comment*/ line 5\n"
...                  "/* comment */line 6\n"
...                  "line 7 /*********\n"
...                  "********************\n"
...                  "**************/\n"
...                  "line ?? /*********\n"
...                  "********************\n"
...                  "********************\n"
...                  "********************\n"
...                  "********************\n"
...                  "**************/\n")
>>> print '"' + '"\n"'.join(
...     remove_comments(comments_test).splitlines()
... ) + '"'
"hello"
"line 2"
"line 3 "
"line 4"
"line 5"
"line 6"
"line 7"
"line ??"
"line ??"

编辑:

  • 更新为新的规范。
  • 添加了另一个例子。

撰写回答