\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\Eona.b. Java and Go match. Python, Rust andfancy-regexrefuse the escape. Perl, JavaScript and Ruby compile it and answerno— and row 3 says why: the same three match the textQa.bE. Their engines read\Qas the letter Q. - Perl's row is not a bug in Perl.
\Qis 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\Edoes not matchaxb— the dot was literal. - Row 4,
\Qwith 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¶
- Backreferences in the replacement — the same problem on the other side of a substitution.
See also¶
- Escaping and literal text — the topic page, with the rules for which characters need a backslash where.
- Character class — the other place metacharacters go quiet.
- Security.