5.14. Escape Regular Expression Metacharacters

Problem

You want to use a literal string provided by a user or from some other source as part of a regular expression. However, you want to escape all regular expression metacharacters within the string before embedding it in your regex, to avoid any unintended consequences.

Solution

By adding a backslash before any characters that potentially have special meaning within a regular expression, you can safely use the resulting pattern to match a literal sequence of characters. Of the programming languages covered by this book, all except JavaScript have a built-in function or method to perform this task (listed in Table 5-3). However, for the sake of completeness, we’ll show how to pull this off using your own regex, even in the languages that have a ready-made solution.

Built-in solutions

Table 5-3 lists the built-in functions and methods designed to solve this problem.

Table 5-3. Built-in solutions for escaping regular expression metacharacters

Language

Function

C#, VB.NET

Regex.Escape(str)

Java

Pattern.quote(str)

XRegExp

XRegExp.escape(str)

Perl

quotemeta(str)

PHP

preg_quote(str, [delimiter])

Python

re.escape(str)

Ruby

Regexp.escape(str)

Notably absent from the list is JavaScript (without XRegExp), which does not have a native function designed for this purpose.

Regular expression

Although it’s best to use a built-in solution if available, you can pull this off on your own by using the following regular expression along with the appropriate replacement string (shown next). Make sure to replace all matches, rather than only the first. Recipe 3.15 shows code for replacing matches with strings that contain backreferences. You’ll need a backreference here to bring back the matched special character along with a preceding backslash:

[[]{}()*+?.\|^$-,&#s]
Regex options: None
Regex flavors: .NET, Java, JavaScript, PCRE, Perl, Python, Ruby

Replacement

Tip

The following replacement strings contain a literal backslash character. The strings are shown without the extra backslashes that may be needed to escape backslashes when using string literals in some programming languages. See Recipe 2.19 for more details about replacement text flavors.

$&
Replacement text flavors: .NET, JavaScript
$0
Replacement text flavors: .NET, XRegExp
\$&
Replacement text flavor: Perl
\$0
Replacement text flavors: Java, PHP
\
Replacement text flavors: PHP, Ruby
\&
Replacement text flavor: Ruby
\g<0>
Replacement text flavor: Python

Example JavaScript function

Here’s an example of how you can put the regular expression and replacement string to use to create a static method called RegExp.escape() in JavaScript:

RegExp.escape = function(str) {
    return str.replace(/[[]{}()*+?.\|^$-,&#s]/g, "\$&");
};

// Test it...
var str = "<Hello World.>";
var escapedStr = RegExp.escape(str);
alert(escapedStr == "<Hello\ World\.>"); // -> true

Discussion

This recipe’s regular expression puts all the regex metacharacters inside a single character class. Let’s take a look at each of those characters, and examine why they need to be escaped. Some are less obvious than others:

[ { ( )

[ creates a character class. { creates an interval quantifier and is also used with some other special constructs, such as Unicode properties. ( and ) are used for grouping, capturing, and other special constructs.

* + ?

These three characters are quantifiers that repeat their preceding element zero or more, one or more, or between zero and one time, respectively. The question mark is also used after an opening parenthesis to create special groupings and other constructs (the same is true for the asterisk in Perl 5.10 and PCRE 7).

. |

A dot matches any character within a line or string, a backslash makes a special character literal or a literal character special, and a vertical bar alternates between multiple options.

^ $

The caret and dollar symbols are anchors that match the start or end of a line or string. The caret can also negate a character class.

The remaining characters matched by the regular expression are only special in special circumstances. They’re included in the list to err on the side of caution.

]

A right square bracket ends a character class. Normally, this would not need to be escaped on its own, but doing so avoids unintentionally ending a character class when embedding text inside one. Keep in mind that if you do embed text inside a character class, the resulting regex will not match the embedded string, but rather any one of the characters in the embedded string.

-

A hyphen creates a range within a character class. It’s escaped here to avoid inadvertently creating ranges when embedding text in the middle of a character class.

}

A right curly bracket ends an interval quantifier or other special construct. Since most regular expression flavors treat curly brackets as literal characters if they do not form a valid quantifier, it’s possible to create a quantifier where there was none before when inserting literal text in a regex if you don’t escape both ends of curly brackets.

,

A comma is used inside an interval quantifier such as {1,5}. It’s possible (though a bit unlikely) to create a quantifier where there was none before when inserting literal text in a regex if you don’t escape commas.

&

The ampersand is included in the list because two ampersands in a row are used for character class intersection in Java (see Flavor-Specific Features). In other programming languages, it’s safe to remove the ampersand from the list of characters that need to be escaped, but it doesn’t hurt to keep it.

# and whitespace

The pound sign and whitespace (matched by s) are metacharacters only if the free-spacing option is enabled. Again, it doesn’t hurt to escape them anyway.

As for the replacement text, one of five tokens («$&», «&», «$0», «», or «g<0>») is used to restore the matched character along with a preceding backslash. In Perl, $& is actually a variable, and using it with any regular expression imposes a global performance penalty on all regular expressions. If $& is used elsewhere in your Perl program, it’s OK to use it as much as you want because you’ve already paid the price. Otherwise, it’s probably better to wrap the entire regex in a capturing group, and use $1 instead of $& in the replacement.

Variations

As explained in Block escape, you can create a block escape sequence within a regex using QE. However, block escapes are only supported by Java, PCRE, and Perl, and even in those languages block escapes are not foolproof. For complete safety, you’d still need to escape any occurrence of E within the string you plan to embed in your regex. In most cases it’s probably easier to just use the cross-language approach of escaping all regex metacharacters.

See Also

Recipe 2.1 discusses how to match literal characters and escape metacharacters. However, its list of characters that need to be escaped is shorter since it doesn’t concern itself with characters that may need to be escaped in free-spacing mode or when dropped into an arbitrary, longer pattern.

The example JavaScript solution in Recipe 5.2 creates a function that escapes any regular expression metacharacters within words to be searched for. It uses the shorter list of special characters from Recipe 2.1.

Techniques used in the regular expression and replacement text in this recipe are discussed in Chapter 2. Recipe 2.3 explains character classes. Recipe 2.20 explains how to insert the regex match into the replacement text.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset