Статьи

Регулярные выражения в JavaScript

Эта статья была обновлена ​​2 июня 2016 года, чтобы добавить демонстрации CodePen, обновить примеры кода, переписать заключение и устранить проблемы с форматированием.

Если вы когда-нибудь программировали на Perl или вам приходилось работать системным администратором Unix, то вы, вероятно, уже давно знакомы с регулярными выражениями . Но даже если Perl выглядит для вас спагетти, и вы чувствуете, что ни один простой смертный не может управлять системой Unix, тот факт, что JavaScript включает поддержку регулярных выражений в стиле Perl, является хорошей новостью … поверьте мне!

В этой статье я начну с объяснения, что такое регулярные выражения и что они могут сделать для вас. Затем я представлю обзор наиболее распространенных функций регулярных выражений (которые поклонники Perl в аудитории могут безопасно пропустить). Наконец, я закончу объяснением того, как регулярные выражения используются в JavaScript, с практическим примером или двумя, чтобы помочь гелевым понятиям. К концу этой статьи вы, вероятно, все еще будете простым смертным, но вы наверняка сможете произвести впечатление на вечеринках своими новыми навыками жонглирования текстом!

Кто они такие?

Регулярные выражения используют специальные (и на первый взгляд несколько запутанные) коды для обнаружения шаблонов в строках текста. Например, если вы предоставляете своим посетителям HTML-форму для ввода их информации, у вас может быть одно поле для номера телефона. Теперь давайте посмотрим правде в глаза: некоторые посетители сайта лучше следуют инструкциям, чем другие. Даже если вы поместите небольшую подсказку рядом с текстовым полем, указывающим требуемый формат номера телефона (например, «(XXX) XXX-XXXX» для номеров в Северной Америке), некоторые люди поймут это неправильно. Написание скрипта для проверки каждого символа введенной строки, чтобы убедиться, что все числа находятся там, где они находятся, с круглыми скобками и тире в правильных местах, было бы довольно утомительным кодом для написания. А телефонный номер — это относительно простой случай! Что если вам нужно проверить, действительно ли пользователь ввел адрес электронной почты или, что еще хуже, URL?

Регулярные выражения обеспечивают быстрый и простой способ сопоставления строки с шаблоном. В нашем примере с номером телефона мы могли бы написать простое регулярное выражение и использовать его, чтобы одним быстрым шагом проверить, является ли любая заданная строка правильно отформатированным номером телефона. Мы рассмотрим этот пример чуть дальше, как только позаботимся о нескольких технических деталях.

Как они выглядят?

Регулярные выражения иногда могут выглядеть довольно сложными, но когда дело доходит до этого, на самом деле они просто текстовые строки. Следующее, например, является регулярным выражением, которое ищет текст «JavaScript» (без кавычек):

JavaScript 

Не так много, не так ли? Говорят, что любая строка, содержащая текст «JavaScript», соответствует этому регулярному выражению. Таким образом, это регулярное выражение позволяет нам обнаруживать строки, содержащие эту конкретную строку текста. Что ж, плохая новость в том, что это не всегда так просто.

Как я упоминал выше, существуют специальные коды, которые можно использовать в регулярных выражениях. Некоторые из них могут быть совершенно запутанными и трудными для запоминания, поэтому, если вы намерены широко их использовать, вы можете найти для себя хороший справочник. К счастью, JavaScript поддерживает тот же синтаксис регулярных выражений, что и Perl, и существует множество веб-сайтов, документирующих регулярные выражения Perl. Один из лучших здесь . Там также документация, приведенная в официальном руководстве по Perl . Оба они загадочны в некоторых местах, так что вы можете обратиться к обоим сразу.

Давайте разберем несколько примеров, чтобы изучить базовый синтаксис регулярных выражений.

Основной синтаксис

Прежде всего, символ каретки ( ^ ) может использоваться для обозначения начала строки, а знак доллара ( $ ) используется для обозначения конца:

 JavaScript // Matches "Isn't JavaScript great?" ^JavaScript // Matches "JavaScript rules!", // not "What is JavaScript?" JavaScript$ // Matches "I love JavaScript", // not "JavaScript is great!" ^JavaScript$ // Matches "JavaScript", and nothing else 

Очевидно, что иногда вам может понадобиться использовать ^ , $ или другие специальные символы для представления соответствующего символа в строке поиска, а не специального значения, подразумеваемого синтаксисом регулярного выражения. Чтобы удалить особое значение символа, добавьте перед ним обратную косую черту:

 \$\$\$ // Matches "Show me the $$$!" 

Квадратные скобки могут использоваться для определения набора символов, которые могут совпадать. Например, следующее регулярное выражение будет соответствовать любой цифре от 1 до 5 включительно.

 [12345] // Matches "1" and "3", but not "a" or "12" 

Диапазоны цифр и букв также могут быть указаны.

 [1-5] // Same as the previous example [az] // Matches any lowercase letter (from the English alphabet) [0-9a-zA-Z] // Matches any letter or digit 

Поместив ^ сразу после открывающей квадратной скобки, вы можете инвертировать набор символов, то есть набор будет соответствовать любому символу, не указанному в списке:

 [^a-zA-Z] // Matches anything except a letter 

Персонажи ? , + и * также имеют специальные значения. Конкретно ? означает «предыдущий символ является необязательным», + означает «один или несколько предыдущих символов», а * означает «ноль или более предыдущих символов».

 bana?na // Matches "banana" and "banna", // but not "banaana". bana+na // Matches "banana" and "banaana", // but not "banna". bana*na // Matches "banna", "banana", and "banaaana", // but not "bnana". ^[a-zA-Z]+$ // Matches any string of one or more // letters and nothing else. 

Скобки могут быть использованы для группировки строк, чтобы применить ? , + или * к ним в целом.

 ba(na)+na // Matches "banana" and "banananana", // but not "bana" or "banaana". 

Скобки также позволяют вам определить несколько строк, которые могут совпадать, используя символ канала ( | ) для их разделения.

 ^(ba|na)+$ // Matches "banana", "nababa", "baba", // "nana", "ba", "na", and others. 

Вот несколько специальных кодов, которые можно использовать для сопоставления символов в регулярных выражениях:

 . // Any character except a newline \n // A newline character \r // A carriage return character \t // A tab character \b // A word boundary (the start or end of a word) \B // Anything but a word boundary \d // Any digit (same as [0-9]) \D // Anything but a digit (same as [^0-9]) \s // Single whitespace (space, tab, newline, etc.) \S // Single nonwhitespace \w // A "word character" (same as [A-Za-z0-9_]) \W // A "nonword character" (same as [^A-Za-z0-9_]) 

Обратите внимание, что в отличие от специальных символов, которые мы рассматривали ранее, некоторые (например, те, что указаны выше) необходимо экранировать с помощью обратной косой черты, чтобы их обрабатывали как специальные коды.

Есть более специальные коды и синтаксические трюки для регулярных выражений, которые должны быть описаны в любой полной ссылке. На данный момент у нас более чем достаточно для наших целей.

Использование регулярных выражений в JavaScript

Использование регулярных выражений в JavaScript настолько просто, что удивительно, что многие люди не знают, что это можно сделать. Вы можете создать регулярное выражение в JavaScript следующим образом:

 var myRE = /regexp/; 

Где regexp — это код регулярного выражения, как описано выше. Например, следующее создает первое примерное регулярное выражение, которое я представил в предыдущем разделе, которое обнаруживает строку «JavaScript»:

 var myRE = /JavaScript/; 

Аналогично, вот как создать последний пример:

 var myRE = /^(ba|na)+$/; 

По умолчанию регулярные выражения JavaScript чувствительны к регистру и ищут только первое совпадение в любой заданной строке. Добавив модификаторы g (для глобальных ) и i (для игнорирования регистра ) после второй / , вы можете выполнять регулярное выражение для поиска всех совпадений в строке и игнорировать регистр соответственно. Вот несколько примеров регулярных выражений. Для каждого я указал, какой части строки "test1 Test2 TEST3" они будут соответствовать:

RegExp Спички):
/ Тест [0-9] + / Только «Test2»
/ Тест [0-9] + / I Только «test1»
/ Тест [0-9] + / г «Test1», «Test2» и «TEST3»

Используя регулярные выражения легко. Каждая переменная JavaScript, содержащая текстовую строку, поддерживает три метода (или функции, если вы не привыкли к объектно-ориентированной терминологии) для работы с регулярными выражениями: match() , replace() и search() .

совпадение()

match() принимает регулярное выражение в качестве параметра и возвращает массив всех соответствующих строк, найденных в рассматриваемой строке. Если совпадений не обнаружено, match() возвращает false. Возвращаясь к нашему первоначальному примеру, предположим, что нам нужна функция, которая может проверять, что строка, введенная пользователем в качестве его или ее номера телефона, имеет форму (XXX) XXX-XXXX. Следующий код поможет вам:

 function checkPhoneNumber(phoneNo) { var phoneRE = /^\(\d\d\d\) \d\d\d-\d\d\d\d$/; if (phoneNo.match(phoneRE)) { return true; } else { alert( "The phone number entered is invalid!" ); return false; } } 

В качестве первого порядка бизнеса эта функция определяет регулярное выражение. Давайте разберемся, чтобы понять, как это работает. Регулярное выражение начинается с ^ , чтобы указать, что любое совпадение должно начинаться с начала строки. Далее следует \( это будет просто соответствовать открывающей скобке. Мы добавили перед символом обратную косую черту, чтобы удалить его специальное значение в синтаксисе регулярного выражения (чтобы отметить начало набора альтернатив для сопоставления). Как упоминалось ранее, \d специальный код, который соответствует любой цифре, таким образом, \d\d\d соответствует любым трем цифрам. Мы могли бы написать [0-9][0-9][0-9] для достижения того же эффекта, но это короче . Остальная часть шаблона должна быть достаточно понятной. \) Соответствует закрывающей скобке, пробел соответствует пробелу, который должен быть оставлен в номере телефона, затем \d\d\d-\d\d\d\d соответствует трем цифрам, за которыми следует тире, а затем еще четыре цифры. Наконец, $ указывает, что любое совпадение должно заканчиваться в конце строки.

Кстати, мы могли бы сократить это регулярное выражение до следующего, используя другой ярлык, который мы не упомянули выше. Если вы видите, как это работает, вы естественны!

 var phoneRE = /^\(\d{3}\) \d{3}-\d{4}$/; 

Затем наша функция проверяет, имеет ли значение phoneNo.match(phoneRE) значение true или false . Другими словами, он проверяет, соответствует ли строка, содержащаяся в phoneNo нашему регулярному выражению (таким образом, возвращает массив, который в JavaScript будет иметь значение true ). Если совпадение обнаружено, наша функция возвращает true чтобы подтвердить, что строка действительно является номером телефона. Если нет, отображается сообщение, предупреждающее о проблеме, и функция возвращает значение false .

Наиболее часто используемым для этого типа функций является проверка пользовательского ввода в форме перед тем, как разрешить ее отправку. Вызвав нашу функцию в onSubmit события onSubmit для формы, мы можем предотвратить onSubmit формы, если введенная информация не отформатирована должным образом. Вот простой пример, демонстрирующий использование нашей функции checkPhoneNumber() :

 <form action="..."> <label>Enter phone number (eg (123) 456-7890): <input type="text" name="phone"> </label> <input type="submit"> </form> <script> var form = document.querySelector('form'); form.addEventListener('submit', function() { return checkPhoneNumber(this.phone.value); }); </script> 

Пользователь не сможет отправить эту форму, пока не будет введен номер телефона. Любая попытка сделать это приведет к появлению сообщения об ошибке, сгенерированного нашей checkPhoneNumber() .

заменить ()

Как следует из его названия, replace() позволяет заменить совпадения с данным регулярным выражением какой-либо новой строкой. Допустим, вы были орфографом и хотели применить старую поговорку «Я до Е, кроме С», чтобы исправить такие орфографические ошибки как «acheive» и «cieling». Нам нужна функция, которая принимает строку и выполняет две операции поиска и замены. Первый заменит «cie» на «cei».

Вот код:

 theString = theString.replace(/cie/gi,"cei"); 

Достаточно просто, верно? Первый параметр — это регулярное выражение, которое мы ищем (обратите внимание, что мы установили его как «игнорировать регистр» и быть «глобальным», чтобы он обнаруживал все вхождения, а не только первый), а второй параметр — строка, которой мы хотим заменить любые совпадения.

Вторая замена немного сложнее. Мы хотим заменить «xei» на «xie», где «x» — любая буква, кроме «c». Регулярное выражение для обнаружения экземпляров «xei» довольно легко понять:

 /[abd-z]ei/gi 

Он просто обнаруживает любую букву, кроме «c» («a», «b» и «d» — «z» включительно), за которой следует «ei», и делает это глобально, без учета регистра.

Сложность заключается в определении нашей замещающей строки. Очевидно, мы хотим заменить совпадение на «xie», но сложность заключается в написании «x». Помните, мы должны заменить ‘x’ любой буквой в соответствующей строке. Для этого нам нужно освоить новый трюк.

Ранее я показал вам, как можно использовать скобки для определения набора альтернатив в регулярном выражении (например, ^(ba|na)+$ ). Как оказалось, скобки имеют и другое значение. Они позволяют нам «помнить» часть совпадения, чтобы мы могли использовать ее в строке замены. В этом случае мы хотим запомнить часть совпадения, которая соответствует [abd-z] в регулярном выражении. Таким образом, мы окружаем его круглыми скобками:

 /([abd-z])ei/gi 

Теперь, при указании строки замены, мы помещаем $1 где мы хотим вставить часть строки, соответствующую части регулярного выражения в скобках. Таким образом, код для выполнения необходимых замен выглядит следующим образом:

 theString = theString.replace(/([abd-z])ei/gi,"$1ie"); 

Подводя итог, вот полная функция для выполнения нашей автокоррекции:

 function autoCorrect(theString) { theString = theString.replace(/cie/gi,"cei"); theString = theString.replace(/([abd-z])ei/gi,"$1ie"); return theString; } 

Прежде чем перейти на эту страницу и использовать эту функцию, поймите, что есть исключения из правила «Я до Е, кроме С». Странно, а?

Функция search() похожа на хорошо известную indexOf() , за исключением того, что она принимает регулярное выражение вместо строки. Затем он ищет строку для первого совпадения с данным регулярным выражением и возвращает целое число, указывающее позицию в строке (например, 0, если совпадение находится в начале строки, 9, если совпадение начинается с 10-го символа в строке ). Если совпадений не найдено, функция возвращает значение -1.

 var theString = "test1 Test2 TEST3"; theString.search(/Test[0-9]+/); // 6 

Подводя итоги

Регулярные выражения являются бесценным инструментом для проверки ввода пользователя. Используя поддержку регулярных выражений в JavaScript, эту проверку можно выполнять по мере ввода данных, обеспечивая более плавный пользовательский интерфейс ( Примечание: проверка на стороне сервера все еще необходима для обеспечения безопасности, а также для удовлетворения ситуаций, когда JavaScript недоступен . )