Regular Expression Flags Reference
Every regex flag, what it actually does, the syntax in JavaScript / Python / Go, and the common mistakes engineers make with each one.
Last reviewed:
Quick reference
| Flag | Name | Effect |
|---|---|---|
g | Global | Match all occurrences in the string, not just the first one. |
i | Case-Insensitive | Match letters regardless of upper/lower case. |
m | Multiline | Makes ^ and $ match the start and end of each line, not just the whole string. |
s | DotAll (single line) | Makes . match newline characters too, not just non-newline characters. |
u | Unicode | Treats the pattern as a sequence of Unicode code points, not bytes. Enables \u{...} escapes and \p{...} property matches. |
y | Sticky | Matches only at the lastIndex position of the regex; does not scan forward. |
d | hasIndices | Include match start/end indices for the full match and each capture group in the result. |
x | Extended / Verbose | Allow whitespace and # comments inside the pattern for readability. Not available in JavaScript. |
v | Unicode Sets | An upgraded version of /u (ES2024) that also supports set operations on character classes — union, intersection, and difference. |
U | Ungreedy | Swaps the default greediness of quantifiers: * and + become lazy by default, and adding ? after them makes them greedy again. |
A | Anchored | Forces the pattern to match only at the very start of the subject string (or immediately after the previous match), like an implicit ^. |
D | Dollar End Only | Makes $ match only at the very end of the string, never right before a trailing newline (which is $'s default PCRE behaviour). |
J | Duplicate Named Groups | Allows multiple capture groups in the same pattern to share the same name — normally a compile-time error. |
n | Explicit Capture | Only named groups (?<name>...) capture; plain (...) groups become non-capturing automatically. |
r | Right-to-Left | Scans the input from right to left instead of left to right — matters for lookaround direction and where scanning starts. |
a | ASCII-only | Restricts \w, \b, \s, \d (and case-insensitive matching) to ASCII characters only, instead of full Unicode. |
L | Locale-dependent | Makes \w, \b, \s (and case conversion) depend on the current C locale rather than a fixed Unicode/ASCII table. |
o | Once (interpolate once) | Tells the engine to interpolate #{...} variables into the pattern only once, on first use, and cache the compiled result. |
e | Eval (deprecated) | A historical PHP modifier that evaluated the replacement string as PHP code inside preg_replace(). |
I | Case-Insensitive (POSIX / grep) | The command-line equivalent of the i flag for tools built on POSIX regex, not an inline pattern flag. |
g — Global
Match all occurrences in the string, not just the first one.
Syntax across languages
JS: /pattern/g · Python: re.findall (implicit) · Go: regexp.FindAllString
Example
// JavaScript
"a-b-c".replace(/-/g, "_") // "a_b_c" (all dashes)
"a-b-c".replace(/-/, "_") // "a_b-c" (first only, no /g) Common gotcha
In JavaScript, /g changes the behaviour of String.match() — without /g it returns capture groups; with /g it returns an array of full matches and no groups.
i — Case-Insensitive
Match letters regardless of upper/lower case.
Syntax across languages
JS: /pattern/i · Python: re.IGNORECASE / re.I · Go: (?i)pattern
Example
// JavaScript
"Hello".match(/hello/i) // ["Hello"]
"Hello".match(/hello/) // null Common gotcha
Unicode letters: in JavaScript, /i alone is byte-level. Combine with /u for proper case folding on accented and non-Latin characters.
m — Multiline
Makes ^ and $ match the start and end of each line, not just the whole string.
Syntax across languages
JS: /pattern/m · Python: re.MULTILINE / re.M · Go: (?m)pattern
Example
// JavaScript
"a\nb\nc".match(/^./gm) // ["a", "b", "c"]
"a\nb\nc".match(/^./g) // ["a"] (without /m) Common gotcha
/m does NOT make . match newlines. For that you need /s (dotAll). The two are commonly confused.
s — DotAll (single line)
Makes . match newline characters too, not just non-newline characters.
Syntax across languages
JS: /pattern/s (ES2018+) · Python: re.DOTALL / re.S · Go: (?s)pattern
Example
// JavaScript
"a\nb".match(/a.b/s) // ["a\nb"]
"a\nb".match(/a.b/) // null Common gotcha
The name is misleading — "single line" mode actually treats the input as a single line so that . can cross line boundaries.
u — Unicode
Treats the pattern as a sequence of Unicode code points, not bytes. Enables \u{...} escapes and \p{...} property matches.
Syntax across languages
JS: /pattern/u · Python: re.UNICODE / re.U (default in Python 3) · Go: regexp is always UTF-8
Example
// JavaScript
"😀".match(/^.$/) // null (without /u — emoji is two surrogate halves)
"😀".match(/^.$/u) // ["😀"]
"café".match(/\p{Letter}+/u) // ["café"] Common gotcha
Without /u, JavaScript treats characters above U+FFFF as two separate surrogate code units — your pattern may match in the wrong place.
y — Sticky
Matches only at the lastIndex position of the regex; does not scan forward.
Syntax across languages
JS only: /pattern/y
Example
// JavaScript
const re = /foo/y;
re.lastIndex = 4;
re.test("xxxxfoo") // true — matches starting at index 4
re.lastIndex = 3;
re.test("xxxxfoo") // false — does NOT scan to position 4 Common gotcha
Mostly useful for tokenisers / lexers that need anchored matching at a specific cursor position.
d — hasIndices
Include match start/end indices for the full match and each capture group in the result.
Syntax across languages
JS only (ES2022+): /pattern/d
Example
// JavaScript
const m = "hello".match(/l(l)/d);
m.indices // [[2, 4], [3, 4]]
// [start, end] of full match, then each group Common gotcha
New in ES2022 — older browsers ignore the flag. Useful for syntax highlighters, linters, and editor integrations.
x — Extended / Verbose
Allow whitespace and # comments inside the pattern for readability. Not available in JavaScript.
Syntax across languages
Python: re.VERBOSE / re.X · Perl, PCRE, Ruby: (?x)
Example
# Python
import re
pattern = re.compile(r"""
^ # start
(?P<area>\d{3}) # area code
-
(?P<num>\d{4}) # number
$ # end
""", re.X)
pattern.match("555-1234").groupdict()
# {'area': '555', 'num': '1234'} Common gotcha
JavaScript does not support /x. To get readable patterns in JS, build them programmatically with new RegExp() from concatenated strings.
v — Unicode Sets
An upgraded version of /u (ES2024) that also supports set operations on character classes — union, intersection, and difference.
Syntax across languages
JS only (ES2024+): /pattern/v
Example
// JavaScript — subtract a set of characters from another set
/[\p{Letter}--[aeiou]]/v.test("b") // true (consonant)
/[\p{Letter}--[aeiou]]/v.test("a") // false (vowel, subtracted) Common gotcha
/v and /u are mutually exclusive on the same regex — you cannot combine them. /v is stricter about nested classes and catches more syntax mistakes that /u silently allowed.
U — Ungreedy
Swaps the default greediness of quantifiers: * and + become lazy by default, and adding ? after them makes them greedy again.
Syntax across languages
PCRE / PHP: (?U) or preg_match with PCRE_UNGREEDY · Go RE2: (?U)pattern
Example
// PHP with the u-modifier's ungreedy sibling
preg_match('/(?U)<.+>/', '<a><b>', $m);
echo $m[0]; // "<a>" — quantifiers are lazy by default under (?U)
// Without (?U), <.+> would greedily match "<a><b>" Common gotcha
Don't confuse PCRE's ungreedy modifier U (uppercase) with JavaScript's /u unicode flag (lowercase) — same letter, completely different meaning across engines.
A — Anchored
Forces the pattern to match only at the very start of the subject string (or immediately after the previous match), like an implicit ^.
Syntax across languages
PCRE: PREG_ANCHORED flag on the call, or (?:^) equivalent behaviour · not available inline in most other engines
Example
// PHP
preg_match('/foo/', 'xxfoo'); // matches "foo" at position 2
preg_match('/foo/', 'xxfoo', $m, 0, 0); // still matches — offset alone doesn't anchor
preg_match('/foo/A', 'xxfoo'); // no match — 'A' forces start-of-string Common gotcha
Anchored mode is stricter than a leading ^ in multiline mode: ^ can still match after a newline under /m, but A always means the true start of the subject.
D — Dollar End Only
Makes $ match only at the very end of the string, never right before a trailing newline (which is $'s default PCRE behaviour).
Syntax across languages
PCRE / PHP: (?D) or the PCRE_DOLLAR_ENDONLY option
Example
// PHP
preg_match('/foo$/', "foo\n"); // matches — $ allows a trailing \n by default
preg_match('/foo$D/', "foo\n"); // no match — 'D' disallows it Common gotcha
D has no effect if the m (multiline) flag is also set, since /m redefines what $ matches on every line rather than just the end of string.
J — Duplicate Named Groups
Allows multiple capture groups in the same pattern to share the same name — normally a compile-time error.
Syntax across languages
PCRE: (?J) inline, or the PCRE_DUPNAMES option
Example
// PCRE — same name reused in alternation branches
(?J)(?<year>\d{4})-\d{2}|(?<year>\d{2})/\d{2}
// Whichever branch matches, its "year" group is populated Common gotcha
Only one branch of the alternation is expected to actually participate per match — reading the "wrong" duplicate-named group when both could theoretically match leads to confusing results.
n — Explicit Capture
Only named groups (?<name>...) capture; plain (...) groups become non-capturing automatically.
Syntax across languages
.NET: RegexOptions.ExplicitCapture or (?n) inline
Example
// C#
var re = new Regex(@"(\d+)-(?<code>[A-Z]+)", RegexOptions.ExplicitCapture);
var m = re.Match("42-AB");
// m.Groups[1] does NOT exist — plain (\d+) wasn't captured
// m.Groups["code"] == "AB" — only the named group captured Common gotcha
Turning this on retroactively can silently break code that indexes capture groups by number, since previously-capturing plain parentheses stop capturing.
r — Right-to-Left
Scans the input from right to left instead of left to right — matters for lookaround direction and where scanning starts.
Syntax across languages
.NET: RegexOptions.RightToLeft
Example
// C#
var re = new Regex(@"\d+", RegexOptions.RightToLeft);
var m = re.Match("12 apples, 34 oranges");
// m.Value == "34" — the first match found scanning from the right Common gotcha
Right-to-left changes which match is found "first" in a string with multiple matches, and flips the practical meaning of lookahead vs. lookbehind — test carefully rather than assuming symmetry.
a — ASCII-only
Restricts \w, \b, \s, \d (and case-insensitive matching) to ASCII characters only, instead of full Unicode.
Syntax across languages
Python: re.ASCII / re.A
Example
# Python
import re
re.findall(r'\w+', 'café', re.UNICODE) # ['café'] (default in Py3)
re.findall(r'\w+', 'café', re.ASCII) # ['caf'] ('é' is not ASCII \w) Common gotcha
Python 3 treats \w/\d/\s as Unicode-aware by default — re.A is an opt-in narrowing, the reverse of most other engines where you opt in to Unicode.
L — Locale-dependent
Makes \w, \b, \s (and case conversion) depend on the current C locale rather than a fixed Unicode/ASCII table.
Syntax across languages
Python: re.LOCALE / re.L (bytes patterns only, deprecated since Python 3.6)
Example
# Python — locale affects what counts as a "word" character
import re, locale
locale.setlocale(locale.LC_ALL, 'de_DE')
re.match(rb'\w+', 'straße'.encode('latin-1'), re.LOCALE) Common gotcha
Deprecated for a reason: locale is global mutable process state, so re.L patterns behave differently depending on what else in the process last called setlocale(). Prefer re.UNICODE for portable behaviour.
o — Once (interpolate once)
Tells the engine to interpolate #{...} variables into the pattern only once, on first use, and cache the compiled result.
Syntax across languages
Ruby only: /pattern#{var}/o
Example
# Ruby
name = "Alice"
re = /^#{name}$/o # interpolated once; later changes to 'name' are ignored
name = "Bob"
"Alice" =~ re # still matches "Alice", not "Bob" Common gotcha
Silently stale patterns are the classic bug here — if the interpolated variable is meant to change per call, /o will use the first value forever. Omit /o (the modern default in most cases) unless you specifically want caching.
e — Eval (deprecated)
A historical PHP modifier that evaluated the replacement string as PHP code inside preg_replace().
Syntax across languages
PHP: preg_replace("/pattern/e", ...) — removed entirely in PHP 7.0
Example
// PHP 5 (legacy, DO NOT use)
preg_replace('/(\d+)/e', 'strtoupper("num")', $subject);
// The replacement string was eval()'d as PHP — a major code-injection risk Common gotcha
This flag was removed in PHP 7 specifically because it made preg_replace() an easy remote-code-execution vector when patterns or subjects came from user input. Use preg_replace_callback() instead.
I — Case-Insensitive (POSIX / grep)
The command-line equivalent of the i flag for tools built on POSIX regex, not an inline pattern flag.
Syntax across languages
grep -i, egrep -i, awk IGNORECASE=1 — POSIX ERE has no inline (?i) syntax
Example
# Shell
grep -i "error" server.log # matches Error, ERROR, error
grep -E -i "warn|fail" *.log # -E enables extended regex, -i case-folds it Common gotcha
POSIX Basic/Extended Regular Expressions (the grep/sed/awk family) generally have no inline flag syntax like (?i) — case-insensitivity and multiline behaviour are controlled by CLI flags or tool-specific variables instead.
English phrases engineers use
- "Add
/gto match all occurrences — without it you only replace the first one." - "This regex is case-sensitive — add the
iflag." - "In multiline mode,
^matches the start of each line." - "By default, the dot does not match newlines — you need
s(dotAll) for that." - "Without
u, emoji break the regex because they are surrogate pairs." - "This regex is too greedy; let's use a non-greedy quantifier."
Frequently Asked Questions
What does the g flag actually change about String.match() behavior in JavaScript?
Without /g, match() returns a single match array that includes capture groups and index information. With /g, match() instead returns an array of every full match in the string, but drops the capture groups entirely — if you need both all occurrences and their captured groups, use matchAll() instead.
What's the difference between the m (multiline) and s (dotAll) flags — they're commonly confused?
m changes what ^ and $ anchor to, making them match the start and end of each line instead of only the start and end of the whole string. s (dotAll) changes what the dot (.) matches, letting it cross newline characters instead of stopping at them. They solve unrelated problems and can be combined, but the similar names lead people to reach for the wrong one.
Why does /u matter when matching emoji or accented characters in JavaScript?
Without /u, JavaScript regex operates on UTF-16 code units, so characters above U+FFFF — most emoji included — are seen as two separate surrogate halves rather than one character. A pattern like /^.$/ fails to match a single emoji without /u, but succeeds with it because /u makes the engine treat the string as full Unicode code points.
What is the y (sticky) flag for, and how is it different from g?
The sticky flag anchors matching to exactly the regex's lastIndex position and fails if the match isn't found there — it does not scan forward looking for the next match the way /g does. This makes it the right tool for tokenizers and lexers that need to consume input from a specific cursor position without skipping ahead.
What does the newer /v flag add over /u in JavaScript (ES2024)?
The /v flag is a stricter superset of /u that additionally supports set operations on character classes — union, intersection, and difference, such as subtracting vowels from all letters. It's mutually exclusive with /u on the same pattern and rejects some ambiguous nested-class syntax that /u silently allowed.
Why was PHP's /e modifier removed, and what should be used instead?
The /e modifier evaluated the replacement string of preg_replace() as executable PHP code, which turned any pattern or subject sourced from user input into a potential remote-code-execution vector. PHP removed it entirely in PHP 7.0; the safe replacement is preg_replace_callback(), which passes matches to an ordinary callback function instead of eval-ing a string.
How does PCRE's ungreedy modifier (?U) differ from JavaScript's /u flag, despite sharing the letter U?
They are unrelated features that happen to share a letter in different cases across engines. PCRE's (?U) — uppercase — flips the default greediness of quantifiers so * and + become lazy by default. JavaScript's /u — lowercase — enables Unicode code-point matching and has nothing to do with greediness.
What does Python's re.ASCII (the a flag) restrict compared to the default?
Python 3 treats \w, \d, \s, and \b as Unicode-aware by default, so \w+ matches accented letters like "café" in full. re.ASCII narrows those same character classes back down to the ASCII range only, which is the opposite of most other regex engines, where you opt into Unicode rather than opt out of it.
Why is Python's re.LOCALE (the L flag) deprecated?
re.LOCALE makes \w, \b, \s, and case conversion depend on the process-wide C locale set by locale.setlocale(), which is global mutable state shared across the whole process. That means a re.LOCALE pattern's behavior can silently change depending on what other code last touched the locale — Python recommends re.UNICODE instead for portable, predictable behavior.
What does PCRE's Explicit Capture (the n flag) change about plain parentheses?
With Explicit Capture enabled (via (?n) or .NET's RegexOptions.ExplicitCapture), only named groups like (?<name>...) actually capture; ordinary (...) groups become non-capturing automatically. Turning this on retroactively can silently break code that indexes capture groups by number, since previously-capturing plain parentheses stop producing a numbered group.