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.