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

FlagNameEffect
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 /g to match all occurrences — without it you only replace the first one."
  • "This regex is case-sensitive — add the i flag."
  • "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.