Skip to content
Topic 20 of 21
Theory

Regular expressions

Why it matters

String methods (includes, indexOf, split) search for specific text. But you often need to find text of a certain kind: "six digits in a row", "a word after #", "something that looks like an email". That's what regular expressions (regex for short) are for — a small language of patterns.

They are everywhere: checking form fields (email, phone, password), search and replace in a code editor, parsing logs, processing data from websites. You don't have to memorize regex, but every programmer should be able to read and write simple patterns.

How to write a pattern

A regular expression is written between two slashes. The test method tells you whether the string has a match:

const pattern = /cat/;
console.log(pattern.test("My cat is sleeping"));
console.log(pattern.test("My dog is sleeping"));

Output:

true
false

After the second slash you write flags: /cat/i — ignore case, /cat/g — find all matches, not just the first.

Character classes

Instead of a specific letter you can specify a kind of character:

Pattern What matches
\d a digit 0–9
\w a Latin letter, digit or _
\s a whitespace character: space, tab, line break
\D, \W, \S the opposite: not a digit, not a letter, not a space
. any character except a line break
[abc] one of the characters a, b, c
[a-z], [0-9] a range
[^0-9] anything except the listed ones
console.log(/\d\d:\d\d/.test("Meeting at 14:30"));
console.log(/[aeiou]/i.test("HELLO"));

Output:

true
true

A dot, brackets, +, ? and other characters have a special meaning. To find them literally, put a backslash before them: \. — exactly a dot, \( — exactly a bracket.

Quantifiers: how many times

Pattern How many
a+ one or more
a* zero or more
a? zero or one (optional)
a{3} exactly 3
a{2,4} from 2 to 4
a{2,} 2 or more
console.log("Prices: 5, 120 and 3500 somoni".match(/\d+/g));
console.log(/^colou?r$/.test("color"), /^colou?r$/.test("colour"));

Output:

["5","120","3500"]
true true

Start and end of the string

^ means the start of the string, $ — the end. Without them a pattern looks for a match anywhere inside the string — a common cause of bugs when checking forms:

console.log(/\d{4}/.test("year 2026!"));     // four digits somewhere inside
console.log(/^\d{4}$/.test("year 2026!"));   // the whole string is four digits
console.log(/^\d{4}$/.test("2026"));

Output:

true
false
true

Groups and "or"

Parentheses join part of a pattern into a group: you can apply a quantifier to it, and you can get its content separately. The vertical bar | means "or":

console.log(/^(ha)+$/.test("hahaha"));
console.log(/^(cat|dog)s?$/.test("dogs"));

const m = "Date: 2026-11-02".match(/(\d{4})-(\d{2})-(\d{2})/);
console.log(m[0]);            // the whole match
console.log(m[1], m[2], m[3]); // the groups in order

Output:

true
true
2026-11-02
2026 11 02

Groups can have names — then the code reads better:

const { groups } = "2026-11-02".match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
console.log(groups.day + "." + groups.month + "." + groups.year);

Output:

02.11.2026

Methods: find all matches

Method What it does
regex.test(str) is there a match → true / false
str.match(regex) without g: the first match with groups (or null); with g: an array of all matches
str.matchAll(regex) all matches with groups (needs the g flag)
str.replace(regex, replacement) replace (all of them with the g flag)
str.split(regex) split by a pattern

matchAll goes through all matches, and each has groups:

const text = "Ann: 5, Timur: 4, Lola: 5";
for (const m of text.matchAll(/([A-Za-z]+): (\d)/g)) {
  console.log(m[1] + " got " + m[2]);
}

Output:

Ann got 5
Timur got 4
Lola got 5

To get an array, spread the matches: [...text.matchAll(/…/g)].

Greediness

The quantifiers + and * are greedy: they take as many characters as possible. Sometimes that's not what you want:

const html = "<b>one</b> and <b>two</b>";
console.log(html.match(/<b>.+<\/b>/)[0]);    // greedy: from the first <b> to the last </b>
console.log(html.match(/<b>.+?<\/b>/)[0]);   // ? after + — as few as possible

Output:

<b>one</b> and <b>two</b>
<b>one</b>

This is the main lesson of regex: a pattern does exactly what is written. Describe the allowed characters as precisely as you can and test the pattern on several examples.

Replacing

In replace you can insert groups: $1, $2… — or pass a function that returns the replacement:

console.log("2026-11-02".replace(/(\d{4})-(\d{2})-(\d{2})/, "$3.$2.$1"));
console.log("   lots    of   spaces   ".trim().replace(/\s+/g, " "));
console.log("price 100 and 250".replace(/\d+/g, (n) => n * 2));

Output:

02.11.2026
lots of spaces
price 200 and 500

And splitting by a pattern — for example, by commas with any spaces around them:

console.log("apples ,pears,  plums".split(/\s*,\s*/));

Output:

["apples","pears","plums"]

How it works inside

The regex engine walks along the string from left to right and at each position tries to "fit" the pattern. Quantifiers are greedy by default: .+ first takes everything up to the end of the string and then gives characters back until the rest of the pattern matches. If you need to take as few as possible, put ? after the quantifier: .+?.

A regular expression with the g flag remembers where it stopped (the lastIndex property). That's why the same /…/g object can give different answers on repeated test calls. You don't need the g flag for test checks.

Common mistakes

  1. No ^ and $ in a check. /\d{6}/ lets "abc1234567xyz" through: there are six digits in it.
  2. An unescaped dot. /a.b/ also matches "a-b". A literal dot is \..
  3. A forgotten g flag. replace without g replaces only the first match, and matchAll without g throws an error.
  4. \w and non-Latin letters. \w is only Latin letters, digits and _. For Cyrillic letters write [А-Яа-яЁё].
  5. A pattern that's too complex. If a regex doesn't fit in your head, split the task into steps: first clean the string, then check it.

Cheat sheet

What How
Is there a match /pattern/.test(str)
All matches str.match(/pattern/g)
With groups [...str.matchAll(/(..)/g)]
Replace all str.replace(/pattern/g, "new")
Digit, letter, space \d, \w, \s
Not a digit \D
How many times +, *, ?, {n}, {n,m}
The whole string ^…$
Group, "or" (…), `a
Ignore case flag i

Postal code

Easy+10 XP

Write a function isPostalCode(text): return true if the string is exactly six digits and nothing else, otherwise false.

isPostalCode("734025")    → true
isPostalCode("73402")     → false   (five digits)
isPostalCode("734 025")   → false   (a space)

Connect the connector to solve tasks

Task code runs on your own computer — faster and more reliable than in the browser. This needs the free CodeWithMe connector app. You only connect it once.

Looking for the connector on this computer…

  1. 1
    Download the connector for your system (Windows, macOS or Linux).Download the connectorOn the download page pick the file with your system in its name, e.g. windows-amd64.
  2. 2Run it and choose a folder for your solutions. It will install Python and Node.js there by itself when needed.
  3. 3
    Enter below the 6-digit code shown in the connector window (“Code for CodeWithMe”). If the browser asks about local network access — allow it.

The connector is open source (on GitHub), works only on your computer and runs code only after you enter the pairing code.