What a regular expression is
A regular expression, or regex, is a short pattern that describes a set of strings. You use it to find text (every phone number in a document), to check that text has a certain shape (does this look like a date?), and to change text (swap first and last names). Almost every programming language and many editors support regexes, with a very similar core syntax and a handful of differences, which are covered below.
Most characters match themselves, so cat finds the letters c, a, t in a row. Special characters add power: the pattern ^\d{3}-\d{4}$ reads, part by part, as “^” (the start of the text (the start of each line with the m flag)), “\d” (any digit, 0 to 9), “{3}” (repeat the item before it: exactly 3 times, taking as many as possible (greedy)), “-” (the character "-"), “\d” (any digit, 0 to 9), “{4}” (repeat the item before it: exactly 4 times, taking as many as possible (greedy)), “$” (the end of the text (the end of each line with the m flag)). The regex tester on this site gives a reading like that for any pattern you type.
Regex testerPaste a pattern and some sample text into the regex tester to see every match highlighted, with its groups and position.The syntax in one table
| Pattern | Meaning |
|---|---|
| . | any character except a line break |
| \d \D | a digit / not a digit |
| \w \W | letter, digit or _ / anything else |
| \s \S | white space / not white space |
| [abc] [^abc] | one of a, b, c / any but those |
| [a-z0-9] | a range |
| \p{L} | any letter (needs the u flag) |
| Pattern | Meaning |
|---|---|
| ^ $ | start / end of the text (or line with m) |
| \b \B | a word edge / not a word edge |
| (?=x) (?!x) | followed by x / not followed by x |
| (?<=x) (?<!x) | preceded by x / not preceded by x |
| Pattern | Meaning |
|---|---|
| * + ? | 0 or more, 1 or more, 0 or 1 |
| {3} {3,} {3,5} | exactly 3, 3 or more, 3 to 5 |
| *? +? ?? | the same, but as few as possible |
| Pattern | Meaning |
|---|---|
| (x) | capture group, numbered from 1 |
| (?:x) | group without capturing |
| (?<name>x) | named group |
| \1 \k<name> | the same text again |
| a|b | a or b |
| Pattern | Meaning |
|---|---|
| $1 $2 | text of group 1, 2 |
| $<name> | text of a named group |
| $& | the whole match |
| $$ | a dollar sign |
To match a character that has a special meaning, put a backslash in front of it: \. is a literal full stop, \( a literal bracket and \\ a literal backslash. The characters that need escaping outside a class are . * + ? ^ $ { } ( ) | [ ] and \. Inside square brackets only ] \ ^ and - usually need it.
Flags change the whole search
- g (global): find every match instead of stopping at the first.
- i (ignore case): A matches a as well as A.
- m (multiline): ^ and $ match at the start and end of every line, not only of the whole text.
- s (dot all): the dot also matches a line break.
- u (unicode): read the pattern as Unicode, so an emoji is one character and \p{L} (any letter) works.
- y (sticky): match only at the exact position where the last match ended.
A common surprise is that without g a replace changes only the first match, and that without m a ^ in a multi-line pasted text matches just once. If a pattern finds fewer things than you expect, check the flags first.
Groups, back-references and replacements
Brackets group part of a pattern and remember what it matched, numbered from 1 by the position of the opening bracket. Naming a group, as in (?<last>\w+), makes the pattern easier to read. A back-reference such as \1 matches the same text again, which is how repeated word detection works: the pattern \b(\w+)\s+\1\b finds “is is” and “the the” in “This is is a test of the the system.”.
In a replacement, $1 is the first group and $<name> is a named one. Using the pattern (?<first>\w+)\s+(?<last>\w+) with the replacement $<last>, $<first> turns “Ada Lovelace” into “Lovelace, Ada”. Use (?:...) when you need grouping for a repeat or an alternative but do not want a numbered capture.
Lookaheads and lookbehinds
Lookarounds check what is next to the match without including it. (?=x) requires x to come next, (?!x) requires that it does not, and (?<=x) and (?<!x) do the same for what comes before. For example \d+(?= dollars) matches the number in “25 dollars” but not the word, and (?<=\$)\d+ matches the digits after a dollar sign. Lookbehinds are the part that differs most between languages, as the next section shows.
Tested patterns for common jobs
Each of these was run on its sample text by the tester on this site and found the matches shown. They are finders, not validators. A pattern can say that text has the shape of an email address, but only sending a message proves the address works.
| Job | Pattern | Found in the sample |
|---|---|---|
| Email address | /[\w.+-]+@[\w-]+(?:\.[\w-]+)+/g | sales@example.com, support+uk@mail.example.co.uk |
| US phone number | /\(?\b\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}\b/g | (415) 555-0132, 415.555.0188, 4155550199 |
| US ZIP code | /\b\d{5}(?:-\d{4})?\b/g | 94105, 10001-1234 |
| IPv4 address | /\b(?:(?:25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(?:25[0-5]|2[0-4]\d|1?\d?\d)\b/g | 192.168.0.1, 10.0.0.255, 8.8.8.8 |
| Date, yyyy-mm-dd | /\b\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])\b/g | 2026-10-20, 2027-01-05 |
| Time, 24-hour | /\b(?:[01]\d|2[0-3]):[0-5]\d\b/g | 06:30, 14:45, 22:00 |
| Hex colour | /#(?:[0-9a-fA-F]{3}){1,2}\b/g | #0e6b5a, #fff |
| Dollar amount | /\$\d{1,3}(?:,\d{3})*(?:\.\d{2})?/g | $1,250.00, $300, $12.50 |
The date pattern checks that the month is 01 to 12 and the day 01 to 31, but not that the day exists in that month, so 2026-02-31 still matches. Parsing a date properly is a job for a date library. The same applies to emails, URLs and HTML: use a parser when the input is untrusted and the result matters.
Python, Java, PHP, C# and Go: what changes
The core syntax is shared, which is why a JavaScript pattern often works elsewhere. The differences are where bugs hide.
| Feature | JavaScript | Python (re) | Java | PHP (PCRE) | C# (.NET) | Go (RE2) |
|---|---|---|---|---|---|---|
| Named group | (?<n>x) | (?P<n>x) | (?<n>x) | (?<n>x) or (?P<n>x) | (?<n>x) | (?P<n>x) and (?<n>x) in recent versions |
| Lookahead | Yes | Yes | Yes | Yes | Yes | No |
| Lookbehind | Any length | Fixed length | Bounded length | Fixed length (a short range in newer PCRE2) | Any length | No |
| Back-references | Yes | Yes | Yes | Yes | Yes | No |
| \d matches | 0 to 9 only | Any Unicode digit | 0 to 9 by default | 0 to 9 by default | Any Unicode digit | 0 to 9 |
| \p{L} | With the u flag | Not in re (use regex) | Yes | With the u modifier | Yes | Yes |
| $ at the end | The very end | Also before a final line break | Also before a final line terminator | Also before a final newline | Also before a final newline | The very end by default |
Take a pattern that works in JavaScript, such as (?<year>\d{4})-(?<month>\d{2}). In Python it must be written (?P<year>\d{4})-(?P<month>\d{2}). The tester’s Code tab makes that conversion and lists differences it can detect: for this pattern it flags 2 things to check. Go is the odd one out because RE2 guarantees linear-time matching by leaving out lookaround and back-references. That gives it safety from the problem described next, and it also means some patterns simply cannot be written.
When a pattern hangs: catastrophic backtracking
Some patterns take far longer on text that almost matches than on text that matches. The classic is (a+)+$ tried against a long run of a’s followed by an x: the engine tries every way to split the a’s between the inner and outer repeat, and the number of ways doubles with each extra letter. The program seems to hang, and this is the root of ReDoS, where an attacker sends text designed to freeze a server. The tester flags the shape (a repeated group that itself repeats: detected for (a+)+$, not detected for the safe a+$) and runs patterns in a background worker that is stopped after two seconds.
- Avoid a repeat inside a repeat, such as (x+)+, (x*)* or (\w+\s?)*.
- Be specific: [^,]* is faster and safer than .* when you mean “everything up to the next comma”.
- Anchor the pattern with ^ and $ where you can, so it cannot restart at every position.
- Put a limit on untrusted input, and use an engine such as Go’s RE2 when the pattern itself comes from a user.
Reading the common errors
| Message | Cause | Fix |
|---|---|---|
| Nothing to repeat | A * + ? or {} with nothing in front, often at the start | Put something before it, or escape it as \* or \+ |
| Unterminated group / Unmatched ) | A bracket opened and never closed, or closed too often | Count the brackets; escape a literal one as \( or \) |
| Unterminated character class | A [ with no ] | Add the ], or escape the bracket as \[ |
| Range out of order | [z-a] or {5,2} | Put the smaller value first |
| Invalid group | (? followed by something unsupported, such as (?P<name> or (?i) | Use (?<name>...) and the flag buttons |
| Invalid escape / property name | A backslash before a letter with the u flag, or a misspelt \p{...} | Remove the backslash or fix the property name |
Common questions
What does ^ do in a regex?
What is the difference between .* and .*? ?
How do I match a literal dot, bracket or dollar sign?
Why does my pattern work in JavaScript but not in Python?
Can I use a regex to validate an email address?
Is it safe to paste text into an online regex tester?
Sources
Reviewed on 20 Oct 2026 against the published sources below. Laws and prices change, so check the current position before you rely on a figure.
Try the calculator
Keep reading
This guide is general information, not legal, financial or religious advice. Figures are estimates worked out from the sources above and may differ from what applies to you. See the disclaimer. Found a mistake? Tell us.