Regex Tester: Writing, Debugging and Trusting Regular Expressions
Table of Contents
The Building Blocks
Character classes describe a set. Square brackets match any one character inside them, so [aeiou] matches a vowel. A leading caret negates the set. The shorthands \d, \w and \s cover digits, word characters and whitespace, and their uppercase forms mean the opposite.
Quantifiers say how many. A star means zero or more, a plus one or more, a question mark zero or one, and braces an explicit count such as {2,4}. They apply to whatever immediately precedes them.
Anchors match positions, not characters. The caret is the start of input, the dollar the end, and \b a word boundary. They consume nothing, which is why a pattern of anchors alone produces empty matches.
Escaping matters. Characters such as dot, plus, star, question mark, parentheses and brackets carry meaning and need a backslash to be taken literally. A dot inside a character class is already literal.
What Each Flag Changes
g, global, continues past the first match. Without it you get one result no matter how many exist, which surprises people constantly.
i, ignore case, makes letter comparisons case insensitive throughout the pattern.
m, multiline, changes the caret and dollar to match at every line boundary rather than only at the start and end of the whole string.
s, dot all, lets a dot match newlines. Useful for spanning lines, dangerous when combined with a greedy quantifier.
u and y. Unicode makes escapes and code points above the basic plane behave correctly, and is needed for \p property escapes. Sticky forces matching to start exactly at the current index, which matters when writing tokenisers.
Capture Groups and Back References
Parentheses capture. Each pair records what it matched, numbered from left to right by opening bracket. The match table above lists every group for every match, which is usually where a misbehaving pattern gives itself away.
Named groups read better. Writing (?<year>\d{4}) lets you refer to the result by name instead of counting brackets, which survives later edits far more gracefully.
Non capturing groups group without recording. Use (?:...) when you only need alternation or a quantifier over several characters. It keeps your group numbers meaningful and is slightly faster.
Back references match what a group already matched. The duplicate word pattern in the library uses \1 to find a repeated word, which is not something plain string searching can do.
Greedy Against Lazy Quantifiers
Quantifiers are greedy by default. They take as much as possible and give back only when forced. Against <b>one</b> and <b>two</b>, the pattern <b>.*</b> swallows the entire line rather than stopping at the first closing tag.
Adding a question mark makes them lazy. .*? takes as little as possible, so the same pattern now matches each bold section separately. This one character fixes a large share of regex bugs.
A negated class is often better than either. Instead of .*?between delimiters, match everything that is not the delimiter, such as [^<]*. It expresses the intent more precisely and cannot backtrack.
Catastrophic Backtracking
The engine tries alternatives until one fits. Usually that is fast. With nested quantifiers over overlapping sets, the number of paths explodes exponentially with input length.
The classic example is (a+)+b tested against a long run of the letter a with no b. Around thirty characters it can take minutes, and each extra character doubles the work.
This is a real denial of service class. Known as ReDoS, it lets an attacker freeze a server thread with one crafted request. Any regex applied to user input deserves a look for nested quantifiers.
The tool stops after one second and tells you, so you find out here rather than in production. If you hit that warning, rewrite before shipping.
Replacing with Group References
Dollar signs reference groups in the replacement. $1 inserts the first capture, $<name> a named one, and $& the whole match. A literal dollar is written $$.
Reordering is the classic use. Matching a date as (\d{4})-(\d{2})-(\d{2}) and replacing with $3/$2/$1converts every ISO date in a document in one pass.
Remember the global flag. Without it, replace changes only the first match. The preview above uses your current flags, so you see exactly what your code will do.
When Not to Use a Regex
Nested structures. HTML, XML and JSON nest to arbitrary depth, and regular expressions cannot count depth. Use a parser. Extracting one attribute from known-shape markup is fine; walking a document is not.
Email validation. The fully correct pattern runs to thousands of characters and still accepts addresses that do not exist. Check for an at sign and a dot, then send a confirmation message, which is the only real test.
Simple string operations. If includes, startsWithor split does the job, use them. They are clearer to the next reader and faster.
Anything you cannot explain next month. A dense pattern is write once, read never. Break it up, name your groups, and leave a comment with an example of what it matches.
Frequently Asked Questions
Before shipping a pattern: Any regular expression applied to user input should be checked for nested quantifiers. A pattern that is merely slow here can hang a server thread in production.