Match Patterns With Regular Expressions in JavaScript
35 min read·Jan 1, 2025
In JavaScript, regular expressions, often abbreviated regex or regexp, are patterns used to match, validate, extract, and replace character combinations in strings.
Creating a regular expression
To create a new regular expression object, you can write a regex literal using the double slash syntax //:
const regex = /pattern/;
Where pattern is a string or regular expression.
Matching patterns in strings
Checking if a pattern exists
To check whether a pattern exists in a string, you can use the test() method of the regex object:
let exists = /pattern/.test(string);
Where:
/pattern/is a regular expression.stringis a string of characters.
This function returns true if the pattern exists, and false otherwise.
Listing all matching patterns
To list all the matched occurrences of the pattern in a string, you can use the match() method of the string:
let exists = string.match(/pattern/);
This function returns an array of matches or null if no matches are found.
Matching simple patterns
Simple patterns are substrings, often composed of characters and numbers, that you want to match exactly.
/pattern/
Note: When using simple patterns, the
test()method of a regex and theincludes()method of a string will produce the same result.
Example
Let's consider this script:
const regex = /the/;
const substring = 'the';
const string = 'The quick brown fox jumps over the lazy dog';
console.log(regex.test(string)); // true
console.log(string.includes(substring)); // true
When executed, it will test whether the substring "the" expressed in the form of a regular expression /the/ exists in the string string.
The wildcard character
The wildcard character . allows you to match any character within a pattern, except line terminators.
/patt.rn/
Note: If you want to match the actual dot character
., you will have to escape it using a backslash character\..
Example
Let's consider this regex:
const regex = /h.llo/;
console.log(regex.test('hello')); // true
console.log(regex.test('h3llo')); // true
console.log(regex.test('h@llo')); // true
When executed, it will match all specified strings as the wildcard character . can be substituted by any character, including 'e', '3', '@', etc.
Let's consider this regex:
const regex = /h\.llo/;
console.log(regex.test('h.llo')); // true
console.log(regex.test('hello')); // false
When executed, it will only match the first string "h.llo" as the wildcard character is escaped with a backslash character \, turning it into a literal dot character.
Character classes
Unlike wildcards, character classes allow you to specify a list of possible characters or range of characters.
Specifying a set of characters
To limit the match to a specific set of individual characters, you can use the square brackets syntax:
[<char>...]
Where <char>... is a list of characters (e.g., aeiou).
Example
Let's consider this regex:
const regex = /h[ae]llo/;
console.log(regex.test('hallo')); // true
console.log(regex.test('hello')); // true
console.log(regex.test('hxllo')); // false
When executed, it will match the strings "hallo" and "hello", but not "hxllo", as the regex only allows for the characters 'a', and 'e'.
Specifying a range of characters
To specify a range of characters or digits, you can place a dash character - between the first and last element:
[<start>-<end>...]
Where:
<start>is the first character or digit in the range.<end>is the last character or digit in the range.
Note: It is possible to list multiple ranges at once by concatenating them one after the other.
[<char>-<char><num>-<num>]
Example
Let's consider this regex, that matches all lowercase and uppercase letters from 'a' to 'h', then all digits from 1 to 8:
const regex = /[a-hA-H][1-8]/;
console.log(regex.test('a1')); // true
console.log(regex.test('F3')); // true
console.log(regex.test('i1')); // false
console.log(regex.test('X9')); // false
When executed, it will match the strings "a1" and "F3", however, it will not match the strings "i1" and "X9", as 'i', 'X', and 9 are out of the specified ranges.
Excluding a set of characters
To exclude a character set from the match, you can place a top caret character ^ at the beginning of the set:
[^<set>]
Example
Let's consider this regex, that excludes all lowercase and uppercase vowels as second character:
const regex = /t[^aeiouAEIOU]p./;
console.log(regex.test('type')); // true
console.log(regex.test('tape')); // false
When executed, it will match the string "type" as the 'y' character is not in the specified range, but not the string "tape" as the 'a' character is in the excluded range.
Character class escapes
A character class escape is a shorthand that represents a set of characters.
The most common ones are:
\d: a shorthand for[0-9].\w: a shorthand for[A-Za-z0-9_].\s: a shorthand for space, tab, and line terminators like\n,\r, etc.
Note: Their uppercase equivalent (i.e.
\D,\W,\S) are used to exclude those character sets from the match.
Disjunctions
The disjunction character | allows you to specify multiple alternative patterns at once.
pattern|pattern
Example
Let's consider this regex, that includes the strings "John" or "Jane":
const regex = /John|Jane/;
console.log(regex.test('John Doe')); // true
console.log(regex.test('Skyller Jane')); // true
console.log(regex.test('Jack Higgins')); // false
When executed, it will match the strings "Jack Doe" and "Skyller Jane", but not the string "Jack Higgins".
Boundary-type assertions
A boundary-type assertion is a special character used to define whether the pattern should match at the beginning and/or the end of a string.
To match a string from the beginning of a pattern, you can use the ^ character:
/^pattern/
To match a string from the end of a pattern, you can use the $ character:
/pattern$/
To match a string from both the beginning and the end of a pattern, you can combine both characters:
/^pattern$/
Example
Let's consider this regex, that matches the strings starting with the substring "+33":
const regex = /^\+33/;
console.log(regex.test('+33645874152')); // true
console.log(regex.test('+41645874152')); // false
Note: In regex, the
+character has a special meaning and must escaped with a backslash character\to lose its meaning.
Let's consider this regex, that matches the strings ending with the substring ".com" or ".dev":
const regex = /\.com|\.dev$/;
console.log(regex.test('johndoe@mail.com')); // true
console.log(regex.test('www.learnbackend.dev')); // true
Quantifiers
A quantifier is a symbol or expression used to specify the amount of times the preceding character, matcher, or group should be repeated:
?: matches the preceding item 0 or 1 times.*: matches the preceding item 0+ times.+: matches the preceding item 1+ times.
Example
Let's consider this regex, that matches the strings "color" or "colour":
const regex = /colou?r/;
console.log(regex.test('The color is blue')); // true
console.log(regex.test('The colour is black')); // true
Where u?: matches the 'u' character 0 or 1 times.
Let's consider this regex, that matches any valid URL:
const regex = /^https?:\/\/\w+\.[a-z]+$/;
console.log(regex.test('http://learnbackend.dev')); // true
console.log(regex.test('https://learnbackend.dev')); // true
console.log(regex.test('www.learnbackend.dev2')); // false
Where:
^: indicates the beginning of the expression.https?: matches the strings'http'or'https'.:\/\/: matches the string://.\w+: matches any alphanumeric and underscore character 1 or more times.\.: matches the dot character exactly 1 time.[a-z]+: matches any lowercase letter 1 or more times.$: indicates the end of the expression.
Matching patterns in "non-greedy" mode
By default quantifiers like * and + are "greedy", meaning that they try to match as much of the string as possible.
The ? character after the quantifier makes the quantifier "non-greedy", meaning that it will stop as soon as it finds a match.
For example, given a string like 'some <foo> <bar> new </bar> </foo> thing':
/<.*>/will match"<foo> <bar> new </bar> </foo>"./<.*?>/will only match"<foo>".
Matching a specific number of elements
To specify exactly, at least, or at most how many times the preceding character, matcher, or group should be repeated, you can use the curly brackets syntax {}:
{n}: matches the preceding item exactlyntimes.{n,}: matches the preceding item at leastntimes.{n,m}: matches the preceding item at leastntimes and at mostmtimes.
Example
Let's consider this regex, that matches any valid email address:
const regex = /^[a-zA-Z0-9._%+-]{1,64}@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
console.log(regex.test('john.doe@mail.com')); // true
console.log(regex.test('john_doe+test@mail.org')); // true
console.log(regex.test('john_doe+test@mail')); // false
console.log(regex.test('john_doe+testmail.org')); // false
Where:
^: indicates the beginning of the expression.[a-zA-Z0-9._%+-]{1,64}: matches any alphanumeric and special characters (.,_,%,+,-) between 1 and 64 times.@: matches the commercial at character exactly 1 time.[a-zA-Z0-9.-]+: matches any alphanumeric and special characters (.,-) at least 1 time.\.: matches the dot character exactly 1 time.[a-zA-Z]{2,}: matches any letter at least 2 times.$: indicates the end of the expression.
Groups
A group is a chunk of a regular expression contained in parenthesis () that allows you to group together multiple matchers as a single expression, and eventually capture them to reuse them later:
(pattern)
Capturing groups
To retrieve the substrings matched by the specified groups, you can either use the exec() method of a regular expression:
let matches = regex.exec(string);
Or the .match() method of a string:
let matches = string.match(regex);
Both methods return an array of matches, where:
matches[0]is the full match.matches[1+n]is the substring captured by thegroup(1+n).matches['index']is the start index of the match.matches['input']is the original string.matches['groups']is the list of named groups.
Or null if none of the specified groups match a substring.
Example
Let's consider this script, that extracts the various components of an email address:
const regex = /^([a-zA-Z0-9._%+-]{1,64})@([a-zA-Z0-9.-]+)\.([a-zA-Z]{2,})$/;
const matches = regex.exec('support@learnbackend.dev');
console.log(`Email address: '${matches[0]}'`);
console.log(`User name: '${matches[1]}'`);
console.log(`Domain name: '${matches[2]}'`);
console.log(`Domain extension: '${matches[3]}'`);
Where:
([a-zA-Z0-9._%+-]{1,64})captures the user name.([a-zA-Z0-9.-]+)captures the domain name.([a-zA-Z]{2,})captures the domain extension
Which will produce this output:
Email address: 'support@learnbackend.dev'
User name: 'support'
Domain name: 'learnbackend'
Domain extension: 'dev'
Non-capturing groups
To prevent the capture of groups and ultimately improve the performance of your script, you can prepend the group elements with the ?: expression:
(?:pattern)
Example
Let's consider this regex, that only extracts the domain name of an email address:
const regex = /^(?:https?:\/\/|www\.)([a-zA-Z0-9.-]+)\.[a-zA-Z]{2,}$/;
let matches;
let url;
url = 'http://learnbackend.dev';
matches = regex.exec(url);
console.log(`${url} => ${matches[1]}`);
url = 'https://learn-backend.dev';
matches = regex.exec(url);
console.log(`${url} => ${matches[1]}`);
url = 'www.l3arn.backend.dev';
matches = regex.exec(url);
console.log(`${url} => ${matches[1]}`);
Where:
(?:https?:\/\/|www\.): matches one of the strings'http://','https://', or'www.'without capturing them.([a-zA-Z0-9.-]+): matches any alphanumeric character and special characters (.,-) at least 1 time while capturing them.\.: matches the dot character exactly 1 time.[a-zA-Z]{2,}: matches any letter at least 2 times.
Which will produce this output:
http://learnbackend.dev => learnbackend
https://learn-backend.dev => learn-backend
www.l3arn.backend.dev => l3arn.backend
Lookahead and lookbehind assertions
The lookahead and lookbehind assertions allow you to match a pattern only if it's followed or preceded by another pattern, without including those patterns in the final match.
The lookahead assertion matches "x" only if "x" is followed by "y":
x(?=y)
The negative lookahead assertion matches "x" only if "x" is not followed by "y":
x(?!y)
The lookbehind assertion matches "x" only if "x" is preceded by "y":
(?<=y)x
The negative lookbehind assertion matches "x" only if "x" is not preceded by "y":
(?<!y)x
Example
Let's consider this regex, that matches any string that doesn't start with a digit and doesn't end with a letter:
const regex = /^(?!\d)(?!.*[a-zA-Z]$)\w+$/;
console.log(regex.test('A1B2C3')); // true
console.log(regex.test('1A2B3C')); // false
Where:
^(?!\d): ensures the string doesn't start with a digit.(?!.*[a-zA-Z]$): ensures the string doesn't end with a letter.\w+: ensures the string only contains alphanumeric characters and underscores.
Flags
Flags are used to modify the behavior of the regex pattern.
/pattern/flags
The most common flags are:
i: makes the regex case-insensitive, meaning it will match letters regardless of whether they are uppercase or lowercase.g: makes the regex find all matches rather than stopping after the first match.m: makes the^and$assertions match the start and end of each line within a string, rather than the start and end of the entire string.
Example
Let's consider this script, that matches all the email addresses in a multiline value, regardless of their case:
const message = `Hello,
Please contact us at Support@email.com for more details.
You can also reach out to helpdesk@company.dev.
Best,
The Team`;
const regex = /[a-zA-Z0-9._%+-]{1,64}@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/gi
const emails = message.match(regex) || [];
console.log(emails);
Which will produce this output:
[ 'Support@email.com', 'helpdesk@company.dev' ]
Summary
Here's a summary of what you've learned in this lesson:
- A regular expression (or regex) is a pattern used to match character combinations in strings.
- The
/pattern/expression or theRegExpclass are used to create new regular expressions. - The
regex.test()method is used to check whether a pattern exists in a string. - The
string.match()method is used to list all the occurrences of a pattern in a string. - The
.wildcard character matches any character within a pattern, except line terminators. - The
[...]character class matches any individual character or character ranges within the brackets. - The
[^...]character class excludes any character set from the match. - The
|disjunction allows to specify alternative patterns. - The
^patternboundary-type assertion matches from the beginning of the pattern. - The
pattern$boundary-type assertion matches from the end of the pattern. - The
?quantifier matches the preceding item 0 or 1 times. - The
*quantifier matches the preceding item 0 or more times. - The
+quantifier matches the preceding item 1 or more times. - The
{n}quantifier matches the preceding item exactlyntimes. - The
{n,}quantifier matches the preceding item at leastntimes. - The
{n,m}quantifier matches the preceding item at leastntimes and at mostmtimes. - The
(pattern)capturing group stores the matched patterns into an array. - The
(?:pattern)non-capturing group doesn't store the matched patterns. - The
x(?=y)lookahead assertion matches "x" only if "x" is followed by "y". - The
x(?!y)negative lookahead assertion matches "x" only if "x" is not followed by "y". - The
(?<=y)xlookbehind assertion matches** "x" only if "x" is preceded by "y". - The
(?<!y)xnegative lookbehind assertion** matches "x" only if "x" is not preceded by "y". - The
iflag makes the regex case-insensitive. - The
gflag makes the regex find all matches. - The
mflag makes the^and$assertions match the start and end of each line within a string.
Enjoying the courses?
I've made these courses completely free so anyone can learn from them. If they've helped you and you'd like to actively support the work behind BackendBrewery, you can leave a tip:
Support BackendBrewery