flashman
← All guides

Regex empty matches and safe global iteration

Build terminating regex loops by understanding zero-width assertions, empty alternatives, runtime iteration rules, Unicode offsets, replacements, and resource limits.

2026-09-12 · 8 min read

  • regex
  • text-processing
  • reliability

A regular expression can succeed without consuming input. Anchors identify boundaries, lookarounds test surrounding text, and optional or empty alternatives can produce a match whose start and end offsets are equal.

Zero-width matches are useful for insertion, splitting, and validation. They become hazardous when a custom global loop assumes every success advances its cursor or when replacement code applies a second incompatible advancement rule.

Audit every path that can match empty

Minimize the pattern and test empty input plus each position in a small public string. Record match text, captures, start, end, flags, previous cursor, next cursor, and runtime version.

  • Review star and question quantifiers.
  • Inspect empty alternatives and optional groups.
  • Include anchors, boundaries, and lookarounds.
  • Treat start equal to end as a valid diagnostic case.

Use documented iteration semantics

Prefer the runtime's standard match iterator or global replacement API when it defines progress after an empty match. If custom iteration is required, advance exactly once after zero consumption while preserving legitimate matches at neighboring positions.

Porting requires new tests because engines differ in whether another empty match is allowed immediately after a non-empty match and how convenience APIs handle adjacent positions.

Define the Unicode offset model

Offsets may count UTF-8 bytes, UTF-16 code units, Unicode code points, or language-specific string indices. Advancing one unit under the wrong model can split an encoded character, skip input, or pass an invalid offset to another API.

  • Keep offsets in one model inside the loop.
  • Convert only at explicit boundaries.
  • Test supplementary characters and combining sequences.
  • Do not promise grapheme-aware behavior from a code-point iterator.

Bound and compare production behavior

Use Flashman's regex tester for public patterns, diff for engine result lists, number-base converter for code-unit fixtures, case converter for text variants, and units converter for documented input limits. Browser behavior is one engine, not a universal regex specification.

Test empty strings, all empty-capable branches, beginning and end boundaries, multiline mode, global and sticky operation, replacements, split, supplementary characters, combining marks, malformed encodings, pattern and input size limits, iteration caps, timeouts, and every server and client runtime in the supported matrix.

Try these tools