regex
multiline text
pattern matching
text processing
regular expressions

Regular expression matching a multiline block of text

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

Matching a block of text across multiple lines usually comes down to one technical detail: by default, the dot character in many regex engines does not match newline characters. The fix is typically to enable dot-all behavior or to use an explicit character class that includes newlines, then make the match non-greedy so it stops at the right boundary.

Know the Difference Between Multiline and Dot-All

People often confuse two regex modes:

  • multiline mode changes how ^ and $ behave
  • dot-all mode changes whether . matches newline characters

If your goal is "match from start marker to end marker across several lines", dot-all is usually the mode that matters most.

In Python:

python
1import re
2
3text = """BEGIN
4line one
5line two
6END
7"""
8
9match = re.search(r"BEGIN.*END", text, flags=re.DOTALL)
10print(match.group(0))

Without re.DOTALL, the .* would stop at the first newline and fail to span the whole block.

Use Non-Greedy Quantifiers for Delimited Blocks

Greedy matching often grabs too much when multiple blocks exist. A non-greedy quantifier is usually safer.

python
1import re
2
3text = """BEGIN
4first block
5END
6BEGIN
7second block
8END
9"""
10
11blocks = re.findall(r"BEGIN.*?END", text, flags=re.DOTALL)
12print(blocks)

The .*? tells the regex engine to stop at the nearest possible END rather than the last one in the whole string.

That is often the difference between a usable multiline regex and one that silently overmatches.

Match with an Explicit Newline-Safe Class

If your regex flavor does not have a dot-all flag, or you want the pattern itself to be explicit, use a class such as [ \s\S] or [ \d\D] depending on the engine. In Python, a common explicit version is [ \s\S], though re.DOTALL is cleaner when available.

python
1import re
2
3text = """BEGIN
4alpha
5beta
6END
7"""
8
9match = re.search(r"BEGIN[\s\S]*?END", text)
10print(match.group(0))

This works because [ \s\S] matches any whitespace or non-whitespace character, which effectively means any character at all.

Match Lines Between Anchors

Sometimes the problem is not "any text until marker" but rather "a whole block starting at a line and ending at another line". In that case, anchors are useful, and multiline mode matters.

python
1import re
2
3text = """Header
4START
5line a
6line b
7STOP
8Footer
9"""
10
11match = re.search(r"^START$[\s\S]*?^STOP$", text, flags=re.MULTILINE)
12print(match.group(0))

Here re.MULTILINE allows ^ and $ to match line boundaries within the larger string, not just the start and end of the whole text.

Be Careful with Structured Data

Regex is fine for delimited plain text blocks, but it becomes fragile when the text is really structured markup such as nested JSON, XML, or programming language syntax. If nesting or escaping rules matter, a parser is usually the better tool.

Regex is strongest when the block shape is simple and bounded clearly by markers.

Performance and Backtracking

Large multiline patterns with overly greedy constructs can backtrack heavily. You can reduce that risk by:

  • using explicit start and end markers
  • making repetition non-greedy
  • narrowing the pattern where possible instead of matching "anything"

For many practical tasks, the best regex is not the cleverest one. It is the one with the clearest stopping condition.

Common Pitfalls

  • Using multiline mode and expecting it to make . match newlines.
  • Forgetting to make the block match non-greedy when multiple blocks exist.
  • Writing .* without a clear terminating marker and overmatching huge sections of text.
  • Using regex for deeply nested structured formats where a parser is more reliable.
  • Ignoring engine-specific regex behavior and assuming all languages handle multiline flags the same way.

Summary

  • Matching multiline blocks usually requires dot-all behavior, not just multiline anchors.
  • Use non-greedy repetition such as .*? when you need the nearest closing marker.
  • 're.DOTALL is the cleanest Python solution for dot matching across newlines.'
  • 're.MULTILINE matters when you need ^ and $ to work per line.'
  • Prefer parsers over regex when the text has nested structure rather than simple delimiters.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.