【问题标题】:How to find multi-line comments wrapped in quotes?如何查找包含在引号中的多行注释?
【发布时间】:2020-07-25 08:34:02
【问题描述】:

我正在解析 Python 代码,我需要删除所有可能的 cmets/docstrings。我已经成功删除了表单的“cmets”:

#comment
"""comment""" 
'''comment''' 

但是,我发现了一些人们编写 cmets 形式的示例:

"'''comment'''" 
"\"\"\"\n comment  \"\"\""

我正在努力成功删除这些 cmets(三个单引号被双引号包围,双引号带有换行符)。我试过的表达是:

p = re.compile("([\'\"])\1\1(.*?)\1{3}", re.DOTALL)
code = p.sub('', code)

但这不适用于后两种情况。有人有什么建议吗?

【问题讨论】:

  • 两者都不像真正的 cmets
  • 我猜它们在技术上只是多行未分配的字符串,我只是称它们为 cmets,因为这是它们在这种情况下的功能(代码作者如何使用它们)
  • 如果你正在解析 python,那么你应该像 python 对待它们一样对待它们——就像你提到的那样,作为多行未分配的字符串。
  • 这可能会在函数或赋值中捕获三引号,这有时在带有换行符的字符串中很有用
  • 从未使用过这个,但标准库中的tokenize 可能会有所帮助。

标签: python regex string parsing


【解决方案1】:

您可以尝试使用strip(). 它通过删除您放在括号之间的字符来工作。如果括号中没有任何内容,它会删除空格,但您想删除由双引号包围的三个单引号,以及带换行符的双引号。举个例子:

txt = ",,,,,rrttgg.....banana....rrr"
x = txt.strip(",.grt")
print(x)

您将得到的输出是“香蕉”,因为它删除了双括号 (x = txt.strip(",.grt")) 之间的 ,.grt

有关更多信息,请查看此页面,我推荐底部的信息以获得更多帮助: https://www.w3schools.com/python/python_strings.asp

【讨论】:

  • 这样做会返回不带字符串引号的代码。所以“语句”'''comment''“语句”变成了“语句注释语句”。有没有办法使用 strip 来删除这些符号之间的所有内容?
  • 好的,你想从该行中删除什么:“statement”“comment”“statement”
【解决方案2】:

因为我的评论难以阅读而发帖作为答案

这是我想出的,它很丑陋,但它确实有效。

import re

txt = "if x = 4: continue  \"'''hi'''\"  print(x) "
print(txt)
#find everything wrapped in double quotes
double_quotes = re.findall(r"\"(.+?)\"", txt)
for string in double_quotes:
    triple_single = re.findall(r"\'''(.+?)\'''", string)[0]
    full_comment = '"'+"'''" +triple_single+"'''"+'"'
    txt = txt.replace(full_comment, '')
    print(txt)

打印:

if x = 4: continue  "'''hi'''"  print(x) 
if x = 4: continue    print(x)

【讨论】:

    【解决方案3】:

    未分配的字符串字面量可以被视为源代码抽象语法树 (AST) 表示上的节点。然后问题被简化为识别这些节点并在没有它们的情况下重写 AST,使用ast 模块中的工具。

    评论 (# ...) 不会被解析为 AST,因此无需为它们编写代码。

    未分配的字符串字面量是ast.Constant 类型的节点,并且构成具有主体的节点的body 属性的一部分,例如模块定义、函数定义和类定义。我们可以识别这些节点,将它们从其父节点的body 中删除,然后重写 AST。

    import ast 
    import io
    
    from unparse import Unparser
    
    
    with open('comments.py') as f:
        src = f.read()
    
    root = ast.parse(src)
    
    # print(ast.dump(root)) to see the ast structure.
    
    
    def filter_constants(node):
        if isinstance(node, ast.Expr):
            if isinstance(node.value, ast.Constant):
                if isinstance(node.value.value, str):
                    return None
        return node
    
    
    class CommentRemover(ast.NodeTransformer):
    
        def visit(self, node):
            if hasattr(node, 'body'):
                node.body = [n for n in node.body if filter_constants(n)]
            return super().visit(node)
    
    
    remover = CommentRemover()
    new = remover.visit(root)
    ast.fix_missing_locations(new)
    
    buf = io.StringIO()
    Unparser(new, buf)
    buf.seek(0)
    print(buf.read())
    

    在此代码上调用脚本 (cmets.py):

    """Module docstring."""                                                                                                             
    
    
    # A real comment                                                                                                                    
    """triple-double-quote comment"""                                                                                                   
    '''triple-single-quote comment'''                                                                                                   
    
    "'''weird comment'''"                                                                                                               
    "\"\"\"\n comment  \"\"\""                                                                                                          
    
    NOT_A_COMMENT = 'spam'                                                                                                              
    
    42                                                                                                                                  
    
    
    def foo():                                                                                                                          
        """Function docstring."""                                                                                                       
        # Function comment                                                                                                              
        bar = 'baz'                                                                                                                     
        return bar                                                                                                                      
    
    
    class Quux:
        """class docstring."""
    
        # class comment
    
        def m(self):
            """method comment"""
            return
    

    给出这个输出:

    NOT_A_COMMENT = 'spam'
    42
    
    def foo():
        bar = 'baz'
        return bar
    
    class Quux():
    
        def m(self):
            return
    

    注意事项:

    • 可以在 Python 发行版的 Tools/parser 文件夹中找到 unparse 脚本(在 v3.8 中 - 在以前的版本中,它位于 ToolsDemo 文件夹中)。它也可以从github 下载 - 请确保下载适用于您的 Python 版本的版本
    • 从 Python 3.8 开始,ast.Constant 类用于所有常量节点;对于早期版本,您可能需要酌情使用ast.Numast.Strast.Bytesast.NameConstantast.Ellipsis。所以在 filter_constants 中可能看起来像这样:

      def filter_constants(node):
          if isinstance(node, ast.Expr):
              if isinstance(node.value, ast.Str):
                  return None
          return node
      
    • 从 Python 3.9 开始,ast 模块提供了一个 unparse 函数,可以用来代替 unparse 脚本

      src = ast.unparse(new)
      print(src)
      

    【讨论】:

      猜你喜欢
      • 2013-04-10
      • 2022-01-23
      • 2012-01-15
      • 1970-01-01
      • 2015-05-02
      • 2014-12-11
      • 2016-03-05
      • 2014-09-18
      • 1970-01-01
      相关资源
      最近更新 更多