非捕获组中的正则表达式捕获组

4
在Python中,如何在非捕获组内捕获一个组?换句话说,如何重复包含捕获组的非捕获子模式?
一个例子是在导入字符串中捕获所有的软件包名称。例如以下字符串:
``` import pandas, os, sys ```
将返回'pandas'、'os'和'sys'。下面的模式捕获第一个包并一直到第二个包:
import\s+([a-zA-Z0=9]*),*\s*

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

它不再捕获组内的内容。


如果您想要该功能,请使用PyPi正则表达式模块。 - Wiktor Stribiżew
1
问题不在于捕获组和非捕获组,而是尝试获取未设置的变量数量以供进一步使用。使用*来捕获组几乎永远不会产生您要查找的结果。这通常不是正则表达式的用途。相反,理性的做法是获取整个导入包集,然后通过,\s*(?=\w)或类似方式拆分字符串。 - Andris Leduskrasts
这个回答解决了你的问题吗?如何重复一个捕获组一次或多次并提取匹配项 - outis
3个回答

1
你的问题严格涉及正则表达式,但如果你愿意使用递归下降解析器(例如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

比正则表达式更直观。


0

重复捕获组将捕获最后一次迭代。这就是为什么您需要重新构造您的正则表达式以与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

在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', '')]

如果您不需要它们,可以删除另外两个捕获组。


0
你可以使用你的正则表达式import\s+(?:([a-zA-Z0-9=]+),*\s*)*(我刚刚修复了0-9范围以匹配任何数字,并包含=到结尾),并使用PyPi regex模块访问第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']]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接