UNB/ CS/ David Bremner/ teaching/ cs2613/ books/ mdn/ Reference/ Regular expressions/ Character class

[[!meta title=""Character class: [...], [^...]""]]

A character class matches any character in or not in a custom set of characters. When the v flag is enabled, it can also be used to match finite-length strings.

Syntax

[]
[abc]
[A-Z]

[^]
[^abc]
[^A-Z]

// `v` mode only
[operand1&&operand2]
[operand1--operand2]
[\q{substring}]

Parameters

Description

A character class specifies a list of characters between square brackets and matches any character in the list. The v flag drastically changes how character classes are parsed and interpreted. The following syntaxes are available in both v mode and non-v mode:

These syntaxes can occur any number of times, and the character sets they represent are unioned. For example, /[a-zA-Z0-9]/ matches any letter or digit.

The ^ prefix in a character class creates a complement class. For example, [^abc] matches any character except a, b, or c. The ^ character is a literal character when it appears in the middle of a character class β€” for example, [a^b] matches the characters a, ^, and b.

The lexical grammar does a very rough parse of regex literals, so that it does not end the regex literal at a / character which appears within a character class. This means /[/]/ is valid without needing to escape the /.

The boundaries of a character range must not specify more than one character, which happens if you use a character class escape. For example:

/[\s-9]/u; // SyntaxError: Invalid regular expression: Invalid character class

In Unicode-unaware mode, character ranges where one boundary is a character class makes the - become a literal character. This is a deprecated syntax for web compatibility, and you should not rely on it.

/[\s-9]/.test("-"); // true

In Unicode-unaware mode, regexes are interpreted as a sequence of BMP characters. Therefore, surrogate pairs in character classes represent two characters instead of one.

/[πŸ˜„]/.test("\ud83d"); // true
/[πŸ˜„]/u.test("\ud83d"); // false

/[πŸ˜„-πŸ˜›]/.test("πŸ˜‘"); // SyntaxError: Invalid regular expression: /[πŸ˜„-πŸ˜›]/: Range out of order in character class
/[πŸ˜„-πŸ˜›]/u.test("πŸ˜‘"); // true

Even if the pattern ignores case, the case of the two ends of a range is significant in determining which characters belong to the range. For example, the pattern /[E-F]/i only matches E, F, e, and f, while the pattern /[E-f]/i matches all uppercase and lowercase letters (because it spans over E–Z and a–f), as well as [, \, ], ^, _, and `.

Non-v-mode character class

Non-v-mode character classes interpret most character literally and have less restrictions about the characters they can contain. For example, . is the literal dot character, not the wildcard. The only characters that cannot appear literally are \, ], and -.

v-mode character class

The basic idea of character classes in v mode remains the same: you can still use most characters literally, use - to denote character ranges, and use escape sequences. One of the most important features of the v flag is set notation within character classes. As previously mentioned, normal character classes can express unions by concatenating two ranges, such as using [A-Z0-9] to mean "the union of the set [A-Z] and the set [0-9]". However, there's no easy way to represent other operations with character sets, such as intersection and difference.

With the v flag, intersection is expressed with &&, and subtraction with --. The absence of both implies union. The two operands of && or -- can be a character, character escape, character class escape, or even another character class. For example, to express "a word character that's not an underscore", you can use [\w--_]. You cannot mix operators on the same level. For example, [\w&&[A-z]--_] is a syntax error. However, because you can nest character classes, you can be explicit by writing [\w&&[[A-z]--_]] or <span class="createlink">&#92;w&#38;&#38;&#91;A-z</span>--_] (which both mean [A-Za-z]). Similarly, [AB--C] is invalid and you need to write [A[B--C]] (which just means [AB]).

In v mode, the Unicode character class escape \p can match finite-length strings, such as emojis. For symmetry, regular character classes can also match more than one character. To write a "string literal" in a character class, you wrap the string in \q{...}. The only regex syntax supported here is disjunction β€” apart from this, \q must completely enclose literals (including escaped characters). This ensures that character classes can only match finite-length strings with finitely many possibilities.

Because the character class syntax is now more sophisticated, more characters are reserved and forbidden from appearing literally.

Complement character classes [^...] cannot possibly be able to match strings longer than one character. For example, [\q{ab|c}] is valid and matches the string "ab", but [^\q{ab|c}] is invalid because it's unclear how many characters should be consumed. The check is done by checking if all \q contain single characters and all \p specify character properties β€” for unions, all operands must be purely characters; for intersections, at least one operand must be purely characters; for subtraction, the leftmost operand must be purely characters. The check is syntactic without looking at the actual character set being specified, which means although /[^\q{ab|c}--\q{ab}]/v is equivalent to /[^c]/v, it's still rejected.

Complement classes and case-insensitive matching

In non-v-mode, complement character classes [^...] are implemented by simply inverting the match result β€” that is, [^...] matches whenever [...] doesn't match, and vice versa. However, the other complement classes, such as \P{...} and \W, work by eagerly constructing the set consisting of all characters without the specified property. They seem to produce the same behavior, but are made more complex when combined with case-insensitive matching.

Consider the following two regexes:

const r1 = /\p{Lowercase_Letter}/iu;
const r2 = /[^\P{Lowercase_Letter}]/iu;

The r2 is a double negation and seems to be equivalent with r1. But in fact, r1 matches all lower- and upper-case ASCII letters, while r2 matches none. To illustrate how it works, pretend that we are only dealing with ASCII characters, not the entire Unicode character set, and r1 and r2 are specified as below:

const r1 = /[a-z]/iu;
const r2 = /[^A-Z]/iu;

Recall that case-insensitive matching happens by folding both the pattern and the input to the same case (see ignoreCase for more details). For r1, the character class a-z stays the same after case folding, while both upper- and lower-case ASCII string inputs are folded to lower-case, so r1 is able to match both "A" and "a". For r2, the character class A-Z is folded to a-z; however, ^ negates the match result, so that [^A-Z] in effect only matches upper-case strings. However, both upper- and lower-case ASCII string inputs are still folded to lower-case, causing r2 to match nothing.

In v mode, this behavior is fixed β€” [^...] also eagerly constructs the complement class instead of negating the match result. This makes [^\P{Lowercase_Letter}] and \p{Lowercase_Letter} are strictly equivalent.

Examples

Matching hexadecimal digits

The following function determines whether a string contains a valid hexadecimal number:

function isHexadecimal(str) {
  return /^[0-9A-F]+$/i.test(str);
}

isHexadecimal("2F3"); // true
isHexadecimal("beef"); // true
isHexadecimal("undefined"); // false

Using intersection

The following function matches Greek letters.

function greekLetters(str) {
  return str.match(/[\p{Script_Extensions=Greek}&&\p{Letter}]/gv);
}

// π†Š is U+1018A GREEK ZERO SIGN
greekLetters("Ο€π†ŠP0ι›ΆΞ±AΞ£"); // [ 'Ο€', 'Ξ±', 'Ξ£' ]

Using subtraction

The following function matches all non-ASCII numbers.

function nonASCIINumbers(str) {
  return str.match(/[\p{Decimal_Number}--[0-9]]/gv);
}

// π‘œΉ is U+11739 AHOM DIGIT NINE
nonASCIINumbers("π†Š0ι›Ά1πŸœπ‘œΉa"); // [ '𝟜', 'π‘œΉ' ]

Matching strings

The following function matches all line terminator sequences, including the line terminator characters and the sequence \r\n (CRLF).

function getLineTerminators(str) {
  return str.match(/[\r\n\u2028\u2029\q{\r\n}]/gv);
}

getLineTerminators(`
A poem\r
Is split\r\n
Into many
Stanzas
`); // [ '\r', '\r\n', '\n' ]

This example is exactly equivalent to /(?:\r|\n|\u2028|\u2029|\r\n)/gu or /(?:[\r\n\u2028\u2029]|\r\n)/gu, except shorter.

The most useful case of \q{} is when doing subtraction and intersection. Previously, this was possible with multiple lookaheads. The following function matches flags that are not one of the American, Chinese, Russian, British, and French flags.

function notUNSCPermanentMember(flag) {
  return /^[\p{RGI_Emoji_Flag_Sequence}--\q{πŸ‡ΊπŸ‡Έ|πŸ‡¨πŸ‡³|πŸ‡·πŸ‡Ί|πŸ‡¬πŸ‡§|πŸ‡«πŸ‡·}]$/v.test(flag);
}

notUNSCPermanentMember("πŸ‡ΊπŸ‡Έ"); // false
notUNSCPermanentMember("πŸ‡©πŸ‡ͺ"); // true

This example is mostly equivalent to /^(?!πŸ‡ΊπŸ‡Έ|πŸ‡¨πŸ‡³|πŸ‡·πŸ‡Ί|πŸ‡¬πŸ‡§|πŸ‡«πŸ‡·)\p{RGI_Emoji_Flag_Sequence}$/v, except perhaps more performant.

Specifications

Browser compatibility

See also