To rephrase, by strictly between one START and one END you mean that no other START or END can intervene in the match? In that case, and if your regex engine allows lookaheads, you consume characters as long as they are not START or END. Your pattern START(?!.START?)(.)END
is on the right track, but you need repetition over the lookahead and the dot:
START((?:(?!START|END).)*)END
For instance, using Python:
>>> import re
>>> s = 'END START START bad result START good result 1 END START good result 2 END START START'
>>> re.findall(r'START((?:(?!START|END).)*)END', s)
[' good result 1 ', ' good result 2 ']
The *
cannot be directly on the .
(i.e., .*
) because then the lookahead would only check the first .
and it would consume to the end of the string, then backtrack to the final END. Also you cannot put it outside of the capturing group (i.e., ((?!START|END).)*
), because then only the last character would be captured. Therefore the repetition happens on the non-capturing group, and the whole thing is inside a capturing group.
If you want to get rid of the spaces after START or before END, add them outside the group: START ((?:(?!START|END).)*) END