Explain Codes LogoExplain Codes Logo

How to make Regular expression into non-greedy?

javascript
regex
non-greedy
regular-expressions
Alex KataevbyAlex Kataev·Jan 12, 2025
TLDR

Simply turn a greedy regEx into a non-greedy one by adding a ? after quantifiers like * or +. This ensures the engine will find the shortest match.

Example:

  • Greedy: /.*/
  • Non-greedy: /.*?/

Code snippet:

let str = "The early bird catches the worm."; let match = str.match(/.*? catches/); // The bird brags after this console.log(match[0]); // "The early bird catches"

Journey from greedy to non-greedy

When working with regular expressions, the difference between greedy and non-greedy (or lazy) matches are of significant importance. A greedy match aims to consume as much of the input as possible, while a non-greedy match is designed to do the opposite.

Non-greedy regex: The superheroes of extraction

Non-greedy expressions come into their own when parsing through text containing special character blocks or frequent separators. Such quantifiers tell the match to stop at the first instance of the next valid pattern. This efficiently prevents overmatching and ensures the extraction of multiple results from a single string.

When to lean on non-greedy expressions

  • Parsing HTML/XML tags, where nesting is common
  • Extracting content within parentheses or brackets
  • Data where start and end delimiters are repeated characters

Easing the job: Negated character classes and possessive quantifiers

In addition to transforming regex to non-greedy with *?, you can use negated character classes (e.g., [^)]* matches everything but a closing parenthesis) and possessive quantifiers (e.g, *+), which discourage backtracking, thus increasing efficiency.

The "g" in regex: searching globally

Utilize the global (g) flag to ensure you find all non-greedy matches, not just the first one:

let str = "Find the (first) and (second) pair of brackets."; let matches = [...str.matchAll(/\(.*?\)/g)]; // Finding Nemo has nothing on finding brackets! console.log(matches.map(m => m[0])); // ["(first)", "(second)"]

Nudging with "{n,m}" construct in regex

Need more control? Modify {n,m} with ? for specialised non-greedy matching:

let str = "123456789"; let greedyMatch = str.match(/\d{2,5}/); let nonGreedyMatch = str.match(/\d{2,5}?/); // The humble version console.log(greedyMatch[0]); // "12345" console.log(nonGreedyMatch[0]); // "12"

Here, the greedy \d{2,5} attempts to match2 to 5 digits max, whereas the non-greedy \d{2,5}? is content with the smallest valid match, i.e., 2 digits.

Leaning on Possessive Quantifiers

For matching scenarios where you don't want any backtracking, possessive quantifiers like ++ or *+ come to the rescue:

let str = "12345!"; let greedyMatch = str.match(/\d*!/); // Some digits have a blast! let possessiveMatch = str.match(/\d*+!/); // All digits partied too hard console.log(greedyMatch[0]); // "12345!" console.log(possessiveMatch); // null

The possessive \d*+! fails to match because it's too possessive and won't let go of the digits to match !.