Regular Expressions Cheatsheet

Unicode

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

Property escapes

\p{...} matches one code point with that Unicode property, \P{...} negates it.

EscapeMatches
\p{L}any letter, any script
\p{Lu} \p{Ll}uppercase, lowercase letter
\p{N} / \p{Nd}any number / decimal digit
\p{P}punctuation
\p{Zs}space separator
\p{M}combining mark
\p{C}control and other
\p{Alpha} \p{White_Space}binary properties
\p{Script=Greek}code points whose script is Greek
\p{Script_Extensions=Greek}Greek plus code points shared with it
\p{Emoji_Presentation}default-emoji-rendering code points
/^\p{L}+$/u.test("café");                    // true
/\p{Script=Greek}+/u.exec("aαβ")[0];         // "αβ"
/\p{Lu}/u.test("A");                         // true
/^\p{Nd}+$/u.test("٣٤");                     // true, Arabic-Indic digits

JavaScript requires the u or v flag. Without it, /\p{L}/ does not throw, it matches the literal text p{L}. That silent failure is the single most common Unicode regex bug in JS.

Support by flavor

Flavor\p{...}
JavaScriptyes, with u or v
PCRE2 / PHPyes, needs /u in PHP for UTF-8
Javayes, \p{IsGreek}, \p{IsAlphabetic}, \p{L}
.NETyes
Go RE2yes, \p{Greek}, \p{L}, no Script= prefix
Rubyyes
Python reno, \p is a bad-escape error
Python regex packageyes
grep -Pyes, PCRE
grep -E / POSIXno, use [[:alpha:]]
# Python re has no \p, install the regex package for it
import regex
regex.findall(r'\p{Greek}+', 'aαβ')   # ['αβ']
# or fall back to what re does give you
import re
re.findall(r'\w+', 'café')            # ['café'], \w is Unicode-aware in re

What the shorthand classes mean per flavor

Flavor\d\w\b
Python re (default)any Unicode digitUnicode letters, digits, _Unicode-aware
Python re with re.A[0-9][A-Za-z0-9_]ASCII
JavaScript, with or without u[0-9] always[A-Za-z0-9_] alwaysASCII
Java[0-9] unless UNICODE_CHARACTER_CLASSsamesame
PCRE[0-9] unless /u and PCRE2_UCPsamesame
Go RE2[0-9][0-9A-Za-z_]ASCII
re.fullmatch(r'\w+', 'café')            # matches
re.fullmatch(r'\w+', 'café', re.A)      # None
re.match(r'\d', '٣')                    # matches, Arabic-Indic three

In JavaScript, use \p{Nd} for "any digit" and [\p{L}\p{N}_] for a Unicode \w. In Go, spell it [\p{L}\p{Nd}_].

Code units, code points, and graphemes

Three different lengths for the same visible text.

TextGraphemesCode pointsUTF-16 units
é (precomposed U+00E9)111
é (e + U+0301)122
👨112
👨‍👩‍👧 (family, ZWJ)158
"👨".match(/./g).length;    // 2, surrogate halves, no u flag
"👨".match(/./gu).length;   // 1, u makes . one code point
[..."👨‍👩‍👧"].length;         // 5, still not 1

. and {n} count code points at best, never graphemes. ^.{1,10}$ on user input rejects a ten-emoji string and accepts a one-character string with fifty combining marks. Count with the host language instead, Intl.Segmenter in JavaScript or the regex module's \X in Python.

const g = [...new Intl.Segmenter().segment("👨‍👩‍👧a")];
g.length;   // 2

Normalize before you match

The same word can be two different strings. NFC composes, NFD decomposes.

const a = "café";              // U+00E9
const b = "café";        // e + combining acute
a === b;                       // false
/^café$/u.test(b);             // false
a.normalize("NFC") === b.normalize("NFC");   // true
import unicodedata as ud
ud.normalize('NFC', 'café') == 'café'   # True

Normalize to NFC on the way in, then match. For search, normalize and strip marks: NFD, drop \p{M}, and you have a fold-insensitive key.

// "Crème Brûlée" -> "creme brulee"
s.normalize("NFD").replace(/\p{M}+/gu, "").toLowerCase();

The v flag, set operations

JavaScript's v flag (ES2024) is u plus real set algebra inside a class. u and v are mutually exclusive.

SyntaxMeaning
[\p{L}--\p{Lu}]letters except uppercase, difference
[\p{L}&&\p{ASCII}]letters that are ASCII, intersection
[\p{L}\p{N}]union, as always
[\q{abc|xyz}]match a multi-code-point string
\p{RGI_Emoji}one whole recommended emoji, ZWJ sequences included
/^[\p{L}--\p{Lu}]$/v.test("a");        // true
/^[\p{L}--\p{Lu}]$/v.test("A");        // false
/^[\p{L}&&\p{ASCII}]$/v.test("é");     // false
/^\p{RGI_Emoji}$/v.test("👨‍👩‍👧");        // true

Under v, [, (, {, /, -, | and the double punctuators must be escaped inside a class. Existing u patterns are not always v-clean, so change the flag deliberately rather than as a blanket upgrade.