我有一个正则表达式
def parse(self, format_string):
for m in re.finditer(
r"""(?: \$ \( ( [^)]+ ) \) ) # the field access specifier
| (
(?:
\n | . (?= \$ \( ) # any one single character before the '$('
)
| (?:
\n | . (?! \$ \( ) # any one single character, except the one before the '$('
)*
)""",
format_string,
re.VERBOSE):
...
我想用一些自定义速记“常量”替换所有重复序列(\$ \(
),如下所示:
def parse(self, format_string):
re.<something>('\BEGIN = \$\(')
for m in re.finditer(
r"""(?: \BEGIN ( [^)]+ ) \) ) # the field access specifier
| (
(?:
\n | . (?= \BEGIN ) # any one single character before the '$('
)
| (?:
\n | . (?! \BEGIN ) # any one single character, except the one before the '$('
)*
)""",
format_string,
re.VERBOSE):
...
有没有办法用正则表达式本身(即不使用Python的字符串格式替换\BEGIN
\$\(
)?
澄清: Python源代码纯粹是为了上下文和插图。我正在寻找RE解决方案,它可以在一些RE方言中使用(可能不是Python的方言),而不是专门针对Python的解决方案。
答案 0 :(得分:9)
我不认为这在Python的正则表达式中是可行的。您需要递归(或者说模式重用),这只是PCRE支持的。实际上,PCRE甚至提到了如何定义短语在man page中的作用(搜索“定义子模式”)。
在PCRE中,您可以以类似于反向引用的方式使用递归语法 - 除了再次应用模式,而不是尝试从反向引用获取相同的文本文本。例如:
/(\d\d)-(?1)-(?1)/
匹配类似日期的内容((?1)
将替换为\d\d
并再次评估)。这非常强大,因为如果你在引用的组本身中使用这个构造,你会得到递归 - 但我们甚至不需要这里。以上也适用于命名组:
/(?<my>\d\d)-(?&my)-(?&my)/
现在我们已经非常接近,但这个定义也是该模式的第一次使用,这有点混淆了表达式。诀窍是首先在永不评估的位置使用模式。手册页建议一个条件依赖于(不存在的)组DEFINE
:
/
(?(DEFINE)
(?<my>\d\d)
)
(?&my)-(?&my)-(?&my)
/x
如果之前使用了组(?(group)true|false)
,则构造true
应用模式group
,否则使用(可选)模式false
。由于没有组DEFINE
,条件将始终为false,并且将跳过true
模式。因此,我们可以在那里放置各种定义,而不用担心它们会被应用并弄乱我们的结果。通过这种方式,我们可以将它们放入模式中,而无需真正使用它们。
替代方案是一种消极的前瞻,它永远不会达到定义表达式的程度:
/
(?!
(?!) # fail - this makes the surrounding lookahead pass unconditionally
# the engine never gets here; now we can write down our definitions
(?<my>\d\d)
)
(?&my)-(?&my)-(?&my)
/x
但是,你真的只需要这个表单,如果你没有条件,但确实有命名模式重用(我不认为这样的味道存在)。另一个变体的优点是,使用DEFINE
可以明确表示该组的用途,而前瞻性方法有点混淆。
回到原来的例子:
/
# Definitions
(?(DEFINE)
(?<BEGIN>[$][(])
)
# And now your pattern
(?: (?&BEGIN) ( [^)]+ ) \) ) # the field access specifier
|
(
(?: # any one single character before the '$('
\n | . (?= (?&BEGIN) )
)
|
(?: # any one single character, except the one before the '$('
\n | . (?! (?&BEGIN) )
)*
)
/x
这种方法有两个主要的注意事项:
(?<myPattern>a(b)c)
之类的内容并重复使用它,则永远不会捕获b
- 重复使用模式时,所有组都不会被捕获。与任何类型的插值或连接相比,最重要的优点是,您永远不会使用此方法生成无效模式,并且您也不会弄乱您的捕获组计数。