Regular Expressions Cheatsheet

Flags and Modifiers

Use this Regular Expressions reference while you build software engineering projects, review code, or refresh the syntax you reach for most.

The common flags

FlagNameEffect
iignore casea matches A
mmultiline^ and $ match at every line break
sdotall / single line. also matches newline
xextended / verboseunescaped whitespace and # comments ignored
uunicodeJS: \p{...}, \u{...}, code-point semantics
vunicode setsJS: u plus set operations inside classes
gglobalJS only, keep scanning past the first match
ystickyJS only, match must start exactly at lastIndex
dindicesJS only, populate match.indices
AASCIIPython: make \d \w \s \b ASCII-only
UungreedyPCRE and Go: swap greedy and lazy meanings
Jdup namesPCRE: allow two groups with the same name

m and s are independent. m does not let . cross a newline, and s does not move ^.

Setting flags per language

// literal, flags after the closing slash
/^\d+$/gim.test(s)
new RegExp("^\\d+$", "gim")
"aAbA".replace(/a/gi, "-")     // "--b-", i widens g
import re
re.search(r'^\d+$', s, re.I | re.M)      # bitwise OR
re.compile(r'^\d+$', re.IGNORECASE)      # long names exist for all of them
# no global flag, the function picks the behavior instead
re.search(...)    # first match
re.findall(...)   # every match
re.finditer(...)  # every match, lazily, with positions
re.sub(...)       # replace all by default, cap it with count=1
grep -i -E 'pattern' file       # case-insensitive ERE
sed -E 's/x/y/gI' file          # g = every match on the line, I = ignore case
regexp.MustCompile(`(?is)^a.b$`)   // Go has no flag argument, only inline

Inline flag syntax

FormScope
(?i)on for the rest of the pattern
(?i:...)on inside the group only
(?-i:...)off inside the group only, even under an outer i
(?im-sx:...)several at once, - separates the off list
re.search(r'(?i:hello) World', 'HELLO World')   # matches
re.search(r'(?i)hello world', 'HELLO WORLD')    # matches
  • Python allows a bare (?i) only at the very start of the pattern. Anywhere else it raises global flags not at the start of the expression. Use (?i:...) when it needs to be local.
  • JavaScript supports no inline flags at all. /(?i)ab/ is a syntax error, and /\p{L}/ without u quietly matches the literal text p{L} instead of erroring.
  • Go supports (?i), (?m), (?s), (?U) and the scoped forms, but not (?x).

Extended mode x

Whitespace and # comments become invisible, which makes a long pattern readable. Escape the spaces you actually want, or put them in a class.

phone = re.compile(r'''
    (?P<area> \d{3} )   # area code
    [-.\s]?             # optional separator
    (?P<exch> \d{3} )
    [-.\s]?
    (?P<line> \d{4} )
''', re.X)
phone.search('call 555-867-5309').groupdict()
# {'area': '555', 'exch': '867', 'line': '5309'}

Inside x mode, a b matches ab. To match a real space write a\ b, a[ ]b, or a\sb. Whitespace inside a bracket expression is still literal, so [a b] includes the space.

Case-insensitive is not case-folding

i compares character by character, so it cannot handle a one-to-many fold.

bool(re.search('straße', 'STRASSE', re.I))   # False, ß vs SS
bool(re.search('É', 'é', re.I))              # True, simple 1:1 fold
/σ/iu.test("Σ")   // true, sigma folds
/k/iu.test("K")   // true, U+212A KELVIN SIGN folds to k

For real language-aware comparison, normalize first (str.casefold() in Python, s.toLowerCase() plus normalize("NFKC") in JavaScript) and match on the normalized form. Turkish dotless i and Greek final sigma will still bite.

Flags that change results, not just matching

  • i widens what a class matches, so [a-z] with i also matches A-Z, and [^a-z] with i therefore matches neither case.
  • s makes .* swallow the whole document. When parsing line-oriented data, keep s off and use [\s\S] where you genuinely mean any character.
  • U in PCRE and Go inverts greediness, so .+ becomes lazy and .+? becomes greedy. It reads as a bug to everyone else, prefer explicit ?.
  • JavaScript's g makes a regex object stateful through lastIndex. Reusing one g regex across calls to .test() skips matches. Create it fresh, or use matchAll and replaceAll.
const r = /a/g;
r.test("aa");   // true,  lastIndex 1
r.test("aa");   // true,  lastIndex 2
r.test("aa");   // false, lastIndex reset to 0