Python Regular Expressions: Capture lookahead value (capturing text without consuming it)

python, python-3.x, regex, regex-lookarounds

Solution

I found it just after posting:

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

Adding an extra pair of brackets inside the lookahead means that it becomes a capture itself.

I found this pretty obscure and hard to find - I'm not sure if it's just everyone else found this obvious, but hopefully anyone else in my position will find this more easily in future.

Problem

I wish to use regular expressions to split words into groups of `(vowels, not_vowels, more_vowels)`, using a marker to ensure every word begins and ends with a vowel. ``` 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) ``` In this example we get: ``` [('~', 'd', 'o')] ``` The issue is that I wish the matches to overlap - the last set of vowels should become the first set of the next match. This appears possible with lookaheads, if we replace the regex as follows: ``` re.findall("([%]+)([^%]+)(?=[%]+)".replace("%", "".join(VOWELS)), word) ``` We get: ``` [('~', 'd'), ('o', 'g')] ``` Which means we are matching what I want. However, it now doesn't return the last set of vowels. The output I want is: ``` [('~', 'd', 'o'), ('o', 'g', '~')] ``` I feel this should be possible (if the regex can check for the second set of vowels, I see no reason it can't return them), but I can't find any way of doing it beyond the brute force method, looping through the results after I have them and appending the first character of the next match to the last match, and the last character of the string to the last match. Is there a better way in which I can do this? The two things that would work would be capturing the lookahead value, or not consuming the text on a match, while capturing the value - I can't find any way of doing either.

Original source