重用正则表达式的一部分

时间:2013-11-05 17:02:14

标签: python regex

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

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,而不是digit/letter/letter/digit

是否有任何方法可以保存模式的一部分,例如\d\w\w\d,因此可以在同一模式中使用后者?换句话说,我可以在模式中重用子模式吗?

6 个答案:

答案 0 :(得分:12)

不,使用标准库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-strings:

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

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

如果您准备安装外部库,regex project可以使用regex subroutine call解决此问题。语法(?<digit>)重用已使用(隐式编号)捕获组的模式:

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

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

最后,groupname支持regex块来“定义”子例程模式,而不会在该阶段实际匹配任何内容。您可以在该构造中放置多个(?(DEFINE)...)(..)捕获组,然后在实际模式中引用它们:

(?<name>...)

要明确:标准库(?(DEFINE)(?<dlld>\d\w\w\d))(?&dlld),(?&dlld) ^...............^ ^......^ ^......^ | \ / creates 'dlld' pattern uses 'dlld' pattern twice 模块不支持子例程模式。

答案 1 :(得分:5)

注意:这适用于PyPi regex module,而不适用于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)

答案 2 :(得分:0)

我对同样的问题感到困扰并写了this snippet

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")

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

访问它们就像{{a}}

一样简单

答案 3 :(得分: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))

答案 4 :(得分:0)

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

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

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

OR

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

re.match(f"{P},{P}", "1aw2,5cx7")

答案 5 :(得分:-1)

尝试使用反向引用,我相信它的工作原理如下,以匹配

1aw2,5cx7

您可以使用

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

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