Регулярные выражения в JavaScript, как правило, не являются простой темой, т.к. вариантов использования этих выражений великое множество + есть несколько ситуаций, при которых эти регулярные выражения нужно использовать.
Мы можем использовать регулярные выражения:
- для замены одной строки на другую
- для получения некоторых параметров из строк
- в условных конструкциях для проверки строк на соответствие определенным параметрам
Если вы ищете информацию по регулярным выражениям, то, скорей всего вы не новичок в программировании и уже работали со строками в JS. Если же вы новичок, то, возможно, вы найдете для себя здесь решение какой-то из проблем. Если же нет - что ж, придется покопаться в теме посерьезней.
В этой статье мы рассмотрим такие вопросы:
- Coздание регулярного выражения
- Методы для работы с регулярными выражениями
- Заменяем одну строку на другую
- Метод split()
- Проверка строк на соответствие определенным параметрам
- Получение значений из результата регулярного выражения
- Полезные сервисы
- Где еще найти примеры регулярных выражений
Coздание регулярного выражения
Описать регулярное выражение, можно, используя литерал в виде строки без кавычек, например:
| 1 | const re1 = /[А-Яа-яЁё]/ig |
либо специальный объект RegExp (от англ. Regular Expression, что в переводе и значит регулярное выражение):
| 1 | const re2 = new RegExp("[А-Яа-яЁё]", "ig"); |
Теперь нужно разобраться, каким образом мы можем составлять регулярные выражения. Дело в том, что для создания различных регулярных выражений нам понадобятся классы символов,
Методы для работы с регулярными выражениями
Для работы с регулярными выражениями в JavaScript существуют 2 группы методов: первая относится к объекту String, и вызывая метод, вы указываете сначала строку, в которой нужно использовать регулярное выражение, а в скобках - само регулярное выражение. Вторая группа - это методы объекта RegExp, для которых сначала указывается выражение, а в скобках - строка для проверки соответствий.
Методы объекта String
| Метод | Синтаксис | Назначение |
|---|---|---|
match() | str.match(regexp) | Метод String, который выполняет поиск совпадения в строке и возвращает массив данных, либо null, если совпадения отсутствуют. |
search() | str.match(regexp) | Метод String, который тестирует строку на совпадения. Он возвращает индекс (число, или позицию) начала совпадения, или -1 если совпадений не будет найдено. |
replace() | str.replace(regexp, newstr) | Метод String, который выполняет поиск совпадения в строке, и заменяет совпавшую подстроку другой подстрокой, переданной как аргумент в этот метод. |
split() | str.split(regexp|delimeter) | Метод String, который использует регулярное выражение или фиксированную строку чтобы разбить строку на массив подстрок. |
Методы объекта RegExp
| Метод | Синтаксис | Назначение |
|---|---|---|
exec() | regexObj.exec(str) | Метод RegExp, который выполняет поиск совпадения в строке и возвращает массив данных. |
test() | regexObj.test(str) | Метод RegExp, который тестирует совпадение в строке и возвращает истину (true) или ложь (false). |
Если говорить кратко о всех методах, то regexObj.test(str) или str.search(regExp) используются для того, чтобы проверить, есть ли в строке часть, соответствующая шаблону. Чтобы получить развернутую информацию о вхождении нужных шаблонов, нужно использовать методы regexObj.exec(str) или str.match(regExp) . Они работают медленнее, но возвращают массивы с данными о вхождениях регулярного выражения, либо null, если вхождений нет.
Метод split() разделит вам строку на элементы массива, которые вы опять сможете превратить в новую строку методом join(''), а замена строк методом replace() - это частая операция и вариантов использования у нее множество.
Заменяем одну строку на другую
Метод replace()
Для замены одной строки на другую, как правило используют метод объекта String replace() или replaceAll()
Синтаксис:
| 1 | str.replace(regexp|substr, newSubStr|function[, flags]) |
На самом деле это один из самых используемых методов, но и с самым большим разнообразием вариантов. В качестве параметров вы можете передавать:
regexp|substr- регулярное выражение или строку;newSubStr|function- новую строку или функцию, причем пр регулярном выражении со скобочными группами будет много интересных вариантов;flags- не обязательный параметр, который не работает в ядре v8 (движок JavaScript в Chrome и NodeJs), поэтому лучше располагать флаги в самом регулярном выражении.
Важное замечание: метод replace() не изменяет исходную строку, для которой он вызывается. Он просто возвращает новую строку.
Пример: замена дат на понятный формат:
| 1 2 3 4 5 6 7 8 | function replaceDate(dateStr){ let date = new Date(dateStr).toDateString(); console.log(date);// Mon Aug 27 2018 и Mon Jan 04 2021 return date.replace(/(\w+) (\w+) (\w+) (\w+)/,"$3 of $2, $4"); } console.log(replaceDate('2018-08-27T01:10:00')); //27 of Aug, 2018 console.log(replaceDate('2021-01-04T12:03:00')); //04 of Jan, 2021 |
Используем функцию внутри метода replace():
| 1 2 3 4 5 | let camel = (str) => str.replace(/(-[a-z])/g, str => str.slice(1).toUpperCase()); document.write('<p> list-style-type => '+camel('list-style-type')+'</p>') document.write('<p> font-size => '+camel('font-size')+'</p>') document.write('<p> border-top-color => '+camel('border-top-color')+'</p>') |
Здесь вложенная функция str => str.slice(1).toUpperCase() ищет первый символ в нижнем регистре после знака "-" и возвращает его в верхнем регистре.
Давайте посмотрим на результат:
Можно также использовать группы со скобками для замены текста ссылки с номером телефона, например, в таком виде:
| 1 2 3 4 5 6 7 8 | <p><a href="tel:+38(099) 357-4689" id="phoneNumLink">+38(099) 357-4689</a></p> <script> let phoneNumLink=document.getElementById( 'phoneNumLink'), phoneNum = phoneNumLink.textContent; let newNumber = phoneNum.replace(/(\+\d+)\((\d+)\)\s(\d+)-(\d+)/g, '$1$2$3$4') ; phoneNumLink.href="tel:"+newNumber; document.write(`Исходная строка: <b>${phoneNum }</b><br>Измененная строка: <b>${ newNumber }</b>`); </script> |
Обычно такая необходимость возникает, если вы задаете номер телефона в настройках какой-нибудь CMS, например, в WordPress.
Результат:
Звоните нам по телефону +38(099) 357-4689
Метод replaceAll()
Метод replaceAll() предназначен для замены всех вхождений искомого шаблона в строке. Он возвращает новую строку со всеми совпадениями, измененную в соответствии с шаблоном.
Для начала рассмотрим пример, в котором нам надо заменить все вхождения, связанные с размером изображений. Для этого используем изображения с ресурса picsum.photos, для вставки изображений с которого можно указать размеры фотографий. Аналогичным образом можно вставлять фото с unsplash.com.
Все изображения собраны в массиве и имеют размеры 400x400px. Мы будем добавлять их в разметку с помощь функции printImages() и метода массивов reduce():
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | <div id="picHolder"></div> <button onclick="changeOneImage()">Заменить только ширину</button> <button onclick="changeImages()">Заменить ширину и высоту</button> <button onclick="printImages()">Вернуться к исходным картинкам</button> <script> let images = ['https://picsum.photos/id/13/400/400', 'https://picsum.photos/id/18/400/400', 'https://picsum.photos/id/29/400/400', 'https://picsum.photos/id/46/400/400']; function printImages(){ picHolder.innerHTML = images.reduce( (str, next) => str+`<img src="${next}" alt="Picture">`, ''); } printImages(); function changeOneImage(){ picHolder.innerHTML = images.reduce( (str, next) => str+`<img src="${next.replace('400', '200')}" alt="Picture">`, ''); } function changeImages(){ picHolder.innerHTML = images.reduce( (str, next) => str+`<img src="${next.replaceAll('400', '200')}" alt="Picture">`, ''); } </script> |
Для использования метода replace() мы будем использовать функцию changeOneImage(), а для метода replaceAll() - changeImages(). Вставить изображения из начального массива можно с помощью функции printImages() и клика по соответствующей кнопке.
Посмотреть результат:
See the Pen Mehod replaceAll() by Elen (@ambassador) on CodePen.
Метод split()
Чаще всего метод split() используется для преобразования строки в массив. Например, для строки complex = 'Разминка;Растяжка;Наклоны;Отжимания;Расслабление' можно выполнить быстрое преобразование составляющих ее строк в список.
| 1 2 | let complex = 'Разминка;Растяжка;Наклоны;Отжимания;Расслабление'; document.write("<ol><li>"+ complex.split(";").join("<li>") + "</ol>"); |
Метод split() позволяет указать лимит количества элементов. Для этого нужно указать второй параметр в виде цифры:
| 1 2 | let jsStr = "I like JavaScript"; console.log(jsStr.split(' ', 2));// ["I","like"] |
Однако, возможна ситуация, когда символов для разбиения должно быть больше. Например, у нас есть необходимость разбить текст на части по предложениям, которые заканчиваются точкой, вопросительным или восклицательным знаком.
| 1 2 3 | let someText = "Hello! How are you? This is Robert. I'd like to introduce myself to you."; let sentences = someText.split(/[!?.]/); console.log(sentences); // ['Hello', ' How are you', ' This is Robert', " I'd like to introduce myself to you", ''] |
Если несколько поменять регулярное выражение, добавив круглые скобки группы, то элементов в результирующем массиве будет больше:
| 1 2 3 | let someText = "Hello! How are you? This is Robert. I'd like to introduce myself to you."; let sentences = someText.split(/([!?.])/); console.log(sentences); //['Hello', '!', ' How are you', '?', ' This is Robert', '.', " I'd like to introduce myself to you", '.', ''] |
Также мы можем использовать метод split(/\d/), чтобы убрать из строки лишние цифры. Например, так:
| 1 2 3 | let strWithDigits = '11My34Dear09Friend!'; document.write(`Исходная строка: <b>${strWithDigits}</b><br> Измененная строка: <b>${ strWithDigits.split(/\d/).join(' ') }</b>`); |
Пример в действии:
Кроме того, мы можем использовать более широкий диапазон символов для разделения строки на массив. Как правило, как в предыдущем примере, этот массив мы потом превращаем обратно в строку методом массива join('разделитель').
Воспользуемся более широким диапазоном для проверки строки на палиндром. Палиндром - это слово, число, буквосочетание, или предложение, одинаково читающееся как слева направо, так и наоборот. Например, число 20402; слова "топот" или "шалаш" в русском языке и финское слово saippuakivikauppias (продавец мыльного камня) — самое длинное слово-палиндром в мире; фраза "Уж я веники не вяжу" и др. являются палиндромами.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function palindrom(str){ str = str.toLowerCase().split(/\s+|[-!?,\._\/]+|[\(\)]/); //console.log(str); let strReverse = str.join('').split('').reverse().join(''); str = str.join('').split('').join(''); //console.log(str, 'и', strReverse) return str === strReverse; } console.log(palindrom('Кит на море! - Не, романтик)')); //true console.log(palindrom('Ша-лаш!)')); //true console.log(palindrom('А роза, упала на лапу Азора?')); //true console.log(palindrom('12321')); //true console.log(palindrom('тук-ТУК!')); //false |
В функции palindrom() вы можете раскомментировать вывод сообщений в консоль, чтобы посмотреть, что получилось в результате преобразований.
Здесь, составляя выражение, нужно помнить об экранировании следующих символов:
| 1 | ^ $ \ . * + ? ( ) [ ] { } | |
Получение значений из результата регулярного выражения
Метод exec()
Метод exec() выполняет поиск сопоставления регулярного выражения в указанной строке. Возвращает массив с результатами или null. Обратите внимание, что это метод объекта RegExp, поэтому сначала мы указываем переменную, в которой находится регулярное выражение, а потом в скобках передаем строку, для которой ищем сопоставления.
Синтаксис: regexObj.exec(str);
Возвращаемое значение:
Если сопоставление успешно выполнилось, метод exec() возвращает массив и обновляет свойства объекта регулярного выражения. Возвращаемый массив в первом элементе содержит сопоставленный текст, а в последующих элементах — текст, захваченный при сопоставлении круглыми скобками.
Когда метод exec() регулярного выражения находит совпадение, он возвращает массив, содержащий первый совпадающий фрагмент строки и далее группы, определённые в регулярном выражении. Деструктурирующее присваивание упрощает получение данных из этих групп, игнорируя первый фрагмент:
| 1 2 3 4 5 6 7 | const url = "https://developer.mozilla.org/en-US/Web/JavaScript"; const parsedURL = /^(\w+)\:\/\/([^\/]+)\/(.*)$/.exec(url); console.log(parsedURL); const [, protocol, fullhost, fullpath] = parsedURL; console.log(protocol); // выведет "https" |
В консоли увидим:
| 1 2 3 4 5 6 7 8 | 0: "https://developer.mozilla.org/en-US/Web/JavaScript" 1: "https" 2: "developer.mozilla.org" 3: "en-US/Web/JavaScript" groups: undefined index: 0 input: "https://developer.mozilla.org/en-US/Web/JavaScript" length: 4 |
Второй пример связан с проверкой телефонного номера. В том случае, если она пройдена, мы выводим телефон в виде нулевого элемента массива.
See the Pen Phone Number Validate by Elen (@ambassador) on CodePen.
Также мы можем найти вхождения слов "Карл" и "Клара" в известной строке. Если добавим еще флаг i, то к индексам добавится позиция начала слова "кларнет".
| 1 2 3 4 5 6 7 | let text = 'Карл у Клары украл кораллы. Клара у Карла украла кларнет.'; let regex = /К(а|л)(а|р)/g; let result, indexes = []; while (result = regex.exec(text)) { indexes.push("" + result.index); } console.log ("indexes " + indexes); //indexes 0,7,28,36 |
Если мы просто напишем regex.exec(text), то получим только индекс начального вхождения регулярного выражения в строку, а с помощью цикла while мы получаем все такие индексы.
Метод match()
Метод match() возвращает получившиеся совпадения при сопоставлении строки с регулярным выражением.
Синтаксис: str.match(regexp)
Если регулярное выражение не содержит флаг g, то вернется тот же массив, что и для метода RegExp.exec(). Возвращаемый массив имеет дополнительное свойство input, которое содержит оригинальную строку. Кроме того, он имеет свойство index, которое представляет индекс (нумерация с нуля) сопоставления в строке.
Если регулярное выражение содержит флаг g, то есть выполняет поиск всех совпадений, то метод вернёт массив, содержащий все сопоставления. Если сопоставлений не было, метод вернёт значение null.
| 1 2 3 4 5 6 | let list = `Завтрак - 9:00 Обед 14:00 Ужин 19:00 Сладкий сон 22:00`, regular = /\d{1,2}:\d{2}/; console.log(list.match(regular)); |
В консоли мы увидим:
| 1 2 3 4 5 | 0: "9:00" groups: undefined index: 10 input: "Завтрак - 9:00\nОбед 14:00\nУжин 19:00\nСладкий сон 22:00" length: 1 |
Если мы меняем регулярное выражение на regular = /\d{1,2}:\d{2}/g, то увидим такой массив: ['9:00', '14:00', '19:00', '22:00'].
В примере ниже мы ищем строку "name is ..." с помощью метода match() и выводим ее в консоль. Поскольку есть флаг 'g', то мы видим в консоли массив.
Во втором регулярном выражении мы выделяем в группу name и видим в консоли вывод этой информации по первому совпадению.
| 1 2 3 4 5 6 7 8 | const nameStr = "My name is George. Your name is Sara. My dad's name is Donald."; const re1 = /name\sis\s[a-zA-Z]+\./gi; console.log(nameStr.match(re1)); const re2 = /name\sis\s(?<name>[a-zA-Z]+)\./i; let found = nameStr.match(re2); //['name is George.', 'name is Sara.', 'name is Donald.'] console.log(found.groups); // {name: "George"} |
Метод matchAll()
String.matchAll() возвращает итератор, который в свою очередь возвращает все совпадающие группы одну за другой. Важно помнить, что в регулярном выражении здесь обязательно был указан флаг 'g'.
| 1 2 3 4 5 6 7 8 9 10 | const str = "abc"; const regexp = /[a-c]/g; const iterator = str.matchAll(regexp); for (result of iterator) { console.log(result); } // ["a", index: 0, input: "abc", groups: undefined] // ["b", index: 1, input: "abc", groups: undefined] // ["c", index: 2, input: "abc", groups: undefined] |
Мы можем получить группы совпадений в виде массива, используя, например, деструктуризацию:
| 1 2 3 4 | let text = 'Карл у Клары украл кораллы. Клара у Карла украла кларнет.', reg1 = /К(а|л)(а|р)[а-я]+/g; let textMatches = text.matchAll(reg1); console.log([...textMatches].map( (m) => m[0])) //['Карл', 'Клары', 'Клара', 'Карла'] |
Возможно, не самый простой способ, но он у вас есть.
Проверка строк на соответствие определенным параметрам
Проверка строк на соответствие определенным параметрам выполняется чаще всего, когда пользователь отправляет форму, в которой есть поля типа email, телефон или пароль (password), который требует соответствия некоторым параметрам. В полях форм можно использовать атрибут pattern, который по сути требует наличия регулярного выражения.
В проверках чаще всего используют метод regexObj.test(str) или String.search([regexp]).
Метод test()
Метод test() проверяет строку на наличие совпадений с регулярным выражением. Метод возвращает true, если совпадения были найдены, и false, если совпадений нет.
Синтаксис метода:
| 1 2 3 | regexObj.test(str) //или регулярное_выражение.test(строка, в которой ищем совпадение) |
Например, нам нужно найти https:// в адресе сайта, который ввел пользователь
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <ul id="siteAddreses"> <li>http://somesite.org</li> <li>https://somesite.com.ua</li> <li>ftp://somesite.net</li> <li>mymail@somesite.org</li> </ul> <script> const reSite = new RegExp('^https://', 'i'); siteAddreses.querySelectorAll('li').forEach(item => item.addEventListener('click', function(){ alert(reSite.test(item.textContent)) }) ) </script> |
Проверьте сами:
Кликните на любом URL
- http://somesite.org
- https://somesite.com.ua
- ftp://somesite.net
- mymail@somesite.org
Метод search()
Метод search() относится к методам строк. Регулярное выражение мы передаем в скобках, хотя оно не обязано быть только в синтаксисе RegExp - искать можно и обычную строку. При успехе метод search() возвращает индекс первого сопоставления с регулярным выражением внутри строки. В противном случае метод вернёт -1.
| 1 2 3 | str.search([regexp]) //или строка_для_поиска.search([регулярное_выражение]) |
Например, мы можем поискать, а входит ли поlстрока 'lorem' в наш текст. Причем можем поискать просто строку, а можем в виде регулярного выражения с флагами, которые ищут без учета регистра и везде. Здесь мы заменяем все вхождения подстроки 'lorem' с помощью метода replace() на тот же текст, но подсвеченный либо тегом <strong>, либо тегом <mark>.
See the Pen String.search(RegExp) by Elen (@ambassador) on CodePen.
Еще один пример: нам нужно найти первое число из трех цифр в нескольких строках:
| 1 2 3 4 5 6 7 8 9 | let strNum = '3 12 327 480', nextNum = '123hello_450', justStr = 'Ivan Petrov', str12 = 'editor12', reNum = /\d{3}/; console.log(strNum.search(reNum)); // 5 console.log(nextNum.search(reNum)); // 0 console.log(justStr.search(reNum)); // -1 console.log(str12.search(reNum)); // -1 |
В первых двух проверках мы видим номер символа, с которого начинается вхождение трехзначной цифры в строку, а в последних двух - число -1, которое показывает, что цифра в строке отсутствует либо, что она не трехзначная.
Проверка электронной почты в JavaScript
Проверка адреса электронной почты является одной из важнейших частей аутентификации HTML-формы. Адрес электронной почты — это строка символов, разделенная на две части с помощью символа @. Первая часть состоит из личной информации, а вторая содержит доменное имя, в котором регистрируется адрес электронной почты.
В символы первой части могут входить:
- Заглавные буквы (от A до Z) и строчные буквы (от a до z)
- Цифровые символы (от 0 до 9)
- Точка, которая не может быть первой или последней буквой в email и не может повторяться, а также "-" или символ нижнего подчеркивания "_".
Вторая часть включает в себя следующее:
- Буквы
- Цифры
- Дефисы
- Точки, которые разделяют доменное имя, например
gmail.comилиsomesite.com.ua
Давайте посмотрим на примеры правильных вариантов электронной почты:
- Vasiliy.Pupkin@my-site.org
- super-admin@somesite.com.ua
- temporary@ukr.net
- sveta_1986@gmail.com
Теперь варианты недействительных адресов электронной почты:
vasiliy.pupkin..7080@abcd.com(не допускается использование двух точек рядом)dolmatskiy()&@abcd.com(в email допускаются только тире (минус), нижнее подчеркивание и цифры.)admin.azbyz.com(отсутствует символ @)myemail@.com.you(домен верхнего уровня не может начинаться с точки)@mysite.org.ua(email не может начинаться с символа @)olga_test345@mysite.b(«.b» не является допустимым доменным именем)
На основе этих проверочных моментов мы будем использовать символы \w, т.к. они проверяют соответствие любому символу слова, в который входят символы от a до z, от A до Z, а также цифры от 0 до 9 и знак подчеркивания.
Регулярное выражение для проверки email будет таким:
| 1 | const mailReg = /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(.\w{2,3})+$/; |
Работоспособность кода можно проверить в этом примере:
See the Pen Email validation by Elen (@ambassador) on CodePen.
Тут следует отметить, что обычно валидацию email выполняют при отправке формы. Однако в браузере "вшита" проверка email, которая выполняется для <input type="email"> при клике на кнопку типа "submit".
Пример обработки правильности ввода данных в поля формы (валидация)
See the Pen Password Form Validation with JavaScript by Envato Tuts+ (@tutsplus) on CodePen.
Проверка пароля
Отличный пример использования большинства регулярных выражений нужно использовать для проверки пароля на ряд условий (JavaScript).
See the Pen Password Validation in JavaScript by Elen (@ambassador) on CodePen.
Валидация поля типа email на JavaScript
See the Pen Validate Email Address in JS by Elen (@ambassador) on CodePen.
Валидация email, пароля и повторения пароля на JS
See the Pen Validate Email & Password (JS) by Elen (@ambassador) on CodePen.
Полезные сервисы
Возможно, у вас не всегда будет доступ к компьютеру или текстовому редактору. Или что-то не так с вашим регулярным выражением: где-то есть ошибка, которую вы не видите. Поэтому хорошо иметь возможность потренироваться или проверить это регулярное выражение на каком-либо тонлан-ресурсе. Вы можете сделать это с помощью таких сервисов, как RegExr, Regexpal или Regex101. Последний, вдобавок, приводит краткие пояснения к тому, как регулярка работает.
Где еще найти примеры регулярных выражений
Если, прочитав эту статью, вы не нашли для себя решения, да и вообще тема регулярных выражений "не зашла", можно воспользоваться сайтом ihateregex.io, на котором собраны регулярные выражения для распространенных задач.