match nth occurrence (exclude the last occurrence)

Viewed 413

I have a question about regex. I don't know why I cannot do the following.

Sample sentence:

"This is a test string with five t's"

The regex I use:

^(.*?(?=t)){3}

I want the regex to match the following.

"This is a test s"

But it doesn't work, does anyone know why?

2 Answers

The point here is that the whole .*?(?=t) group pattern can match an empty string. It stops before the first t and cannot "hop thru" because it remains where it is when the lookahead pattern (a non-consuming pattern) matches.

You cannot do it like this, you must consume (and move the regex index) at least one char.

An alternative solution for this concrete case is

^(?:[^t]*t){2}[^t]*

See the regex demo, the ^(?:[^t]*t){2}[^t]* matches the start of string (^), then consumes two occurrences ({2}) of any chars other than t ([^t]*) followed with t, and then again consumes two occurrences ({2}) of any chars other than t.

Or, a general case solution (if t is a multicharacter string):

^(?:.*?t){2}(?:(?!t).)*

See another regex demo. The (?:.*?t){2} pattern matches two occurrences of any 0+ chars, as few as possible, up to the first t, and then (?:(?!t).)* matches any char, 0+ occurrences, that does not start a t char sequence.

As said by @CertainPerformance, .* will match zero or more characters in the pattern, but you use its lazy version .*?. The lazy version of a quantifier will have it match as few characters as possible. With a quantifier that matches the empty string, this will always lead to a zero-length match.

You need to use the + quantifier instead`, in order to prevent an empty string match.

Demonstration with Python:

>>> import re
>>> s = "This is a test string with five t's"
>>> r = r'^(.+?(?=t)){3}'
>>> re.match(r, s)
<_sre.SRE_Match object; span=(0, 16), match='This is a test s'>
Related