Python正则表达式:捕获前瞻值(捕获文本而不消耗它)

4
我希望使用正则表达式将单词分成 (元音字母、非元音字母、更多元音字母) 三组,并使用标记确保每个单词都以元音字母开头和结尾。
import re

MARKER = "~"
VOWELS = {"a", "e", "i", "o", "u", MARKER}

word = "dog"

if word[0] not in VOWELS:
    word = MARKER+word

if word[-1] not in VOWELS:
    word += MARKER

re.findall("([%]+)([^%]+)([%]+)".replace("%", "".join(VOWELS)), word)

在这个例子中,我们得到了:
[('~', 'd', 'o')]

问题在于我希望匹配重叠- 最后一组元音应成为下一个匹配的第一组。如果我们使用前瞻替换正则表达式,则这似乎是可能的,如下所示:
re.findall("([%]+)([^%]+)(?=[%]+)".replace("%", "".join(VOWELS)), word)

我们得到:
[('~', 'd'), ('o', 'g')]

这意味着我们正在匹配我想要的内容。然而,它现在没有返回最后一组元音字母。我想要的输出结果是:
[('~', 'd', 'o'), ('o', 'g', '~')]

我认为这应该是可能的(如果正则表达式可以检查第二组元音,我认为它也可以返回它们),但我找不到除了 brute force 方法以外的任何方法来实现它,即在获得结果后循环遍历并将下一个匹配的第一个字符追加到最后一个匹配中,并将字符串的最后一个字符追加到最后一个匹配中。是否存在更好的方法来实现此目标?
有两种方法可以解决这个问题:一种是捕获 lookahead 值,另一种是在匹配时不消耗文本,同时捕获值 - 但我找不到任何方法来实现这两种方法。

我被踩了一下,有什么特别的原因吗? - Gareth Latty
2个回答

10

我刚发布完就找到了它:

re.findall("([%]+)([^%]+)(?=([%]+))".replace("%", "".join(VOWELS)), word)

在 lookahead 里面添加额外的一对括号意味着它本身变成了一个捕获组。

我发现这个很难找到 - 我不确定是不是其他人都觉得这很明显,但希望像我这样的其他人将来能更容易地找到这个信息。


1
顺便说一下,这是查找重叠匹配的标准方式。 - tchrist
是的, (?...) 括号不再捕获。这就是为什么存在 (?:) 的原因:这样你就可以明确地避免捕获仍然需要分组的内容。在你的情况下,你仍然需要捕获它,所以你只需明确添加捕获括号即可。 - Karl Knechtel
这只是显示了我对正则表达式知识的缺乏。学到新东西总是好的。 - Gareth Latty
2
@Lattyware:+1 谢谢,这对我很有帮助。我不知道在前瞻中可以使用 () 并进行捕获。 - RanRag

3
我不会尝试让正则表达式引擎来做这件事;我会将字符串分成辅音和元音块,然后生成重叠的结果。这样做的好处是,你实际上不需要在标记中进行修改,假设你可以接受在单词并不真正以元音开始或结束时,''作为“元音”部分。
def overlapping_matches(word):
    pieces = re.split('([^aeiou]+)', word)
    # There are other ways to do this; I'm kinda showing off
    return zip(pieces[:-2], pieces[1:-1], pieces[2:])[::2]

overlapping_matches('dog') # [('', 'd', 'o'), ('o', 'g', '')]

(如果 word 仅包含元音,这仍然失败,但如果必要,可以轻松纠正。)

+1。这是一个不错的解决方案,但我觉得它没有我找到的那个方案易读。 - Gareth Latty

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