Sets and ranges [...] [Link]
info/regexp-character-sets-and-ranges
Buy EPUB/PDF
EN
→ Regular expressions
October 14, 2022
Sets and ranges [...]
characters or character classes inside square brackets […] mean to “search for any character among
Several
given”.
Sets
For instance, [eao] means any of the 3 characters: 'a' , 'e' , or 'o' .
That’s called a set. Sets can be used in a regexp along with regular characters:
1 // find [t or m], and then "op"
2 alert( "Mop top".match(/[tm]op/gi) ); // "Mop", "top"
Please note that although there are multiple characters in the set, they correspond to exactly one character in the
match.
So the example below gives no matches:
1 // find "V", then [o or i], then "la"
2 alert( "Voila".match(/V[oi]la/) ); // null, no matches
The pattern searches for:
● V,
● then one of the letters [oi] ,
● then la .
So there would be a match for Vola or Vila .
Ranges
Square brackets may also contain character ranges.
1 of 6 2/26/23, 22:59
Sets and ranges [...] [Link]
For instance, [a-z] is a character in range from a to z , and [0-5] is a digit from 0 to 5 .
In the example below we’re searching for "x" followed by two digits or letters from A to F :
1 alert( "Exception 0xAF".match(/x[0-9A-F][0-9A-F]/g) ); // xAF
Here [0-9A-F] has two ranges: it searches for a character that is either a digit from 0 to 9 or a letter from
A to F .
If we’d like to look for lowercase letters as well, we can add the range a-f : [0-9A-Fa-f] . Or add the flag i .
We can also use character classes inside […] .
For instance, if we’d like to look for a wordly character \w or a hyphen - , then the set is [\w-] .
Combining multiple classes is also possible, e.g. [\s\d] means “a space character or a digit”.
Character classes are shorthands for certain character sets
For instance:
● \d – is the same as [0-9] ,
● \w – is the same as [a-zA-Z0-9_] ,
● \s – is the same as [\t\n\v\f\r ] , plus few other rare Unicode space characters.
Example: multi-language \w
As the character class \w is a shorthand for [a-zA-Z0-9_] , it can’t find Chinese hieroglyphs, Cyrillic letters,
etc.
We can write a more universal pattern, that looks for wordly characters in any language. That’s easy with
Unicode properties: [\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}] .
Let’s decipher it. Similar to \w , we’re making a set of our own that includes characters with following Unicode
properties:
● Alphabetic ( Alpha ) – for letters,
● Mark ( M ) – for accents,
● Decimal_Number ( Nd ) – for digits,
● Connector_Punctuation ( Pc ) – for the underscore '_' and similar characters,
● Join_Control ( Join_C ) – two special codes 200c and 200d , used in ligatures, e.g. in Arabic.
An example of use:
2 of 6 2/26/23, 22:59
Sets and ranges [...] [Link]
1 let regexp = /[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]/gu;
2
3 let str = `Hi 你好 12`;
4
5 // finds all letters and digits:
6 alert( [Link](regexp) ); // H,i,你,好,1,2
Of course, we can edit this pattern: add Unicode properties or remove them. Unicode properties are covered in
more details in the article Unicode: flag "u" and class \p{...}.
Unicode properties aren’t supported in IE
Unicode properties p{…} are not implemented in IE. If we really need them, we can use library XRegExp.
Or just use ranges of characters in a language that interests us, e.g. [а-я] for Cyrillic letters.
Excluding ranges
Besides normal ranges, there are “excluding” ranges that look like [^…] .
They are denoted by a caret character ^ at the start and match any character except the given ones.
For instance:
● [^aeyo] – any character except 'a' , 'e' , 'y' or 'o' .
● [^0-9] – any character except a digit, the same as \D .
● [^\s] – any non-space character, same as \S .
The example below looks for any characters except letters, digits and spaces:
1 alert( "alice15@[Link]".match(/[^\d\sA-Z]/gi) ); // @ and .
Escaping in […]
Usually when we want to find exactly a special character, we need to escape it like \. . And if we need a
backslash, then we use \\ , and so on.
In square brackets we can use the vast majority of special characters without escaping:
● Symbols . + ( ) never need escaping.
● A hyphen - is not escaped in the beginning or the end (where it does not define a range).
● A caret ^ is only escaped in the beginning (where it means exclusion).
● The closing square bracket ] is always escaped (if we need to look for that symbol).
3 of 6 2/26/23, 22:59
Sets and ranges [...] [Link]
In other words, all special characters are allowed without escaping, except when they mean something for square
brackets.
A dot . inside square brackets means just a dot. The pattern [.,] would look for one of characters: either a
dot or a comma.
In the example below the regexp [-().^+] looks for one of the characters -().^+ :
1 // No need to escape
2 let regexp = /[-().^+]/g;
3
4 alert( "1 + 2 - 3".match(regexp) ); // Matches +, -
…But if you decide to escape them “just in case”, then there would be no harm:
1 // Escaped everything
2 let regexp = /[\-\(\)\.\^\+]/g;
3
4 alert( "1 + 2 - 3".match(regexp) ); // also works: +, -
Ranges and flag “u”
If there are surrogate pairs in the set, flag u is required for them to work correctly.
For instance, let’s look for [𝒳𝒴] in the string 𝒳 :
1 alert( '𝒳'.match(/[𝒳𝒴]/) ); // shows a strange character, like [?]
2 // (the search was performed incorrectly, half-character returned)
The result is incorrect, because by default regular expressions “don’t know” about surrogate pairs.
The regular expression engine thinks that [𝒳𝒴] – are not two, but four characters:
1. left half of 𝒳 (1) ,
2. right half of 𝒳 (2) ,
3. left half of 𝒴 (3) ,
4. right half of 𝒴 (4) .
We can see their codes like this:
4 of 6 2/26/23, 22:59
Sets and ranges [...] [Link]
1 for(let i=0; i<'𝒳𝒴'.length; i++) {
2 alert('𝒳𝒴'.charCodeAt(i)); // 55349, 56499, 55349, 56500
3 };
So, the example above finds and shows the left half of 𝒳 .
If we add flag u , then the behavior will be correct:
1 alert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳
The similar situation occurs when looking for a range, such as [𝒳-𝒴] .
If we forget to add flag u , there will be an error:
1 '𝒳'.match(/[𝒳-𝒴]/); // Error: Invalid regular expression
The reason is that without flag u surrogate pairs are perceived as two characters, so [𝒳-𝒴] is interpreted as
[<55349><56499>-<55349><56500>] (every surrogate pair is replaced with its codes). Now it’s easy to see
that the range 56499-55349 is invalid: its starting code 56499 is greater than the end 55349 . That’s the
formal reason for the error.
With the flag u the pattern works correctly:
1 // look for characters from 𝒳 to 𝒵
2 alert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴
Tasks
Java[^script]
We have a regexp /Java[^script]/ .
Does it match anything in the string Java ? In the string JavaScript ?
solution
Find the time as hh:mm or hh-mm
5 of 6 2/26/23, 22:59
Sets and ranges [...] [Link]
The time can be in the format hours:minutes or hours-minutes . Both hours and minutes have 2 digits:
09:00 or 21-30 .
Write a regexp to find time:
1 let regexp = /your regexp/g;
2 alert( "Breakfast at 09:00. Dinner at 21-30".match(regexp) ); // 09:00, 21-30
P.S. In this task we assume that the time is always correct, there’s no need to filter out bad strings like “45:67”.
Later we’ll deal with that too.
solution
Previous lesson Next lesson
Share Tutorial map
Comments
● If you have suggestions what to improve - please submit a GitHub issue or a pull request instead of
commenting.
● If you can't understand something in the article – please elaborate.
● To insert few words of code, use the <code> tag, for several lines – wrap them in <pre> tag, for
more than 10 lines – use a sandbox (plnkr, jsbin, codepen…)
© 2007—2023 Ilya Kantorabout the projectcontact usterms of usage
privacy policy
6 of 6 2/26/23, 22:59