Regex tester · Guide

Regex Match Between Two Strings: Extracting What Is Inside

Extracting the text between two markers is the most common capture task there is. Three approaches solve it with different trade-offs, and the popular one is usually the worst.

The three approaches

Regex
START(.*?)END          # lazy — shortest match, needs dotall for newlines
START([^E]*)END        # negated class — cannot cross the delimiter
(?<=START).*?(?=END)   # lookarounds — match excludes the markers

The lazy version is the most readable and the most portable. The negated class is fastest and only practical when the delimiter is a single character. Lookarounds keep the markers out of the match, which avoids needing a capture group at all.

Greedy versus lazy

This is the trap. START(.*)END on START a END START b END captures a END START b, because the greedy .* runs to the last END and backs off. START(.*?)END captures a, then b on the next match.

Whenever an extraction returns far more than expected, a greedy quantifier is almost always the reason.

Crossing newlines

The dot does not match newlines by default, so START(.*?)END fails when the content spans lines. Enable dotall (/s, re.DOTALL, RegexOptions.Singleline) or use [\s\S]*?, which works in every engine with no flag.

For quoted strings specifically, prefer the negated class: "([^"]*)" cannot run past the closing quote at all, which makes greediness irrelevant and the intent obvious.

When to stop

For a single delimiter pair in flat text, regex is the right tool. For nested markers — balanced parentheses, HTML tags, quoted strings with escapes — it is not: regular expressions cannot count nesting, and patterns that appear to work fail on the first nested case.

.NET balancing groups are the exception and are unreadable. For HTML use a parser; for JSON use a JSON parser; for anything recursive, write the loop.

Frequently asked questions

Why does my capture include everything up to the last delimiter?

The quantifier is greedy. Use .*? or a negated character class.

How do I match across multiple lines?

Enable dotall, or use [\s\S]*? which needs no flag.

Can regex handle nested delimiters?

Not in general. Regular expressions cannot count nesting; use a parser.

Ready to try it?

Open the free browser-based Regex tester and apply what you just read — no sign-up, runs locally.

Open the Regex tester tool