Skip to content

\Q…\E — quote a run of metacharacters, and the function that does it better

One line: \Q…\E makes everything between the two escapes literal — in Java, Go and PCRE2 as a feature of the engine, in Perl as a feature of string interpolation that the engine itself does not have — and every host language ships a function (re.escape, quotemeta, Regexp.escape, Pattern.quote, regexp.QuoteMeta, regex::escape) that is the portable way to say the same thing.

Level: reference · one construct, eight engines

What it means

A pattern built from user input needs the input's metacharacters neutralised, or a.b matches axb and ( is a syntax error. Two mechanisms:

Mechanism Where How
\Q…\E in the pattern Java, Go, PCRE2 (ABAP), grep -P the engine treats the run as literal
\Q…\E in Perl Perl quotemeta applied during interpolation — the engine never sees \Q
An escape function every language returns a pattern that matches the text exactly

Measured

Verified output of kw_quote_sh.sh — regenerated by tools/run_examples.py, never hand-typed.

construct                 python  perl   node   ruby   java  go   rust  fancy
\Q...\E quoted            -       no     no     no     a.b   a.b  -     -
\Q...\E does not match x  -       no     no     no     no    no   -     -
\Q in JavaScript          -       Qa.bE  Qa.bE  Qa.bE  a.b   a.b  -     -
\Q without \E             -       no     no     no     a.b   a.b  -     -

Reading the table

  • Row 1, \Qa.b\E on a.b. Java and Go match. Python, Rust and fancy-regex refuse the escape. Perl, JavaScript and Ruby compile it and answer no — and row 3 says why: the same three match the text Qa.bE. Their engines read \Q as the letter Q.
  • Perl's row is not a bug in Perl. \Q is real in Perl source — /\Q$input\E/ works — because the interpolating string quotes the text before the engine sees it. The probe hands the engine a pattern that already contains the characters \Q, which is what happens to any Perl pattern loaded from a file or a database, and the engine has no such escape. The lesson is about where the feature lives, and it lives in the string, not the regex.
  • Row 2. For the engines that have it, \Qa.b\E does not match axb — the dot was literal.
  • Row 4, \Q with no \E. Java and Go quote to the end of the pattern.

The function in each language

Verified output of kw_quote_escape_sh.sh — regenerated by tools/run_examples.py, never hand-typed.

input    a.b*c[d]{e}$ f
python   a\.b\*c\[d\]\{e\}\$\ f
perl     a\.b\*c\[d\]\{e\}\$\ f
ruby     a\.b\*c\[d\]\{e\}\$\ f
java     \Qa.b*c[d]{e}$ f\E
go       a\.b\*c\[d\]\{e\}\$ f
rust     a\.b\*c\[d\]\{e\}\$ f

Six languages, three answers for one input. Python, Perl and Ruby backslash every metacharacter and the space. Go and Rust backslash the metacharacters only. Java does not backslash anything: Pattern.quote wraps the text in \Q…\E, which is why Java's table cells above were the ones that worked. Every one of these six outputs is a correct pattern for its own engine, and none is guaranteed to be correct for another — a Python-escaped string handed to Java has a \ in it, which Java rejects.

JavaScript's RegExp.escape is an ES2025 addition; Node 24 has it, Node 20 does not, so it is not in the table.

The trap

Building a pattern with string concatenation and no escaping. "^" + name + "$" is a working validator until name contains a dot, a bracket or a dollar, and then it is either an error or — worse — a pattern that accepts more than it should. Use the function; it exists in every language here and it is one call.

Where it is measured in depth

See also