29

考虑这个(非常简化的)示例字符串:

1aw2,5cx7

如您所见,它是digit/letter/letter/digit用逗号分隔的两个值。

现在,我可以将其与以下内容相匹配:

>>> from re import match
>>> match("\d\w\w\d,\d\w\w\d", "1aw2,5cx7")
<_sre.SRE_Match object at 0x01749D40>
>>>

问题是,我必须写\d\w\w\d两次。对于小模式,这还不错,但是对于更复杂的正则表达式,两次编写完全相同的东西会使最终模式变得庞大且难以处理。它也似乎是多余的。

我尝试使用命名捕获组:

>>> from re import match
>>> match("(?P<id>\d\w\w\d),(?P=id)", "1aw2,5cx7")
>>>

但它不起作用,因为它正在寻找两次出现的1aw2, not digit/letter/letter/digit

有什么方法可以保存模式的一部分,例如\d\w\w\d, 以便以后可以在相同的模式中使用它?换句话说,我可以在模式中重用子模式吗?

4

6 回答 6

23

不,当使用标准库re模块时,正则表达式模式不能被“符号化”。

当然,您总是可以通过重用 Python 变量来做到这一点:

digit_letter_letter_digit = r'\d\w\w\d'

然后使用字符串格式化来构建更大的模式:

match(r"{0},{0}".format(digit_letter_letter_digit), inputtext)

或者,使用 Python 3.6+ f 字符串:

dlld = r'\d\w\w\d'
match(fr"{dlld},{dlld}", inputtext)

我经常使用这种技术从可重用的子模式中组合更大、更复杂的模式。

如果您准备安装外部库,那么该regex项目可以通过正则表达式子例程调用来解决此问题。该语法(?<digit>)重新使用已使用(隐式编号)捕获组的模式:

(\d\w\w\d),(?1)
^........^ ^..^
|           \
|             re-use pattern of capturing group 1  
\
  capturing group 1

您可以对命名的捕获组执行相同的操作,其中(?<groupname>...)是命名的 groupgroupname(?&groupname)(?P&groupname)或者(?P>groupname)重新使用匹配的模式groupname(后两种形式是与其他引擎兼容的替代方案)。

最后,regex支持(?(DEFINE)...)块来“定义”子程序模式,而它们在那个阶段实际上不匹配任何东西。您可以在该构造中放置多个(..)(?<name>...)捕获组,以便稍后在实际模式中引用它们:

(?(DEFINE)(?<dlld>\d\w\w\d))(?&dlld),(?&dlld)
          ^...............^ ^......^ ^......^
          |                    \       /          
 creates 'dlld' pattern      uses 'dlld' pattern twice

明确一点:标准库re模块不支持子例程模式。

于 2013-11-05T17:05:10.390 回答
8

注意:这将适用于PyPi 正则表达式模块,而不是re模块。

在您的情况下,您可以使用符号(?group-number)

(\d\w\w\d),(?1)

它相当于:

(\d\w\w\d),(\d\w\w\d)

请注意,\w包括\d. 正则表达式将是:

(\d[a-zA-Z]{2}\d),(?1)
于 2014-02-04T18:27:23.637 回答
0

我被同样的问题困扰并写了这个片段

import nre
my_regex=nre.from_string('''
a=\d\w\w\d
b={{a}},{{a}}
c=?P<id>{{a}}),(?P=id)
''')
my_regex["b"].match("1aw2,5cx7")

由于缺少更具描述性的名称,我将部分正则表达式命名为a,bc.

访问它们就像{{a}}

于 2017-10-25T10:58:59.447 回答
0
import re
digit_letter_letter_digit = re.compile("\d\w\w\d") # we compile pattern so that we can reuse it later
all_finds = re.findall(digit_letter_letter_digit, "1aw2,5cx7") # finditer instead of findall
for value in all_finds:
    print(re.match(digit_letter_letter_digit, value))
于 2018-07-23T19:09:42.430 回答
0

既然您已经在使用 re,为什么不使用字符串处理来管理模式重复:

pattern = "P,P".replace("P",r"\d\w\w\d")

re.match(pattern, "1aw2,5cx7")

或者

P = r"\d\w\w\d"

re.match(f"{P},{P}", "1aw2,5cx7")
于 2019-03-07T12:40:03.947 回答
-1

尝试使用反向引用,我相信它可以像下面这样匹配

1aw2,5cx7

你可以使用

(\d\w\w\d),\1

请参阅此处以获取参考http://www.regular-expressions.info/backref.html

于 2013-11-05T17:18:19.013 回答