Введение
Первым делом необходимо выяснить, что такое регулярные выражения. Регулярные выражения — это способ описания шаблона или правила. Их можно использовать, чтобы проверить, есть ли в строке совпадения с шаблоном. Плюс регулярных выражений в том, что их можно использовать во многих языках программирования.
Регулярные выражения — это не просто ещё одна часть языка JavaScript, вроде свойства или чего-то подобного. По сути, это небольшой самостоятельный язык, независимый от других. Ещё одно достоинство регулярных выражений: они крайне удобны, так как позволяют выполнять сложные манипуляции со строками и при этом экономить код.
Недостаток регулярных выражений в том, что часто они выглядят странно и даже пугающе. Особенно это касается более сложных шаблонов. По этой причине многие программисты не горят желанием их изучать. И это неправильно. Регулярные выражения могут быть мощным инструментом, позволяющим писать меньше кода. Надеюсь, это руководство поможет вам преодолеть страх перед их изучением.
Как создавать регулярные выражения?
Если с помощью JavaScript вы хотите создать регулярное выражение (описать шаблон), есть два способа это сделать.
Конструктор регулярных выражений
Первый способ — использование конструктора. Это громкое слово на самом деле означает функцию-конструктор объекта RegExp. Конструктор принимает два параметра. Первый — шаблон, который вы хотите описать. Это обязательный параметр. В конце концов, зачем вообще создавать регулярное выражение, если нет шаблона?
Второй параметр — строка с флагами (flags
). Не волнуйтесь, скоро мы с ними познакомимся. Этот параметр необязательный. Стоит запомнить одно: после создания регулярного выражения флаги уже нельзя будет добавить или убрать. Поэтому, если хотите использовать флаг, добавьте его на этапе создания выражения.
// Синтаксис конструктора регулярных выражений
new RegExp(pattern[, flags])
// Создание регулярного выражения
// с помощью конструктора
// без флагов
const myPattern = new RegExp('[a-z]')
// Создание регулярного выражения
// с помощью конструктора
// с одним флагом
const myPattern = new RegExp('[a-z]', 'g')
Литерал регулярных выражений
Второй способ — использование литерала. Как и конструктор, литерал регулярных выражений состоит из двух частей. Первая часть — это описываемый шаблон. Он заключается в слэши (//
). После закрывающего слэша идёт вторая часть — флаги. Они необязательны.
// Синтаксис литерала регулярных выражений
/pattern/flags
// Создание регулярного выражения
// с помощью литерала
// без флагов
const myPattern = /[a-z]/
// Создание регулярного выражения
// с помощью литерала
// с одним флагом
const myPattern = /[a-z]/g
Примечание: два слэша в литерале регулярных выражений используются для того, чтобы заключить в них шаблон. Если ваш шаблон предполагает использование ещё одного или нескольких прямых слэшей, их необходимо экранировать обратным слэшем (\
), то есть \ /.
Конструктор или литерал?
Конструктор и литерал выполняют одну функцию, но есть одно важное различие. Регулярное выражение, созданное при помощи конструктора, компилируется при выполнении программы, литерал — на этапе загрузки скрипта. Это значит, что литерал нельзя изменить динамически, в то время как конструктор — можно.
Таким образом, если вам нужно (или может понадобиться) изменить шаблон на лету, создавайте регулярное выражение с помощью конструктора. Также конструктор будет лучшим решением, если шаблон нужно создавать динамически. С другой стороны, если вам не понадобится менять или создавать шаблон, вы можете воспользоваться литералом.
Как использовать регулярные выражения с методами объекта RegExp?
Прежде чем приступить к созданию шаблонов, давайте кратко рассмотрим, как они используются. С помощью описанных ниже методов мы сможем в дальнейшем применять разные способы создания шаблонов.
test()
Для работы с регулярными выражениями есть несколько методов. Простейший из них — test()
. При использовании этого метода необходимо передать функции проверяемую строку в качестве аргумента. В результате метод возвращает булево значение: true
— если в строке есть совпадения с шаблоном, false
— если совпадений нет.
// Синтаксис метода test()
// /шаблон/.test('проверяемый текст')
// Проверка строки,
// когда test() не находит совпадений
myPattern.test('There was a cat and dog in the house.')
// false
// Создание переменной,
// которой присваивается текст для проверки
const myString = 'The world of code.'
// Создание шаблона
const myPattern = /code/
// Проверка текста с помощью шаблона,
// когда test() находит совпадение
myPattern.test(myString)
// true
exec()
Ещё один метод, который можно использовать — exec()
. Если есть совпадение, метод exec()
возвращает массив. Массив содержит в себе информацию об используемом шаблоне, позиции, на которой было найдено совпадение, проверяемом тексте и наборах. Если совпадений нет, метод exec()
возвращает null
.
Необходимо запомнить одну вещь: метод exec()
возвращает информацию только о первом найденном в тексте совпадении. Он прекращает работу после нахождения первого совпадения. Не используйте этот метод, если хотите получить множественные совпадения.
// Синтаксис метода exec()
// /шаблон/.exec('проверяемый текст')
// Создание строки для проверки
const myString = 'The world of code is not full of code.'
// Описание шаблона
const myPattern = /code/
// Использование exec() для проверки текста,
// когда exec() находит совпадение
myPattern.exec(myString)
// [
// 'code',
// index: 13,
// input: 'The world of code is not full of code.',
// groups: undefined
// ]
// Описание другого шаблона
const myPatternTwo = /JavaScript/
// Использование exec() с новым шаблоном для новой проверки текста,
// когда exec() не находит совпадений
myPatternTwo.exec(myString)
// null
Как использовать регулярные выражения с методами объекта String?
test()
и exec()
— не единственные методы, которые можно использовать для поиска совпадений строки с шаблоном. Есть ещё search()
, match()
и matchAll()
. Эти методы принадлежат не объекту RegExp, а строкам. Несмотря на это, они позволяют применять регулярные выражения.
Чтобы использовать эти методы, нужно инвертировать синтаксис. Поскольку методы вызываются на строках, а не на шаблонах, в качестве аргумента надо передать не строку, а шаблон.
search()
Первый метод, search()
, ищет в строке заданный шаблон. Если он находит совпадение, то возвращает позицию в строке, где это совпадение начинается. Если совпадения нет, возвращается -1
. Нужно запомнить, что метод search()
возвращает позицию только первого совпадения. После нахождения первого совпадения он прекращает работу.
// Синтаксис метода search()
// 'проверяемый текст'.search(/шаблон/)
// Создание текста для проверки
const myString = 'The world of code is not full of code.'.
// Описание шаблона
const myPattern = /code/
// Использование search() для поиска
//совпадения строки с шаблоном,
//когда search() находит совпадение
myString.search(myPattern)
// -13
// Вызов search() прямо на строке,
// когда search() не находит совпадений
'Another day in the life.'.search(myPattern)
// -1
match()
match()
— второй метод объекта String, который позволяет использовать регулярные выражения. Он работает аналогично exec()
: при нахождении совпадения метод match()
возвращает массив с информацией об использованном шаблоне, позиции в строке, на которой было найдено совпадение, проверяемом тексте и наборах.
Так же как и exec()
, match()
возвращает null
при отсутствии совпадений. При использовании метода match()
с флагом g
для поиска всех совпадений с шаблоном возвращается массив из всех совпадений.
// Синтаксис метода match()
// 'проверяемый текст'.match(/шаблон/)
// Создание текста для проверки
const myString = 'The world of code is not full of code.'
// Описание шаблона
const myPattern = /code/
// Использование match() для поиска совпадения в тексте
myString.match(myPattern)
// [
// 'code',
// index: 13,
// input: 'The world of code is not full of code.',
// groups: undefined
// ]
'Another day in the life.'.match(myPattern)
// null
// Использование match() для поиска всех совпадений
// Создание текста для проверки
const myString = 'The world of code is not full of code.'
// Описание шаблона
const myPattern = /code/g // добавление флага 'g'
// Использование match() для поиска совпадения в тексте
myString.match(myPattern)
// [ 'code', 'code' ]
matchAll()
Подобно методу match()
, matchAll()
возвращает все совпадения при использовании флага g
в шаблоне. Однако работает он по-другому. Метод matchAll()
возвращает объект RegExp String Iterator
. Есть несколько способов извлечь из него все совпадения.
Во-первых, можно пройтись по объекту циклом for…of
и вернуть или записать все совпадения. Также можно использовать Array.from()
, чтобы создать массив из содержимого объекта, или оператор spread, который даст точно такой же результат, как и Array.from()
.
// Синтаксис метода match()
// 'проверяемый текст'.match(/шаблон/)
// Создание текста для проверки
const myString = 'The world of code is not full of code.'
// Описание шаблона
const myPattern = /code/g
// Обратите внимание, что используется флаг 'g'
// Использование matchAll() для поиска совпадений в тексте
const matches = myString.matchAll(myPattern)
// Использование цикла for...of для получения всех совпадений
for (const match of matches) {
console.log(match)
}
// [
// [
// 'code',
// index: 13,
// input: 'The world of code is not full of code.',
// groups: undefined
// ],
// [
// 'code',
// index: 33,
// input: 'The world of code is not full of code.',
// groups: undefined
// ]
// ]
// Использование Array.from() для получения всех совпадений
const matches = Array.from(myString.matchAll(myPattern))
// [
// [
// 'code',
// index: 13,
// input: 'The world of code is not full of code.',
// groups: undefined
// ],
// [
// 'code',
// index: 33,
// input: 'The world of code is not full of code.',
// groups: undefined
// ]
// ]
// Использование оператора spread для получения всех совпадений
const matches = [...myString.matchAll(myPattern)]
// [
// [
// 'code',
// index: 13,
// input: 'The world of code is not full of code.',
// groups: undefined
// ],
// [
// 'code',
// index: 33,
// input: 'The world of code is not full of code.',
// groups: undefined
// ]
// ]
Как создавать простые шаблоны?
Вы узнали, как создавать и использовать регулярные выражения. Теперь давайте рассмотрим процесс создания шаблонов. Простейший способ составлять регулярные выражения —применение простых шаблонов. Это значит, что необходимо создать строку с особым текстом, а затем проверить, имеет ли какая-то другая строка совпадения с этим текстом.
// Создание простого шаблона
// с использованием литерала регулярного выражения
const myPattern = /JavaScript/
// Проверка строки на совпадения с шаблоном
myPattern.test('One of the most popular languages is also JavaScript.')
// true
// Проверка строки на совпадения с шаблоном
myPattern.test('What happens if you combine Java with scripting?')
// false
Как создавать сложные шаблоны со специальными символами?
До сих пор мы использовали регулярные выражения из простых шаблонов. Их может быть достаточно для каких-то простых задач. Однако для сложных случаев такие выражения не подойдут. Настало время создавать и использовать более сложные шаблоны. Здесь в игру вступают специальные символы. Давайте рассмотрим те из них, которые наиболее часто используются в регулярных выражениях.
Символьные классы
Символьные классы — это своеобразные сокращения для разных типов символов. К примеру, есть свои классы для букв, цифр, пробелов и т. д.
/* Символьный класс - Значение */
. - любой символ, кроме первой строки
\d - одноразрядное число (то же, что и [0-9])
\w - отдельный буквенно-числовой словообразующий символ из латинского алфавита, включая подчёркивание (то же, что и [A-Za-z0-9_)
\s - отдельный символ разделителя (пробел, табуляция и т. п.) (то же, что и [\t\r\n\v\f])
\D - отдельный нечисловой символ (то же, что и[^0-9])
\W - отдельный несловообразующий символ из латинского алфавита (то же, что и [^A-Za-z0-9_])
\S - отдельный символ, который не является разделителем (то же, что и [^\t\r\n\v\f]).
Примеры:
// . - любой символ, кроме первой строки
const myPattern = /./
console.log(myPattern.test(''))
// false
console.log(myPattern.test('word'))
// true
console.log(myPattern.test('9'))
// true
// \d - одноразрядное число
const myPattern = /\d/
console.log(myPattern.test('3'))
// true
console.log(myPattern.test('word'))
// false
// \w - отдельный буквенно-числовой словообразующий символ
const myPattern = /\w/
console.log(myPattern.test(''))
// false
console.log(myPattern.test('word'))
// true
console.log(myPattern.test('9'))
// true
// \s - отдельный символ разделителя
const myPattern = /\s/
console.log(myPattern.test(''))
// false
console.log(myPattern.test(' '))
// true
console.log(myPattern.test('foo'))
// false
// \D - отдельный нечисловой символ
const myPattern = /\D/
console.log(myPattern.test('Worm'))
// true
console.log(myPattern.test('1'))
// false
// \W - отдельный несловообразующий символ
const myPattern = /\W/
console.log(myPattern.test('Worm'))
// false
console.log(myPattern.test('1'))
// false
console.log(myPattern.test('*'))
// true
console.log(myPattern.test(' '))
// true
// \S - отдельный символ, который не является разделителем
const myPattern = /\S/
console.log(myPattern.test('clap'))
// true
console.log(myPattern.test(''))
// false
console.log(myPattern.test('-'))
// true
Операторы контроля
Ещё один вид специальных символов — это операторы контроля. Такие символы позволяют описывать шаблоны с границами, то есть указывать, где начинается или заканчивается слово или строка. С помощью операторов контроля также можно создавать более сложные шаблоны, такие как опережающие проверки, ретроспективные проверки и условные выражения.
/* Оператор контроля - Значение */
^ - начало строки (последующее регулярное выражение должно совпадать с началом проверяемой строки).
$ - конец строки (последующее регулярное выражение должно совпадать с концом проверяемой строки).
\b - граница слова, то есть его начало или конец.
\B - несловообразующая граница.
x(?=y) - опережающая проверка. Совпадение с "x", только если за "x" следует "y".
x(?!y) - негативная опережающая проверка. Совпадение с "x", только если за "x" не следует "y".
(?<=y)x - ретроспективная проверка. Совпадение с "x", только если перед "x" стоит "y".
(?<!y)x - негативная ретроспективная проверка. Совпадение с "x", только если перед "x" не стоит "y".
Примеры:
// ^ - Начало строки
const myPattern = /^re/
console.log(myPattern.test('write'))
// false
console.log(myPattern.test('read'))
// true
console.log(myPattern.test('real'))
// true
console.log(myPattern.test('free'))
// false
// $ - Конец строки
const myPattern = /ne$/
console.log(myPattern.test('all is done'))
// true
console.log(myPattern.test('on the phone'))
// true
console.log(myPattern.test('in Rome'))
// false
console.log(myPattern.test('Buy toner'))
// false
// \b - Граница слова
const myPattern = /\bro/
console.log(myPattern.test('road'))
// true
console.log(myPattern.test('steep'))
// false
console.log(myPattern.test('umbro'))
// false
// Или
const myPattern = /\btea\b/
console.log(myPattern.test('tea'))
// true
console.log(myPattern.test('steap'))
// false
console.log(myPattern.test('tear'))
// false
// \B - Несловообразующая граница
const myPattern = /\Btea\B/
console.log(myPattern.test('tea'))
// false
console.log(myPattern.test('steap'))
// true
console.log(myPattern.test('tear'))
// false
// x(?=y) - Опережающая проверка
const myPattern = /doo(?=dle)/
console.log(myPattern.test('poodle'))
// false
console.log(myPattern.test('doodle'))
// true
console.log(myPattern.test('moodle'))
// false
// x(?!y) - Негативная опережающая проверка
const myPattern = /gl(?!u)/
console.log(myPattern.test('glue'))
// false
console.log(myPattern.test('gleam'))
// true
// (?<=y)x - Ретроспективная проверка
const myPattern = /(?<=re)a/
console.log(myPattern.test('realm'))
// true
console.log(myPattern.test('read'))
// true
console.log(myPattern.test('rest'))
// false
// (?<!y)x - Негативная ретроспективная проверка
const myPattern = /(?<!re)a/
console.log(myPattern.test('break'))
// false
console.log(myPattern.test('treat'))
// false
console.log(myPattern.test('take'))
// true
Квантификаторы
Квантификаторы используются, когда необходимо указать количество символов или выражений, по которым производится сопоставление.
/* Квантификатор - Значение */
* - 0 или более совпадений с предшествующим выражением.
+ - 1 или более совпадений с предшествующим выражением.
? - Предшествующее выражение необязательно (то есть совпадений 0 или 1).
x{n} - "n" должно быть целым положительным числом. Количество вхождений предшествующего выражения "x" равно "n".
x{n, } - "n" должно быть целым положительным числом. Количество вхождений предшествующего выражения "x" равно, как минимум, "n".
x{n, m} - "n" может быть равно 0 или целому положительному числу. "m" - целое положительное число. Если "m" > "n", количество вхождений предшествующего выражения "x" равно минимум "n" и максимум "m".
Примеры:
// * - 0 или более совпадений с предшествующим выражением
const myPattern = /bo*k/
console.log(myPattern.test('b'))
// false
console.log(myPattern.test('bk'))
// true
console.log(myPattern.test('bok'))
// true
// + - 1 или более совпадений с предшествующим выражением
const myPattern = /\d+/
console.log(myPattern.test('word'))
// false
console.log(myPattern.test(13))
// true
// ? - Предшествующее выражение необязательно, совпадений 0 или 1
const myPattern = /foo?bar/
console.log(myPattern.test('foobar'))
// true
console.log(myPattern.test('fooobar'))
// false
// x{n} - Количество вхождений предшествующего выражения "x" равно "n"
const myPattern = /bo{2}m/
console.log(myPattern.test('bom'))
// false
console.log(myPattern.test('boom'))
// true
console.log(myPattern.test('booom'))
// false
// x{n, } - Количество вхождений предшествующего выражения "x" равно, как минимум, "n"
const myPattern = /do{2,}r/
console.log(myPattern.test('dor'))
// false
console.log(myPattern.test('door'))
// true
console.log(myPattern.test('dooor'))
// true
// x{n, m} - Количество вхождений предшествующего выражения "x" равно минимум "n" и максимум "m"
const myPattern = /zo{1,3}m/
console.log(myPattern.test('zom'))
// false
console.log(myPattern.test('zoom'))
// true
console.log(myPattern.test('zooom'))
// true
console.log(myPattern.test('zoooom'))
// false
Наборы и диапазоны
Наборы и диапазоны могут пригодиться, когда нужно указать специальные символы набора или их диапазон.
/* Набор или диапазон - Значение */
[abc] - любой один из символов в скобках.
[^abc] — любой символ, за исключением символов в скобках.
[a-z] - любой символ в диапазоне от "a" до "z".
[^a-z] - любой символ не из диапазона от "a" до "z".
(x) - "x", значение запоминается для дальнейшего использования.
(?<name>x) - создание именованной скобочной группы, к которой можно обратиться по указанному имени.
(?:x) - "x", значение не запоминается, поэтому совпадение невозможно извлечь из итогового массива элементов.
Примеры:
// [abc] - Любой один из символов в скобках.
const myPattern = /[aei]/
console.log(myPattern.test('aei'))
// true (есть a, e, i)
console.log(myPattern.test('form'))
// false (нет a, e или i)
// [^abc] - Любой символ, за исключением символов в скобках.
const myPattern = /[^aei]/
console.log(myPattern.test('aei'))
// false (нет других символов, кроме a, e и i)
console.log(myPattern.test('form'))
// true (есть другие символы, кроме a, e и i)
// [a-z] - Любой символ в диапазоне от "a" до "z".
const myPattern = /[b-g]/
console.log(myPattern.test('bcd'))
// true (есть символы в диапазоне от 'b' до 'g')
console.log(myPattern.test('jklm'))
// false (нет символов в диапазоне от 'b' до 'g')
// [^a-z] - Любой символ не из диапазона от "a" до "z".
const myPattern = /[^b-g]/
console.log(myPattern.test('bcd'))
// false (нет других символов, кроме входящих в диапазон от 'b' до 'g')
console.log(myPattern.test('jklm'))
// true (есть другие символы, кроме входящих в диапазон от 'b' до 'g')
// (x) - "x", значение запоминается для дальнейшего использования.
const myPattern = /(na)da/
console.log(myPattern.test('nadana'))
// true - запоминает и использует совпадение 'na' из первого выражения в скобках.
console.log(myPattern.test('nada'))
// false
// (?<name>x) - Создание именованной скобочной группы, к которой можно обратиться по указанному имени.
const myPattern = /(?<foo>is)/
console.log(myPattern.test('Work is created.'))
// true
console.log(myPattern.test('Just a text'))
// false
// (?:x) - "x", значение не запоминается.
const myPattern = /(?:war)/
console.log(myPattern.test('warsawwar'))
// true
console.log(myPattern.test('arsaw'))
// false
Альтернация
Альтернация позволяет находить соответствие, по крайней мере, одному из нескольких выражений.
/* Альтернация - Значение */
| - выражение до или после символа |, как в булевом ИЛИ (||).
Примеры:
// | - Выражение до или после символа |
const myPattern = /(black|white)swan/
console.log(myPattern.test('black swan'))
// true
console.log(myPattern.test('white swan'))
// true
console.log(myPattern.test('gray swan'))
// false
Флаги
Флаги — последний тип символов, которые используются в регулярных выражениях. С помощью флагов можно легко расширить функционал шаблонов. К примеру, флаги позволяют игнорировать регистр букв, чтобы шаблон находил совпадения и в верхнем, и в нижнем регистрах, находить множественные совпадения и совпадения в многострочном тексте и т. д.
/* Флаг - Значение */
g – Глобальный поиск, не останавливается после нахождения первого совпадения.
i – Игнорирование регистра (соответствует верхнему и нижнему регистрам).
s - Точка (.) соответствует переводу на новую строку.
m – Многострочный ввод, начинается с "^" и заканчивается "$" (начало и конец каждой строки).
Примеры:
// флаг g - Глобальный поиск
const myPattern = /xyz/g
console.log(myPattern.test('One xyz and one more xyz'))
// true
// флаг i - Игнорирование регистра
const myPattern = /xyz/i
console.log(myPattern.test('XyZ'))
// true - регистр символов не имеет значения при нечувствительном к регистру поиске.
// флаг s - Точка (.) соответствует переводу на новую строку
const myPattern = /foo.bar/s
console.log(myPattern.test('foo\nbar'))
// true
console.log(myPattern.test('foo bar'))
// true
console.log(myPattern.test('foobar'))
// false
Заключение
Понимать и изучать регулярные выражения может быть непросто. Однако с помощью их короткого кода можно решать очень сложные задачи. И это определённо стоит стараний. Надеюсь, это руководство помогло вам разобраться в работе и способах применения регулярных выражений.
Читайте также:
- Чистый код JavaScript - объекты и классы
- Хроники нового текстового редактора - от замысла до реализации
- Двоичное дерево поиска: вставка значения с использованием JavaScript
Перевод статьи Alex Devero: Introduction to Regular Expressions in JavaScript