Введение в регулярные выражения в JavaScript
Регулярные выражения — это такие команды для сложного поиска и замены (или просто поиска). Они позволяют делать очень интересные вещи, но, к сожалению, довольно тяжелы в освоении.
Существует несколько методов JavaScript для работы с регулярными выражениями. Мы начнем знакомится с ними на примере replace — вы уже знакомы с этим методом: он первым параметром принимает что менять, а вторым — на что менять. А сам метод применяется к строке, в которой производится замена:
‘bab’.replace(‘a’, ‘!’); // вернет ‘b!b’
Первым параметром этого метода можно передавать не просто строку, а . Регулярное выражение представляет собой набор команд, расположенных внутри слешей / . Эти слеши называются регулярных выражений.
Сами регулярные выражения состоят из двух типов символов: из тех, которые обозначают сами себя и из символов-команд, которые называются .
Буквы и цифры обозначают сами себя. В следующем примере мы с помощью регулярного выражения заменим букву ‘a’ на ! :
‘bab’.replace(/a/, ‘!’); // вернет ‘b!b’
А вот точка является специальным символом и обозначает любой символ . В следующем примере мы найдем строку по такому шаблону: буква ‘x’ , затем любой символ, затем опять буква ‘x’ :
‘xax eee’.replace(/x.x/, ‘!’); // вернет ‘! eee’
После ограничителей можно писать — команды, которые изменяют общие свойства регулярного выражения. Например, модификатор g включает режим глобального поиска и замены — без него регулярка ищет только первое совпадение, а с ним — все совпадения.
В следующем примере не указан модификатор g и регулярка найдет только первое совпадение:
‘aab’.replace(/a/, ‘!’); // вернет ‘!ab’
А теперь регулярка найдет все совпадения:
‘aab’.replace(/a/g, ‘!’); // вернет ‘!!b’
let str = ‘ahb acb aeb aeeb adcb axeb’;
Напишите регулярку, которая найдет строки ‘ahb’ , ‘acb’ , ‘aeb’ по шаблону: буква ‘a’ , любой символ, буква ‘b’ .
let str = ‘aba aca aea abba adca abea’;
Напишите регулярку, которая найдет строки ‘abba’ , ‘adca’ , ‘abea’ по шаблону: буква ‘a’ , 2 любых символа, буква ‘a’ .
let str = ‘aba aca aea abba adca abea’;
Напишите регулярку, которая найдет строки ‘abba’ и ‘abea’ , не захватив ‘adca’ .
Регулярные выражения
Регулярные выражения — это шаблоны, используемые для сопоставления последовательностей символов в строках. В JavaScript регулярные выражения также являются объектами. Эти шаблоны используются в методах exec (en-US) и test (en-US) объекта RegExp (en-US) а также match (en-US) , replace , search (en-US) , split (en-US) объекта String . Данная глава описывает регулярные выражения в JavaScript.
Создание регулярного выражения
Регулярное выражение можно создать двумя способами:
-
Используя литерал регулярного выражения, например:
var re = /ab+c/;
var re = new RegExp("ab+c");
Написание шаблона регулярного выражения
Шаблон регулярного выражения состоит из обычных символов, например /abc/ , или комбинаций обычных и специальных символов, например /ab*c/ или /Chapter (\d+)\.\d*/ . Последний пример включает в себя скобки, которые используются как «запоминающий механизм». Соответствие этой части шаблона запоминается для дальнейшего использования, как описано в Использование совпадений подстрок заключённых в скобки.
Использование простых шаблонов
Простые шаблоны используются для нахождения прямого соответствия в тексте. Например, шаблон /abc/ соответствует комбинации символов в строке только когда символы ‘abc’ встречаются вместе и в том же порядке. Такое сопоставление произойдёт в строке «Hi, do you know your abc’s?» и «The latest airplane designs evolved from slabcraft.» В обоих случаях сопоставление произойдёт с подстрокой ‘abc’. Сопоставление не произойдёт в строке «Grab crab», потому что она не содержит подстроку ‘abc’.
Использование специальных символов
В случае когда поиск соответствия требует чего-то большего, чем прямое сопоставление, например нахождение последовательности символов ‘b’ или нахождение пробела, шаблон включает в себя специальные символы. Например, шаблон /ab*c/ соответствует любой комбинации символов, в которой за ‘a’ следует ноль или более символов ‘b’ ( * означает ноль или более вхождений предыдущего символа), за которыми сразу же следует символ ‘c’. В строке «cbbabbbbcdebc,» этому шаблону сопоставляется подстрока ‘abbbbc’.
В следующей таблице приводится полный список специальных символов регулярных выражений с их описаниями.
- Для символов обычно обрабатываемых буквально, означает что следующий символ является специальным и не должен интерпретироваться буквально.
- Например, /b/ сопоставляется символу ‘b’. Добавляя слеш перед b, т.е используя /\b/ , символ становится специальным символом, означающим границу слова.
- Для символов обычно обрабатываемых особым образом означает, что следующий символ не является специальным и должен интерпретироваться буквально.
- Например, * является специальным символом, сопоставляемым 0 или более повторений предыдущего символа; например, /a*/ означает соответствие 0 или более символов а. Для буквальной интерпретации *, поставьте перед ней обратный слеш; например, /a\*/ соответствует ‘a*’.
- Также не забудьте заэкранировать сам \ при его использовании в записи new RegExp(«pattern») поскольку \ также является экранирующим символом в обычных строках.
Соответствует началу ввода. Если установлен флаг многострочности, также производит сопоставление непосредственно после переноса строки.
Например, /^A/ не соответствует ‘A’ в «an A», но соответствует ‘A’ в «An E».
Этот символ имеет другое значение при появлении в начале шаблона набора символов.
Например, /[^a-z\s]/ соответствует ‘I’ в «I have 3 sisters».
Соответствует концу ввода. Если установлен битовый флаг многострочности, также сопоставляется содержимому до переноса строки.
Например, /t$/ не соответствует ‘t’ в строке «eater», но соответствует строке «eat».
Соответствует предыдущему символу повторенному 0 или более раз. Эквивалентно .
Например, /bo*/ соответствует ‘boooo’ в «A ghost booooed» и ‘b’ в «A bird warbled», но не в «A goat grunted».
Соответствует предыдущему символу повторенному 1 или более раз. Эквивалентно .
Например, /a+/ соответствует ‘a’ в «candy» и всем символам ‘a’ в «caaaaaaandy».
0 или 1 раз. Эквивалентно .
Например, /e?le?/ соответствует ‘el’ в «angel» и ‘le’ в «angle» а также ‘l’ в «oslo».
Если использован сразу после квалификаторов * , + , ? , или <> , делает квалификатор «нежадным» (соответствующим минимальному количеству символов), в отличие от режима по умолчанию, являющимся «жадным» (соответствующим максимальному числу символов). Например, используя /\d+/ не глобальное сопоставление «123abc» возвращает «123», если использовать /\d+?/, только «1» будет возвращена.
Также используется в упреждающих утверждениях (assertions), описанных в строках x(?=y) и x(?!y) данной таблицы.
(десятичная точка) соответствует любому символу кроме переноса строки.
Например, /.n/ соответствует ‘an’ и ‘on’ в «nay, an apple is on the tree», но не ‘nay’.
Соответствует ‘x’ и запоминает это соответствие. Это называется захватывающие скобки.
Например, /(foo)/ соответствует ‘foo’ в «foo bar.» Сопоставленная строка может быть получена из элементов результирующего массива [1] , . [n] .
Соответствует ‘x’ только если за ‘x’ следует ‘y’. Это называется упреждение.
Например, /Jack(?=Sprat)/ соответствует ‘Jack’ только если за ним следует ‘Sprat’. /Jack(?=Sprat|Frost)/ соответствует ‘Jack’ только если за ним следует ‘Sprat’ или ‘Frost’. Тем не менее, ни ‘Sprat’ ни ‘Frost’ не являются частью сопоставленного результата.
Соответствует ‘x’ только если за ‘x’ не следует ‘y’. Это называется отрицательное упреждение.
Например, /\d+(?!\.)/ соответствует числу только если за ним не следует десятичная точка. Регулярное выражение /\d+(?!\.)/.exec(«3.141») сопоставит ‘141’ но не ‘3.141’.
Соответствует либо ‘x’ либо ‘y’.
Например, /green|red/ соответствует ‘green’ в «green apple» и ‘red’ в «red apple.»
n — положительное целое. Соответствует ровно n вхождениям предыдущего символа.
Например, /a/ не соответствует ‘a’ в «candy,» но соответствует всем а в «caandy,» первым двум а в «caaandy.»
m и n — положительные целые. Соответствует как минимум n и максимум m вхождениям предыдущего символа. При m=n=1 пропускается.
Например, /a/ ничему не соответствует в строке «cndy», символу ‘a’ в «candy,» двум а в «caandy,» и трём первым а в «caaaaaaandy». Отметим, что при сопоставлении «caaaaaaandy», совпадает «aaa», хотя изначальная строка содержит больше а.
Набор символов. Соответствует любому символу из перечисленных. Можно указать диапазон символов, используя тире. Специальные символы (как точка ( . ) и звёздочка ( * )) не имеют специального значения внутри такого набора. Их не надо экранировать. Экранирование работает также.
Например, [abcd] эквивалентна [ a-d] . Они соответствуют ‘b’ в «brisket» и ‘c’ в «city». /[a-z.]+/ и /[\w.]+/ обе соответствуют всему в «test.i.ng».
Инвертированный или дополняющий набор символов. Это означает соответствие всему, что не в скобках. Можно указать диапазон символов с помощью тире. Все, что действует в обычном наборе символов, действует и здесь.
Например, [^abc] эквивалентно [^a-c] . Они соответствуют изначально ‘r’ в «brisket» и ‘h’ в «chop.»
Соответствует границе слова. Граница слова соответствует позиции, где за символом слова не следует другой символ слова или предшествует ему. Отметим, что граница слова не включается в соответствие. Другими словами, длина сопоставленной границы слова равна нулю. (Не путать с [\b] .)
Примеры:
/\bmoo/ соответствует ‘moo’ в слове «moon» ;
/oo\b/ не соответствует ‘oo’ в слове «moon», поскольку за ‘oo’ следует символ ‘n’ , являющимся символом слова;
/oon\b/ соответствует ‘oon’ в слове «moon», поскольку ‘oon’ является окончанием строки, и таким образом, за этими символами не следует другой символ слова;
/\w\b\w/ никогда не будет ничему соответствовать, поскольку за символом слова никогда не может следовать и граница слова, и символ слова.
Note: JavaScript’s regular expression engine defines a specific set of charactersto be «word» characters. Any character not in that set is considered a word break. This set of characters is fairly limited: it consists solely of the Roman alphabet in both upper- and lower-case, decimal digits, and the underscore character. Accented characters, such as «é» or «ü» are, unfortunately, treated as word breaks.
Соответствует несловообразующей границе. Несловообразующая граница соответствует позиции, в которой предыдущий и следующий символы являются символами одного типа: либо оба должны быть словообразующими символами, либо несловообразующими. Начало и конец строки считаются несловообразующими символами.
Например, /\B../ соответствует ‘oo’ в слове «noonday» (, а /y\B./ соответствует ‘ye’ в «possibly yesterday.»
Где X является символом случайного выбора из последовательности от А до Я. Соответствует управляющему символу в строке.
Например, /\cM/ соответствует control-M (U+000D) в строке.
Соответствует цифровому символу. Эквивалентно выражению [0-9] .
Например, /\d/ or /[0-9]/ соответствует ‘2’ в «B2 is the suite number.»
Соответствует любому нецифровому символу. Эквивалентно выражению [^0-9] .
Например, /\D/ or /[^0-9]/ соответствует ‘B’ в предложении «B2 is the suite number.»
Соответствует символу прогона страницы (U+000C). Особый символ управления печатью.
Соответствует одиночному символу пустого пространства, включая пробел, табуляция, прогон страницы, перевод строки. Эквивалентен [ \f\n\r\t\v\u00A0\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u2028\u2029\u2028\u2029\u202f\u205f\u3000] .
Например, /\s\w*/ совпадает с ‘ bar’ в «foo bar.»
Соответствует одиночному символу непустого пространства. Эквивалентен [^ \f\n\r\t\v\u00A0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000] .
Например, /\S\w*/ совпадает с ‘foo’ в «foo bar.»
Соответствует любому цифробуквенному символу включая нижнее подчёркивание. Эквивалентен [A-Za-z0-9_] .
Например, /\w/ совпадает с ‘a’ в «apple,» ‘5’ в «$5.28,» и ‘3’ в «3D.»
Соответствует любому не цифробуквенному символу. Равносилен [^A-Za-z0-9_] .
Например, /\W/ or /[^A-Za-z0-9_]/ совпадает с ‘%’ в «50%.»
Где n это положительное целое, обратная ссылка на последнюю найденную подстроку, соответствующую n , заключённую в круглые скобки в регулярном выражении (считая левые скобки).
Например, /apple(,)\sorange\1/ соответствует ‘apple, orange,’ в «apple, orange, cherry, peach.»
Экранирование пользовательского ввода, соответствующего буквенной строке внутри регулярного выражения, может быть достигнуто простой заменой:
function escapeRegExp(string) return string.replace(/[.*+?^$<>()|[\]\\]/g, "\\$&"); // $& means the whole matched string >
Использование скобок
Скобки вокруг любой части регулярного выражения означают что эта часть совпадаемой подстроки будет запомнена. Раз запомнена, подстрока может выбрана для использования как это описано в Using Parenthesized Substring Matches.
Например, паттерн /Chapter (\d+)\.\d*/ включает в себя дополнительные экранирующие и специальные символы и указывает на то, что часть шаблона должна быть запомнена. Он точно соответствует символам слова ‘Chapter ‘, за которыми следует один или более цифровых символов ( \d означает любой цифровой символ, а ‘ +’ означает 1 или более раз), за которым следует десятичная точка (сама по себе являющаяся специальным символом; предшествующий десятичной точке слеш ‘ \’ означает, что паттерн должен искать литеральный символ ‘.’), после которой следует любой цифровой символ 0 или более раз (‘ \d’ обозначает цифровой символ, ‘ *’ обозначает 0 или более раз). Кроме того, круглые скобки используются для запоминания первых же совпавших цифровых символов.
Этот шаблон будет найден во фразе «Open Chapter 4.3, paragraph 6» и цифра ‘4’ будет запомнена. Но он не будет найден во фразе «Chapter 3 and 4», поскольку эта строка не имеет точки после цифры ‘3’.
Для того, чтобы сопоставить подстроку без вызова совпавшей части для запоминания, внутри круглых скобок необходимо предварить паттерн сочетанием символов ‘ ?:’ . Например, шаблон (?:\d+) будет соответствовать одному или более цифровому символу, но не запомнит совпавших символов.
Работа с Регулярными Выражениями
Регулярные выражения используются в методах test и exec объекта RegExp и с методами match , replace , search , и split объекта String . Эти методы подробно объясняются в Справочнике JavaScript
| Метод | Описание |
|---|---|
| exec (en-US) | Метод RegExp, который выполняет поиск совпадения в строке. Он возвращает массив данных. |
| test (en-US) | Метод RegExp , который тестирует совпадение в строке. Возвращает либо истину либо ложь. |
| match (en-US) | Метод String , который выполняет поиск совпадения в строке. Он возвращает массив данных либо null если совпадения отсутствуют. |
| search (en-US) | Метод String, который тестирует на совпадение в строке. Он возвращает индекс совпадения, или -1 если совпадений не будет найдено. |
| replace | Метод String , который выполняет поиск совпадения в строке, и заменяет совпавшую подстроку другой подстрокой, переданной как аргумент в этот метод. |
| split (en-US) | Метод String, который использует регулярное выражение или фиксированную строку чтобы разбить строку на массив подстрок. |
Чтобы просто узнать есть ли в строке что либо соответствующее шаблону, воспользуйтесь методами test или search ; а чтобы получить больше информации пользуйтесь методами exec или match (хотя эти методы работают медленнее). Если вы пользуетесь exec или match и если совпадения есть, эти методы вернут массив и обновлённые свойства объекта ассоциированного регулярного выражения а также предопределённого объекта RegExp регулярного выражения. Если совпадений нет, метод exec вернёт null (который сконвертируется в false ).
В след. примере, скрипт использует метод exec чтобы найти совпадения в строке.
var myRe = /d(b+)d/g; var myArray = myRe.exec("cdbbdbsbz");
Если вам не нужен доступ к свойствам регулярного выражения, то альтернативный способ получить myArray можно так:
var myArray = /d(b+)d/g.exec("cdbbdbsbz");
Если вы хотите сконструировать регулярное выражение из строки, другой способ сделать это приведён ниже:
var myRe = new RegExp("d(b+)d", "g"); var myArray = myRe.exec("cdbbdbsbz");
С помощью этих скриптов, поиск совпадения завершается и возвращает массив и обновлённые свойства показанные в след. таблице.
| Объект | Свойство или индекс | Описание | В этом примере. |
|---|---|---|---|
| myArray | Совпавшая строка и все запомненные подстроки. | [«dbbd», «bb»] | |
| index | Индекс совпавшей подстроки (индекс начинается с нуля). | 1 | |
| input | Исходная строка. | «cdbbdbsbz» | |
| [0] | Последние совпавшие символы. | «dbbd» | |
| myRe | lastIndex | Индекс с которого начнётся след. поиск совпадения. (Это свойство определяется только если регулярное выражение использует параметр g, описанный в [Advanced Searching With Flags](#Advanced_Searching_With_Flags).) | 5 |
| source | Текст шаблона. Обновляется в момент создания регулярного выражения, а не во время выполнения. | «d(b+)d» |
Как показано во втором варианте этого примера, вы можете использовать регулярное выражение, созданное при помощи инициализатора объекта, без присваивания его переменной. Таким образом, если вы используете данную форму записи без присваивания переменной, то в процессе дальнейшего использования вы не можете получить доступ к свойствам данного регулярного выражения. Например, у вас есть следующий скрипт:
var myRe = /d(b+)d/g; var myArray = myRe.exec("cdbbdbsbz"); console.log("The value of lastIndex is " + myRe.lastIndex);
Этот скрипт выведет:
The value of lastIndex is 5
Однако, если у вас есть следующий скрипт:
var myArray = /d(b+)d/g.exec("cdbbdbsbz"); console.log("The value of lastIndex is " + /d(b+)d/g.lastIndex);
The value of lastIndex is 0
Совпадения /d(b+)d/g в двух случаях являются разными объектами регулярного выражения и, следовательно, имеют различные значения для свойства lastIndex . Если вам необходим доступ к свойствам объекта, созданного при помощи инициализатора, то вы должны сначала присвоить его переменной.
Использование скобочных выражений для нахождения подстрок
Использование скобок в шаблоне регулярного выражения повлечёт «запоминание» совпавшей подстроки. Для примера, /a(b)c/ вызовет совпадение ‘abc’ и запомнит ‘b’. Чтобы получить совпадения скобочного выражения используйте Array elements [1] , . [n] .
Число возможных скобочных подстрок неограничено. Возвращаемый массив содержит все полученные совпадения, удовлетворяющие выражению в скобках. Следующий пример показывает как использовать скобочные выражения для нахождения подстрок.
Следующий скрипт использует метод replace(), чтобы поменять местами слова (символы) в строке. Для замены текста скрипт использует обозначения $1 и $2 для обозначения первого и второго совпадения скобочного выражения.
var re = /(\w+)\s(\w+)/; var str = "John Smith"; var newstr = str.replace(re, "$2, $1"); console.log(newstr);
Выведет «Smith, John».
Расширенный поиск с флагами
Регулярные выражения имеют четыре опциональных флага, которые делают возможным глобальный и регистронезависимый поиск. Флаги могут использоваться самостоятельно или вместе в любом порядке, а также могут являться частью регулярного выражения.
| Flag | Description |
|---|---|
| g | Глобальный поиск. |
| i | Регистронезависимый поиск. |
| m | Многострочный поиск. |
| y | Выполняет поиск начиная с символа, который находится на позиции свойства lastindex текущего регулярного выражения. |
Чтобы использовать флаги в шаблоне регулярного выражения используйте следующий синтаксис:
var re = /pattern/flags;
var re = new RegExp("pattern", "flags");
Обратите внимание, что флаги являются неотъемлемой частью регулярного выражения. Флаги не могут быть добавлены или удалены позднее.
Для примера, re = /\w+\s/g создаёт регулярное выражение, которое ищет один или более символов, после которых следует пробел и ищет данное совпадение на протяжении всей строки.
var re = /\w+\s/g; var str = "fee fi fo fum"; var myArray = str.match(re); console.log(myArray);
Выведет [«fee «, «fi «, «fo «]. В этом примере вы бы могли заменить строку:
var re = /\w+\s/g;
var re = new RegExp("\\w+\\s", "g");
и получить тот же результат.
Флаг m используется, чтобы входная строка рассматривалась как многострочная. Если флаг m используется, то ^ и $ вызовет совпадение в начале или конце любой строки в строке ввода вместо начала или конца вводимой строки целиком.
Примеры
След. примеры показывают использование регулярных выражений.
Изменение порядка в Исходной Строке
След. пример иллюстрирует формирование регулярного выражения и использование string.split() и string.replace() . Он очищает неправильно сформатированную исходную строку, которая содержит имена в неправильном порядке (имя идёт первым) разделённые пробелами, табуляцией и одной точкой с запятой. В конце, изменяется порядок следования имён (фамилия станет первой) и сортируется список.
// The name string contains multiple spaces and tabs, // and may have multiple spaces between first and last names. var names = "Harry Trump ;Fred Barney; Helen Rigby ; Bill Abel ; Chris Hand "; var output = ["---------- Original String\n", names + "\n"]; // Prepare two regular expression patterns and array storage. // Split the string into array elements. // pattern: possible white space then semicolon then possible white space var pattern = /\s*;\s*/; // Break the string into pieces separated by the pattern above and // store the pieces in an array called nameList var nameList = names.split(pattern); // new pattern: one or more characters then spaces then characters. // Use parentheses to "memorize" portions of the pattern. // The memorized portions are referred to later. pattern = /(\w+)\s+(\w+)/; // New array for holding names being processed. var bySurnameList = []; // Display the name array and populate the new array // with comma-separated names, last first. // // The replace method removes anything matching the pattern // and replaces it with the memorized string—second memorized portion // followed by comma space followed by first memorized portion. // // The variables $1 and $2 refer to the portions // memorized while matching the pattern. output.push("---------- After Split by Regular Expression"); var i, len; for (i = 0, len = nameList.length; i len; i++) output.push(nameList[i]); bySurnameList[i] = nameList[i].replace(pattern, "$2, $1"); > // Display the new array. output.push("---------- Names Reversed"); for (i = 0, len = bySurnameList.length; i len; i++) output.push(bySurnameList[i]); > // Sort by last name, then display the sorted array. bySurnameList.sort(); output.push("---------- Sorted"); for (i = 0, len = bySurnameList.length; i len; i++) output.push(bySurnameList[i]); > output.push("---------- End"); console.log(output.join("\n"));
Использование спецсимволов для проверки входных данных
В след. примере, ожидается что пользователь введёт телефонный номер и требуется проверить правильность символов набранных пользователем. Когда пользователь нажмёт кнопку «Check», скрипт проверит правильность введённого номера. Если номер правильный (совпадает с символами определёнными в регулярном выражении), то скрипт покажет сообщение благодарности для пользователя и подтвердит номер. Если нет, то скрипт проинформирует пользователя, что телефонный номер неправильный.
Внутри незахватывающих скобок (?: , регуляное выражение ищет три цифры \d ИЛИ | открывающую скобку \( , затем три цифры \d , затем закрывающую скобку \) , (закрывающая незахватывающая скобка ) ), затем тире, слеш, или десятичная точка, и когда это выражение найдено, запоминает символ ([-\/\.]) , следующие за ним и запомненные три цифры \d , следующее соответствие тире, слеша или десятичной точки \1 , и следующие четыре цифры \d .
Регулярное выражение ищет сначала 0 или одну открывающую скобку \(? , затем три цифры \d , затем 0 или одну закрывающую скобку \)? , потом одно тире, слеш или точка и когда найдёт это, запомнит символ ([-\/\.]) , след. три цифры \d , followed by the remembered match of a dash, forward slash, or decimal point \1 , followed by four digits \d .
Событие «Изменить» активируется, когда пользователь подтвердит ввод значения регулярного выражения, нажав клавишу «Enter».
doctype html> html> head> meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" /> meta http-equiv="Content-Script-Type" content="text/javascript" /> script type="text/javascript"> var re = /\(?\d \)?([-\/\.])\d \1\d /; function testInfo(phoneInput) var OK = re.exec(phoneInput.value); if (!OK) window.alert(RegExp.input + " isn't a phone number with area code!"); else window.alert("Thanks, your phone number is " + OK[0]); > script> head> body> p> Enter your phone number (with area code) and then click "Check". br />The expected format is like ###-###-####. p> form action="#"> input id="phone" />button onclick="testInfo(document.getElementById('phone'));"> Check button> form> body> html>
Found a content problem with this page?
- Edit the page on GitHub.
- Report the content issue.
- View the source on GitHub.
This page was last modified on 7 авг. 2023 г. by MDN contributors.
Как применять регулярные выражения в javascript
Методы для работы с регулярными выражениями
Методы обработки и поиска совпадений шаблона регулярного выражения со строкой текста:
- test() – метод проверяет на совпадение подстроки и шаблона, возвращает значение true или false .
- search() – также ищет совпадения и возвращает индекс при успешном результате или -1 в противном случае.
- exec() – метод находит подстроки заданного шаблона в целевой строке и в результате возвращает массив с результатами, null – в противном случае.
Пример использования этих методов:
let string1 = 'Hello, my friend! I\\'m really glad to see you'; let reg1 = /friend\\!/; console.log(reg1.test(string1)); // true let string2 = 'Hello, friend! Are you real friend\\?'; let reg2 = /friend/; console.log(string2.search(reg2)); // 7 let reg3 = /(fr.)/; console.log(reg3.exec(string2)[0]); // fri
Также регулярные выражения используются вместе со строчными функциями в JS: replace() , split() , match() . Пример использования этих методов смотрите в статье 40 основных методов для работы со строками в JS.
В этой статье мы познакомились со способами создания регулярных выражений, скобочными символами в шаблонах, а также узнали методы для работы с регулярным выражением и целевой строкой.
Материалы по теме
- ☕ Учебник по JavaScript: основные операторы
- ☕ Учебник по JavaScript: ООП на простых примерах
- ☕ Учебник по JavaScript: работа с датой и временем
JavaScript: изучаем регулярные выражения на практике

Регулярное выражение (далее также — регулярка) — это последовательность специальных символов, формирующих паттерн или шаблон (pattern), который сопоставляется со строкой.
Цель такого сопоставления может состоять либо в поиске подстроки в строке, например, для замены подстроки, либо в определении соответствия строки шаблону для валидации строки.
В данной статье мы сосредоточимся на валидации.
Что конкретно мы будем делать? Мы возьмем несколько регулярок из validator.js (наиболее популярной библиотеки для валидации данных с помощью регулярных выражений) и произведем их подробный разбор. Также мы рассмотрим несколько дополнительных регулярок и один алгоритм.
Как результат, мы реализуем несколько полезных функций, которые вы впоследствии сможете использовать в своих проектах.
Здесь можно получить общую информацию о регулярных выражениях, а здесь — более подробные сведения. Рекомендую ознакомиться с указанными материалами — так вам будет легче воспринимать дальнейшую информацию.
Еще парочка полезных ссылок:
// Валидация `URL` с протоколом `http(s)` const regexp = VerEx() // начало строки .startOfLine() // затем `http` .then('http') // затем, возможно, `s` .maybe('s') // затем `://` .then('://') // затем, возможно, `www.` .maybe('www.') // затем любой символ, кроме пробела .anythingBut(' ') // конец строки .endOfLine() const isURL = (str) => regexp.test(str) console.log(isURL('https://www.google.com')) // true
Для того, чтобы немного размять мозги, начнем с двух «валидаторов», в которых регулярные выражения не используются.
Является ли строка пустой?
const isEmpty = (str) => str.trim().length === 0 console.log(isEmpty('not empty')) // false console.log(isEmpty(' ')) // true
Метод trim() удаляет пробелы в начале и конце строки. Свойство length содержит количество символов, из которых состоит строка. Если строка не содержит символов, значит, ее свойство length имеет значение 0 . В этом случае выражение str.length === 0 возвращает true . В противном случае, возвращается false .
Данную функцию можно переписать так:
const isEmpty = (str) => !str.trim().length // `0` - ложное значение, `1` и больше - истинное значение, // `!` - логическое НЕ, выполняет преобразование значения в логическое и меняет его на противоположное // получается `!false`, или `true` // или так const _isEmpty = (str) => str.trim() === ''
Является ли значение «логическим»?
const isBoolean = (str) => ['true', 'false', '1', '0'].indexOf(str) >= 0 console.log(isBoolean('true')) // true console.log(isBoolean(false)) // false (см. ниже)
Метод indexOf() возвращает индекс элемента или -1 при отсутствии элемента в массиве. Если элемент в массиве есть, его индекс будет равным 0 или больше (в пределах длины массива — 1). В этом случае выражение arr.indexOf(str) >= 0 возвращает true . Иначе, возвращается false .
Обратите внимание: в данном случае логическими считаются не только значения true и false , но также 1 (обозначающее истину) и 0 (обозначающее ложь). Также обратите внимание, что функция принимает только строку (как и функции, которые будут рассматриваться в дальнейшем), поэтому второй вызов функции isBoolean() с логическим значением false возвращает false .
Данную функцию можно переписать так:
const bools = ['true', 'false', '1', '0'] const isBoolean = (str) => bools.indexOf(str) !== -1 // или так const _isBoolean = (str) => bools.indexOf(str) > -1
Размялись? Отлично. Переходим к регуляркам.
Состоит ли строка только из букв?
// для латиницы const isAlpha = /^[A-Z]+$/i.test(str) // для кириллицы const _isAlpha = /^[А-ЯЁ]+$/i.test(str) // для латиницы и криллицы const __isAlpha = /^[A-ZА-ЯЁ]+$/i.test(str)
- ^ и $ — символы начала и конца строки, соответственно (границы)
- [] — набор символов, перечисление (символ строки должен совпадать хотя бы с одним из вариантов)
- A-Z или А-ЯЁ — диапазон букв (например, А-ЯЁ — это все буквы киррилицы от А до Я + буква Ё , которая стоит особняком)
Обратите внимание: A-Z — это не тоже самое, что символьный класс \w . \w означает любая буква латиницы ИЛИ любая цифра ИЛИ символ нижнего подчеркивания.
- + — один или более предшествующий символ, т.е. символ, находящийся перед + (квантификатор)
- i — регистронезависимый поиск, т.е. поиск осуществляется без учета того, из больших или маленьких букв состоит строка (флаг)
Читаем регулярку: строка ДОЛЖНА состоять хотя бы из одной (одной или более) буквы, независимо от регистра.
Состоит ли строка только из букв и/или целых чисел?
// для латиницы const isAlphaNumeric = (str) => /^[0-9A-Z]+$/i.test(str) // для кириллицы const _isAlphaNumeric = (str) => /^[0-9А-ЯЁ]+$/i.test(str) // для латиницы и кириллицы const __isAlphaNumeric = (str) => /^[0-9A-ZА-ЯЁ]+$/i.test(str)
Разбор: такие же спецсимволы, что и в предыдущем примере +
- 0-9 — диапазон цифр от 0 до 9 ; данный диапазон можно заменить символьным классом \d , означающим любое число, для латиницы получится /^[\dA-Z]+$/i
Обратите внимание: здесь мы также не можем использовать \w из-за нижнего подчеркивания. Впрочем, мы можем исключить его из проверки, написав что-то вроде /^[^\W_]$/i , где [^] означает любой символ, кроме указанных в наборе (одним из таких символов является _ ), \W — НЕ буква латиницы, НЕ число и НЕ нижнее подчеркивание. Таким образом, [^\W_] означает любой символ, который НЕ является нижним подчеркиванием, а также не относится к буквам латиницы ИЛИ цифрам. Или в переводе на человеческий язык — любой символ, который является буквой латиницы или числом.
Читаем регулярку: строка ДОЛЖНА состоять хотя бы из одной буквы И/ИЛИ (арабского — в дальнейшем предполагается) числа, без учета регистра
Является ли значение почтовым индексом?
const isPostalCode = (str) => /^\d$/.test(str)
Обратите внимание: в большинстве случаев при валидации предполагается, что строка прошла предварительную очистку от пробелов и других символов, которых в ней быть не должно (см. ниже).
Читаем: значение ДОЛЖНО состоять (точно — в дальнейшем предполагается) из 6 цифр.
Является ли значение номером паспорта?
// оригинал из `validator.js` const passportNumber = /^\d\d\d$/ // можно упростить const _passportNumber = /^\d$/ const removeSpaces = (str) => str.replace(/\s+/g, '') const isPassportNumber = (str) => _passportNumber.test(removeSpaces(str))
- в самом регулярном выражении нет ничего нового — 10 цифр (или 2 цифры + 2 цифры + 6 цифр — в оригинале)
- removeSpaces() — утилита для удаления из строки пробелов, поскольку номер паспорта может выглядеть как 12 34 567890 , 1234 567890 и т.д.
- \s означает пробельный символ: пробел, табуляция, перенос строки и т.д. (символьный класс)
- g — глобальный поиск, т.е. будут обнаружены все пробелы, имеющиеся в строке, а не только первый (флаг)
Читаем: значение ДОЛЖНО состоять из 10 цифр.
Является ли значение числом (целым или с плавающей точкой/запятой)?
const numeric = (delimiter = '.') => new RegExp(`^[+-]?([0-9]*\\$)?[0-9]+$`) const isNumeric = (str, delimiter) => numeric(delimiter).test(str)
- numeric — функция, возвращающая регулярное выражение с указанным разделителем (delimiter), которым по умолчанию является символ . ; это один из тех немногих случаев, когда для создания регулярки используется объект RegExp , позволяющий создавать регулярное выражение динамически, в отличие от // , создающего статический шаблон
Обратите внимание: символ . необходимо экранировать с помощью \ или, в данном случае, с помощью \\ (чтобы не экранировать $ ). Экранирование превращает . в обычную точку, в противном случае, этот символ будет иметь специальное значение — любой символ. Мы также экранируем любой другой символ, переданный в функцию isNumeric() в качестве разделителя (побочный эффект), но это не страшно.
- ? — НОЛЬ или ОДИН предшествующий символ (квантификатор); по сути, применение этого квантификатора делает предшествующий символ опциональным (необязательным)
- * — НОЛЬ или БОЛЕЕ предшествующих символов (квантификатор); этот квантификатор также делает предшествующий символ необязательным
- [+-]? — необязательный + или —
- ([0-9]*\.)? — необязательная группа, которая МОЖЕТ состоять из числа и точки ИЛИ только из точки (или другого разделителя)
- [0-9]+ — хотя бы одна цифра
Читаем: значение ДОЛЖНО состоять хотя бы из одной цифры и МОЖЕТ включать знак + или — в начале строки, за которым МОЖЕТ следовать любое количество цифр и разделитель ИЛИ только разделитель
Является ли строка цветом в шестнадцатиричном формате?
const hexColor = /^#?([0-9A-F]|[0-9A-F]|[0-9A-F]|[0-9A-F])$/i const isHexColor = (str) => hexColor.test(str)
- длина регулярки может испугать, но это всего лишь повторяющийся шаблон
- #? — необязательный символ #
- [0-9A-F] — n любых чисел ИЛИ букв латиницы от A до F (без учета регистра)
- | — альтерация, ИЛИ (строка должна совпадать с одним из вариантов — наборов)
Читаем: строка ДОЛЖНА состоять ЛИБО из 3, ЛИБО из 4, ЛИБО из 6, ЛИБО из 8 букв латиницы от A до F (без учета регистра) И/ИЛИ цифр и МОЖЕТ включать символ # в начале
Является ли строка цветом в формате RGB или RGBA ?
// RGB без альфа-канала - прозрачности const rgbColorRegexp = /^rgb\((([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]),)([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\)$/ // RGBA с прозрасностью const rgbaColorRegexp = /^rgba\((([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5]),)(0?\.\d|1(\.0)?|0(\.0)?)\)$/ // RGB в процентах const rgbColorPercentRegexp = /^rgb\((([0-9]%|[1-9][0-9]%|100%),)([0-9]%|[1-9][0-9]%|100%)\)/ // RGBA в процентах const rgbaColorPercentRegexp = /^rgba\((([0-9]%|[1-9][0-9]%|100%),)(0?\.\d|1(\.0)?|0(\.0)?)\)/ // строка должна совпадать хотя бы с одним вариантом const isRgbColor = (str) => rgbColorRegexp.test(str) || rgbaColorRegexp.test(str) || rgbColorPercentRegexp.test(str) || rgbaColorPercentRegexp.test(str)
Является ли значение номером сотового телефона?
Функция (для России):
const mobilePhoneRegexp = /^(\+?7|8)?9\d$/ const removeNonDigits = (str) => replace(/\D+/g, '') const isMobilePhone = (str) => mobilePhoneRegexp.test(removeNonDigits(str))
- \+? — опциональный символ + ; обратите внимание на экранирование
- (\+?7|8)? — опциональная группа, которая МОЖЕТ состоять из символа + и числа 7 или 8 ИЛИ только из числа 7 или 8
- removeNonDigits() — утилита для удаления всех символов, которые не являются числом ( \D )
Читаем: строка ДОЛЖНА состоять из 10 цифр, первым из которых ДОЛЖНО БЫТЬ число 9 , и МОЖЕТ включать символ + и число 7 или 8 ИЛИ только число 7 или 8 в начале
Перед тем, как переходить к самой сложной части, поиграем с редко используемыми, но от того не менее интересными возможностями регулярных выражений.
// Определяем, является ли строка названием изображения в формате PNG, JPG, JPEG или SVG с помощью опережающей проверки // Строка ДОЛЖНА состоять из одного и более (любого) символа, включать точку и заканчиваться на `png`, `jpg`, `jpeg` или `svg` (без учета регистра) const isImage = (str) => /.+\.(?=png|jpe?g|gif|svg)/i.test(str) // Определяем, что строка НЕ является названием изображения в формате WebP или AVIF с помощью негативной опережающей проверки // ПОСЛЕ строки, состоящей из одного или более символа, НЕ ДОЛЖНО быть слова `webp` или `avif` (без учета регистра) const isNotWebpOrAvif = (str) => /.+\.(?!webp|avif)/i.test(str) // Что касается ретроспективной проверки, то я не смог придумать достойного примера - такого, который бы имел какие-то преимущества по сравнению с использованием метода `startsWith()` // Определяем, что значение является долларами США с помощью ретроспективной проверки // ПЕРЕД хотя бы одной цифрой ДОЛЖЕН быть символ `$` const isUSD = (str) => /(? <=\$)\d+/.test(str) // Определяем, что значение НЕ является евро с помощью негативной ретроспективной проверки // ПЕРЕД хотя бы одной цифрой НЕ ДОЛЖНО быть символа `€` const isNotEUR = (str) =>/(?` - тема для отдельного разговора, however. const isEmoji = (str) => /\p/u.test(str) // Универсальная функция для поиска символов на любом языке const languageAgnostic = /[\p\p\p\p\p]/u const isLanguageAgnosticSymbol = (str) => languageAgnostic.test(str)
Еще парочка полезных утилит
// Определяем, является ли строка HTML-тегом // [^>] - означает любой символ, кроме `>` // ([^>]+) - один или более таких символов const isHTMLTag = (str) => /(^<([^>]+)>$)/i.test(str) const tag = '' console.log(isHTMLTag(tag)) // true // Находим слова в одинарных или двойных (парных) кавычках // обратите внимание: в данном случае мы не учитываем, что кавычки могут быть экранированы const quotes = /(["'])([^"'])*\1/g const getQuottedWords = (str) => str.match(quotes) const str = `some string with "double quotes" and 'single quotes'` console.log(getQuottedWords(str).join('\n')) /* "double quotes" 'single quotes' */ // или так (короче, но также без учета экранирования) const _quotes = /(["']).*\1/g // или так (с учетом экранирования) const __quotes = /(["'])([^"'\\])*\1/g const _getQuottedWords = (str) => str.match(__quotes) const str = "some string with \"escaped double quotes\"" console.log(. _getQuottedWords(str)) // "escaped double quotes" // Определяем, является ли строка временем в формате ЧЧ:ММ:СС // Строка ДОЛЖНА начинаться с 1 с ведущим 0 или без него и любым числом (01-19) ИЛИ // 2 и любым числом в диапазане от 0 до 3 (20-23) - часы, // затем ДОЛЖНЫ следовать две группы, состоящие из двоеточия и любого двухзначного числа, // первая цифра которого ДОЛЖНА находиться в диапазоне от 0 до 5 (01-59) - часы и минуты const isTime = (str) => /([01]\d|2[0-3])(:[0-5]\d)$/.test(str) console.log(isTime('12:00')) // false console.log(isTime('12:00:00')) // true
Функция для удаления из строки ВСЕХ пробелов
const str = ' some string with crazy spaces ' const formatted = str .replace(/\s/g, ' ') .trim() .replace('crazy', 'normal') console.log(formatted) // 'some string with normal spaces'
Функция для форматирования даты
// В таком формате возвращается дата из `` const str = '2021-06-29' // допустим, что мы хотим преобразовать ее в привычный для нас формат - `ДД.ММ.ГГГГ` // С помощью обычных скобочных групп // определяем скобочные группы 1, 2 и 3 const regexp = /(\d)-(\d)-(\d)/ // производим замену const formatted = str.replace(regexp, '$3.$2.$1') console.log(formatted) // 29.06.2021 // С помощью именованных групп захвата // определяем группы `year`, `month` и `day`, // чтобы не запоминать (считать) номера групп (количество скобок) const _regexp = /(?\d)-(?\d)-(?\d)/ // производим замену const _formatted = str.replace(_regexp, '$.$.$') console.log(_formatted) // 29.06.2021 // С помощью функции-заменителя // это не имеет особого смысла, просто для примера const regexp = /(\d)-(\d)-(\d)/ const result = str.replace( regexp, // первый аргумент, который нам не нужен - это объект совпадения `match` (_, year, month, day) => `$.$.$` ) console.log(result) // 29.06.2021
Теперь поговорим о строках, к содержанию которых предъявляются особые требования. Под такими строками я подразумеваю URL (или, если угодно, URI ) и email . В принципе, сюда же можно отнести пароли, требования к которым предъявляются не нормативными документами ( RFC ), а клиентами/заказчиками/разработчиками.
URL
URL (Uniform Resource Locator — унифицированный указатель ресурса) — система унифицированных адресов электронных ресурсов, или единообразный определитель местонахождения ресурса.
Он состоит из следующих частей:
Требования, предъявляемые к «урлам», содержатся, в основном, в RFC 3986 . Согласно этому документу URL может содержать следующие символы:
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&'()*+,;=
Однако, при этом не уточняется, в какой части URL какие символы могут использоваться.
Также в названном документе приводится регулярка для разбора урлов (приложение B ), которая выглядит так (числа — это номера групп):
^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? 12 3 4 5 6 7 8 9
Попробуем ее применить:
const url = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/ const str = 'https://example.com/main?search#hash' const result = str.match(url) console.log(result) /* [ 0: "https://example.com/main?search#hash" 1: "https:" 2: "https" 3: "//example.com" 4: "example.com" 5: "/main" 6: "?search" 7: "search" 8: "#hash" 9: "hash" // . ] */
Данная регулярка отлично подходит для простых URL (как в приведенном примере), но в более сложных случаях лучше использовать конструктор URL() :
const str = 'https://user:pwd@example.com:80/main?query#anchor' const result = new URL(str) console.log(result) /* < hash: "#anchor" host: "example.com:80" hostname: "example.com" href: "https://user:pwd@example.com:80/main?query#anchor" origin: "https://example.com:80" password: "pwd" pathname: "/main" port: "80" protocol: "https:" search: "?query" >*/
Вот один из возможных вариантов регулярки для проверки URL с учетом протокола http(s) , а также без учета некоторых символов:
/ https?:\/\/ - `http` с необязательным `s` (www\.)? - опциональные `www` и `.` [-\w@:%\.\+~#=] - любые символы из набора в количестве 1-256 \. - точка [a-z0-9()] - домен размером от 2 символов \b - граница строки ([-\w()@:%\.\+~#=//?&]*) - поддомен, строка запроса, якорь и т.д. /i
const url = /https?:\/\/(www\.)?[-\w@:%\.\+~#=]\.[a-z0-9()]\b([-\w()@:%\.\+~#=//?&]*)/i const urls = [ 'http://www.example.ru', 'https://www.example.ru', 'http://example.ru', 'http://www.example.ru/main', 'htt://example.ru', 'www.example.ru', 'www.mp3.com', 'www.e.org', 'http://e.org', 'http://www.e.org', 'https://www.e.org', 'www.ab.com', 'http://ab.com', 'http://www.ab.com', 'https://www.ab.com', 'www.example', 'www.example-.ru', 'www.-example.ru', 'example.ru', 'http://www.example', 'http://example', 'www.mp3#.com' ] urls.forEach((u) => < if (url.test(u)) < console.log(`%cCorrect: $`, 'color: green;') > else < console.info(`%cWrong: $`, 'color: red;') > >) /* Correct: http://www.example.ru Correct: https://www.example.ru Correct: http://example.ru Correct: http://www.example.ru/main Wrong: htt://example.ru Wrong: www.example.ru Wrong: www.mp3.com Wrong: www.e.org Correct: http://e.org Correct: http://www.e.org Correct: https://www.e.org Wrong: www.ab.com Correct: http://ab.com Correct: http://www.ab.com Correct: https://www.ab.com Wrong: www.example Wrong: www.example-.ru Wrong: www.-example.ru Wrong: example.ru Wrong: http://www.example Wrong: http://example Wrong: www.mp3#.com */
Электронная почта — технология и служба по пересылке и получению электронных сообщений между пользователями компьютерной сети.
Адрес электронной почты ( email ) состоит из из следующих частей:
Что касается символов, которые могут использоваться в адресе электронной почты, то здесь ситуация довольно неоднозначная, поскольку существует большое количество RFC , по-разному регламентирующих этот вопрос. Относительно полный список этих RFC можно найти здесь.
В простейшем случае, регулярное выражение для определения того, является ли значение email , может выглядеть так:
// (хотя бы один (любой) символ)@(хотя бы один символ).(хотя бы один символ) const isEmail = (str) => /^(.+)@(.+)\.(.+)$/.test(str)
Или можно обойтись вообще без регулярки:
// преобразуем строку в массив const isEmail = ([. str]) => // проверяем, что в массиве есть символ `@`, он находится, как минимум, на второй позиции str.indexOf('@') > 0 && // и является единственным str.indexOf('@') === str.lastIndexOf('@') && // проверяем, что в массиве есть точка, str.indexOf('.') > 0 && // она стоит после символа `@` str.indexOf('@') < str.indexOf('.') && // и не является последним символом str.indexOf('.') < str.length - 1
Однако, любой символ — это как any в TypeScript — обуславливает ненадежность регулярного выражения. Но, с повышением точности регулярки, ее сложность растет в геометрической прогрессии.
Вот гораздо более продвинутый (надежный) пример:
/^ ( ( [^<>()[\]\\.,;:\s@"]+ - один или более символ, кроме указанных (\. - точка [^<>()[\]\\.,;:\s@"]+ - один или более символ, кроме указанных ) * - ноль или более ) | - ИЛИ (".+") - один или более символ в двойных кавычках ) @ - символ `@` ( ( \[ - открывающая квадратная скобка [0-9] - от 1 до 3 цифр \. - точка [0-9] \. [0-9] \. [0-9] \] - закрывающая квадратная скобка ) | - ИЛИ ( ( [-a-z0-9]+ - одна или более буква латиницы, цифра или символ `-` \. - точка )+ - один или более [a-z] - хотя бы `2` буквы латиницы ) ) $/i - без учета регистра
const email = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]\.[0-9]\.[0-9]\.[0-9]\])|(([a-z\-0-9]+\.)+[a-z]))$/i const isEmail = (str) => email.test(str) // тестируем // вместо `@`, написали `_` console.log(isEmail('my_mail.com')) // false // пропустили `.` console.log(isEmail('my@mailcom')) // false // пропустили начало console.log(isEmail('@mail.com')) // false // ок console.log(isEmail('my@mail.com')) // true
Пароль
К паролю могут предъявляться самые разные требования. Как правило, среди таких требований значится следующее:
- определенная длина или диапазон пароля, т.е. ограничение минимального ИЛИ минимального и максимального количества символов
- минимум одна большая буква
- минимум одна маленькая буква
- минимум одна цифра
- минимум один спецсимвол
Эти требования можно комбинировать. Переведем их на язык регулярных выражений:
// от 8 символов // от 8 до 20 символов (?=.*\d) // минимум одна цифра (?=.*[a-z]) // минимум одна буква в нижнем регистре (?=.*[A-Z]) // минимум одна буква в верхнем регистре (?=.*[-#!$@%^&*_+~=:;?\/]) // минимум один символ из набора
const password = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[-#!$@%^&*_+~=:;?\/])[-\w#!$@%^&*+~=:;?\/]$/ const isPassword = (str) => password.test(str) // буквы в верхнем и нижнем регистре console.log(isPassword('Password')) // false // + число console.log(isPassword('Passw0rd')) // false // + спецсимвол, но недостаточная длина console.log(isPassword('Pas_w0r')) // false // ok console.log(isPassword('Pas$_W0rd')) // true
Не забывайте модифицировать [-\w#!$@%^&*+~=:;?\/] при добавлении/удалении групп.
Номер карты
Напоследок, рассмотрим один интересный алгоритм — алгоритм Луна, который используется для вычисления контрольной цифры номера пластиковой карты в соответствии со стандартом ISO/IEC 7812 с целью ее (номера) валидации.
В упрощенном виде этот алгорит включает следующие шаги:
- Цифры проверяемой последовательности нумеруются справа налево.
- Цифры, оказавшиеся на нечетных местах, остаются без изменений.
- Цифры, стоящие на четных местах, умножаются на 2.
Обратите внимание: речь идет не о "четности" числа, а о четности его позиции в строке.
- Если в результате такого умножения возникает число больше 9, оно заменяется суммой цифр получившегося произведения — однозначным числом, то есть цифрой.
- Все полученные в результате преобразования цифры складываются. Если сумма кратна 10, то исходные данные верны.
Реализация алгоритма из Википедии:
function luhnAlgorithm(value) < value = value.replace(/\D/g, '') // контрольная сумма let nCheck = 0 // индикатор "четности" позиции числа let bEven = false // перебираем числа в обратном порядке for (let n = value.length - 1; n >= 0; n--) < // извлекаем число из строки // позиция первого извлеченного числа является нечетной let nDigit = parseInt(value.charAt(n), 10) // если позиция числа четная и при умножении на 2 число становится больше 9, // вычитаем из числа 9 // результат будет таким же, как при сложении цифр, из которых состоит число // например, `6 * 2 = 12`, `1 + 2 = 3` и `12 - 9 = 3` if (bEven && (nDigit *= 2) >9) < nDigit -= 9 >// прибавляем число к сумме nCheck += nDigit // инвертируем индикатор bEven = !bEven > // если контрольная сумма делится на 10 без остатка, // значит, номер карты является валидным return nCheck % 10 == 0 >
validator.js предлагает такой вариант рассматриваемого алгоритма:
const creditCard = /^(?:4[0-9](?:[0-9])?|5[1-5][0-9]|(222[1-9]|22[3-9][0-9]|2[3-6][0-9]|27[01][0-9]|2720)[0-9]|6(?:011|5[0-9][0-9])[0-9]|3[47][0-9]|3(?:0[0-5]|[68][0-9])[0-9]|(?:2131|1800|35\d)\d|6[27][0-9])$/ function isCreditCard(str) < const sanitized = str.replace(/[- ]+/g, '') if (!creditCard.test(sanitized)) < return false >let sum = 0 let digit let tmpNum let shouldDouble for (let i = sanitized.length - 1; i >= 0; i--) < digit = sanitized.substring(i, i + 1) tmpNum = parseInt(digit, 10) if (shouldDouble) < tmpNum *= 2 if (tmpNum >= 10) < sum += (tmpNum % 10) + 1 >else < sum += tmpNum >> else < sum += tmpNum >shouldDouble = !shouldDouble > return !!(sum % 10 === 0 ? sanitized : false) >
Попробуем реализовать этот алгоритм в одну строку:
// функция принимает строку const isCreditCard = (str) => // строка не должна быть пустой // можно воспользоваться нашей функцией `!isEmpty()` или str.trim().length !== 0 && str // удаляем из строки все НЕ числа .replace(/\D/g, '') // преобразуем строку в массив .split('') // меняем порядок следования элементов массива на противоположный .reverse() // s - контрольная сумма // c - текущий символ строки, начиная с последнего // i - индекс элемента .reduce( (s, c, i) => (s += // если индекс элемента - нечетное число (1, 3 и т.д.), // значит, число находится на четной позиции, умножаем его на 2 // если при этом число становится больше 9, вычитаем из него 9 // преобразуем результат в число с помощью `+` +(i % 2 !== 0 && (c *= 2) > 9 ? (c -= 9) : c)), 0) // начальная контрольная сумма // если контрольная сумма делится на 10 без остатка, // значит, номер карты является валидным % 10 === 0 console.log(isCreditCard('1234 5678 9009 8765')) // false console.log(isCreditCard('5555 5555 5555 4444')) // true
Как видите, регулярные выражения — тема, конечно, сложная, но довольно интересная и чрезвычайно полезная. Редкий проект обходится без необходимости очистки или валидации данных, вводимых пользователем и (в меньшей степени, но все же) "прилетающих" с сервера. Владение языком регулярных выражений существенно облегчает решение многих задач, возникающих в веб-разработке.
Обратите внимание: регулярные выражения предоставляют широкий простор для творчества, поэтому приведенные в статье паттерны можно реализовать совершенно по-разному, главный вопрос в том, решает ли регулярное выражение поставленную перед ней задачу и насколько хорошо она это делает, т.е. насколько регулярка является надежной и как сильно ее использование влияет на производительность (часто это влияние оказывается критичным даже в случае с простыми шаблонами).
Подборка функций для валидации, реализацией которых мы занимались на протяжении статьи:
const isEmpty = (str) => !str.trim().length const isBoolean = (str) => ['true', 'false', '1', '0'].indexOf(str) > -1 const isAlpha = /^[A-ZА-ЯЁ]+$/i.test(str) const isAlphaNumeric = (str) => /^[0-9A-ZА-ЯЁ]+$/i.test(str) const numeric = (delimiter = '.') => new RegExp(`^[+-]?([0-9]*\\$)?[0-9]+$`) const isNumeric = (str, delimiter) => numeric(delimiter).test(str) const isPostalCode = (str) => /^\d$/.test(str) const isPassportNumber = (str) => /^\d$/.test(str) const isMobilePhone = (str) => /^(\+?7|8)?9\d$/.test(str) const isURL = (str) => /https?:\/\/(www\.)?[-\w@:%\.\+~#=]\.[a-z0-9()]\b([-\w()@:%\.\+~#=//?&]*)/i.test(str) const isEmail = (str) => /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]\.[0-9]\.[0-9]\.[0-9]\])|(([a-z\-0-9]+\.)+[a-z]))$/i.test(str) const isStrongPassword = (str) => /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[-#!$@%^&*_+~=:;?\/])[-\w#!$@%^&*+~=:;?\/]$/.test(str) const isCreditCard = (str) => str.trim().length !== 0 && str .replace(/\D/g, '') .split('') .reverse() .reduce((s, c, i) => (s += +(i % 2 !== 0 && (c *= 2) > 9 ? (c -= 9) : c)), 0) % 10 === 0
Это все, о чем я хотел вам сегодня рассказать. Приветствуются любые замечания, дополнения и предложения.
Купить VPS-хостинг с быстрыми NVMе-дисками и посуточной оплатой у хостинга Маклауд.
