Skip to content
Eugene Lazutkin edited this page Nov 11, 2026 · 2 revisions

Unicode support

RE2 always works in Unicode mode. In most cases this is either invisible or preferred.

const RE2 = require('re2');

new RE2('\\p{Script=Cyrillic}+').exec('abc привет')[0]; // 'привет'
new RE2('[^\\p{ASCII}]').test('é'); // true

Unicode warning level

For applications that need tight control, the static property RE2.unicodeWarningLevel governs what happens when a non-Unicode regular expression is created.

If a regular expression lacks the u flag, it is added silently by default:

const RE2 = require('re2');

const x = /./;
x.flags; // ''
const y = new RE2(x);
y.flags; // 'u'

Values of RE2.unicodeWarningLevel:

  • 'nothing' (default) — silently add u.
  • 'warnOnce' — warn once, then silently add u. Assigning this value resets the one-time flag.
  • 'warn' — warn every time, still add u.
  • 'throw' — throw SyntaxError.
  • Any other value is silently ignored, leaving the previous value unchanged.

Warnings and exceptions help audit an application for stray non-Unicode regular expressions.

RE2.unicodeWarningLevel is global and shared across threads, so take care in multi-threaded environments.

Unicode classes \p{...} and \P{...}

node-re2 follows MDN's Unicode character class escape reference — the same set of property escapes that JavaScript's native RegExp accepts with the u flag.

Supported categories:

  • General_Category — both short names (for example, \p{L}, \p{Lu}) and long names (for example, \p{Letter}, \p{Uppercase_Letter}). The gc= and General_Category= prefixes also work: \p{gc=Letter}, \p{General_Category=Letter}.

  • Script — for example, \p{Script=Latin}, \p{sc=Cyrillic}. ISO 15924 four-letter codes are accepted as well: \p{sc=Latn}.

  • Script_Extensions — for example, \p{Script_Extensions=Hani}, \p{scx=Latn}. Matches characters whose Script_Extensions list includes the named script (a superset of Script=).

  • Binary properties — the full ECMAScript set of binary properties, including:

    • Alphabetic, ASCII, ASCII_Hex_Digit, Hex_Digit, White_Space, Math, Dash, Diacritic, Quotation_Mark, Bidi_Mirrored, Bidi_Control, Default_Ignorable_Code_Point
    • Lowercase, Uppercase, Cased, Case_Ignorable, Changes_When_Lowercased, Changes_When_Uppercased, Changes_When_Casefolded, Changes_When_Casemapped, Changes_When_Titlecased, Changes_When_NFKC_Casefolded
    • ID_Start, ID_Continue, XID_Start, XID_Continue, Pattern_Syntax, Pattern_White_Space
    • Emoji, Emoji_Presentation, Emoji_Modifier, Emoji_Modifier_Base, Emoji_Component, Extended_Pictographic, Regional_Indicator
    • Grapheme_Base, Grapheme_Extend, Extender, Variation_Selector, Join_Control, Logical_Order_Exception, Sentence_Terminal, Terminal_Punctuation, Soft_Dotted, Radical, Unified_Ideograph, Ideographic, IDS_Binary_Operator, IDS_Trinary_Operator, Noncharacter_Code_Point, Deprecated, Any, Assigned

    Short aliases listed in PropertyAliases.txt are accepted alongside the canonical names: \p{Alpha} ≡ \p{Alphabetic}, \p{Hex} ≡ \p{Hex_Digit}, \p{Lower} ≡ \p{Lowercase}, and so on.

The negated form \P{...} and use inside character classes ([\p{L}\p{Emoji}], [^\p{ASCII}]) work for every category.

Not supported: Properties of Strings (\p{Basic_Emoji}, \p{RGI_Emoji}, and so on). These match multi-codepoint sequences and require the v flag, which RE2 does not model.

Tables are baked in at build time from Unicode 17.0. To target a newer Unicode version, bump @unicode/unicode-XX.X.X in devDependencies and run node scripts/gen-unicode-properties.mjs.

Memory cost of Unicode classes

A \p{...} or \P{...} escape stands for a character class that can hold hundreds of codepoint ranges. Building that class takes memory and time, and the cost repeats for every occurrence of the escape in a pattern.

node-re2 adds to the cost for some properties. RE2 supports General_Category and Script natively, and node-re2 passes those escapes to it unchanged. For binary properties, such as Alphabetic and Emoji, and for Script_Extensions, node-re2 writes the full list of ranges into the pattern, once for each occurrence. For example, \p{Assigned} becomes about 12 KB of pattern text, and \p{Grapheme_Base} about 14 KB.

The escapes that RE2 handles natively are costly too, because RE2's parser builds a separate class for every occurrence. RE2's memory budget (max_mem, 8 MiB by default, set by the maxMem option of RE2.Set) applies when RE2 compiles the parsed pattern, so nothing limits the parse itself. The built-in RegExp also builds a class for every occurrence.

The following table shows the approximate memory that each occurrence takes while a long pattern is parsed:

Escape Expanded by node-re2 RE2 RegExp
\p{Script=Greek} No 3 KiB 1 KiB
\p{L} or \pL No 32 KiB 13 KiB
\p{Assigned} Yes 60 KiB 13 KiB
\p{Grapheme_Base} Yes 75 KiB 27 KiB

The numbers come from Node.js 26.13.0 on Linux x64, with patterns that repeat one escape thousands of times. RegExp rejects \pL, because JavaScript requires the braces.

A pattern that compiles costs more, because compiling adds its own memory: a pattern of 400 \pL escapes takes about 65 MiB to build. With the default budget, a pattern holds a few hundred large classes before RE2 rejects it with SyntaxError: pattern too large - compile failed, and the parse spends its memory before that error. A pattern of a few hundred kilobytes can take several gigabytes and run the process out of memory. For example, each of the following lines stops a process limited to 2 GiB of address space:

new RE2('\\pL'.repeat(100000), 'u'); // a 300 KB pattern
new RegExp('\\p{L}'.repeat(400000), 'u'); // a 2 MB pattern

To keep the cost down:

  • Use the narrowest class that fits your data. The cost grows with the number of ranges in the class.
  • Repeat a class with + or *. A counted repeat costs as much as writing the class out: \pL{400} takes as much memory to build as 400 copies of \pL.
  • Build a pattern once and reuse the RE2 object.
  • If you build patterns from untrusted input, limit their length and the number of their \p and \P escapes before you pass them to RE2 or RegExp. Neither engine limits the memory that a pattern takes to parse.

Clone this wiki locally