【问题标题】:Regex capturing group within non-capturing group非捕获组中的正则表达式捕获组
【发布时间】:2016-09-09 17:32:13
【问题描述】:

在 Python 中,如何在非捕获组中捕获组?换句话说,如何重复包含捕获组的非捕获子模式?

这方面的一个例子是捕获导入字符串上的所有包名称。例如。字符串:

导入熊猫、操作系统、系统

将返回“pandas”、“os”和“sys”。以下模式捕获第一个包并到达第二个包:

import\s+([a-zA-Z0=9]*),*\s*

从这里开始,我想重复捕获组并匹配以下字符的子模式,即([a-zA-Z0=9]*),*\s*。当我用一个非捕获组包围这个子模式并重复它时:

import\s+(?:([a-zA-Z0=9]*),*\s*)*

它不再捕获内部的组。

【问题讨论】:

  • 如果您想要该功能,请使用 PyPi 正则表达式模块。
  • 问题不在于捕获组和非捕获组,问题在于尝试获取 unset 数量的变量以供进一步使用,使用 * 捕获组几乎不会产生您正在寻找的结果。这不是正则表达式通常用于的东西。相反,合理的做法是获取整个导入包集,然后将字符串拆分为 ,\s*(?=\w) 或类似的东西。

标签: python regex


【解决方案1】:

您的问题是关于正则表达式的严格表述,但如果您愿意使用recursive descent parser(例如pyparsing),许多需要正则表达式专业知识的事情都会变得非常简单。

例如,你要问的变成了

from pyparsing import *

p = Suppress(Literal('import')) + commaSeparatedList

>>> p.parseString('import pandas, os, sys').asList()
['pandas', 'os', 'sys']

>>> p.parseString('import                    pandas,             os').asList()
['pandas', 'os']

这可能是个人喜好问题,但对我来说,

Suppress(Literal('import')) + commaSeparatedList

也比正则表达式更直观。

【讨论】:

    【解决方案2】:

    重复捕获组将捕获最后一次迭代。这就是为什么您需要重组您的正则表达式以使用 re.findall

    \s*
    (?:
      (?:^from\s+
        (  # Base (from (base) import ...)
          (?:[a-zA-Z_][a-zA-Z_0-9]*  # Variable name
            (?:\.[a-zA-Z_][a-zA-Z_0-9]*)*  # Attribute (.attr)
          )
        )\s+import\s+
      )
    |
      (?:^import\s|,)\s*
    )
    (  # Name of imported module (import (this))
      (?:[a-zA-Z_][a-zA-Z_0-9]*  # Variable name
        (?:\.[a-zA-Z_][a-zA-Z_0-9]*)*  # Attribute (.attr)
      )
    )
    (?:
      \s+as\s+
      (  # Variable module is imported into (import foo as bar)
        (?:[a-zA-Z_][a-zA-Z_0-9]*  # Variable name
          (?:\.[a-zA-Z_][a-zA-Z_0-9]*)*  # Attribute (.attr)
        )
      )
    )?
    \s*
    (?=,|$)  # Ensure there is another thing being imported or it is the end of string
    

    Try it on regex101.com

    捕获组 0 将是 Base,捕获组 1 将是(您所追求的)导入模块的名称,捕获组 2 将是模块所在的变量 (from (group 0) import (group 1) as (group 2))

    import re
    
    regex = r"\s*(?:(?:^from\s+((?:[a-zA-Z_][a-zA-Z_0-9]*(?:\.[a-zA-Z_][a-zA-Z_0-9]*)*))\s+import\s+)|(?:^import\s|,)\s*)((?:[a-zA-Z_][a-zA-Z_0-9]*(?:\.[a-zA-Z_][a-zA-Z_0-9]*)*))(?:\s+as\s+((?:[a-zA-Z_][a-zA-Z_0-9]*(?:\.[a-zA-Z_][a-zA-Z_0-9]*)*)))?\s*(?=,|$)"
    
    print(re.findall(regex, "import pandas, os, sys"))
    
    [('', 'pandas', ''), ('', 'os', ''), ('', 'sys', '')]
    

    如果你不关心其他两个捕获组,你可以删除它们。

    【讨论】:

      【解决方案3】:

      您可以使用您的import\s+(?:([a-zA-Z0-9=]+),*\s*)* 正则表达式(我刚刚修复了0-9 范围以匹配任何数字并将= 包含在末尾)并使用PyPi regex module 访问第1 组捕获堆栈:

      >>> import regex
      >>> s = 'import pandas, os, sys'
      >>> rx = regex.compile(r'^import\s+(?:([a-zA-Z0-9=]+),*\s*)*$')
      >>> print([x.captures(1) for x in rx.finditer(s)])
      [['pandas', 'os', 'sys']]
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-24
        • 2014-08-17
        • 2019-01-12
        相关资源
        最近更新 更多