Если вам нужно использовать регулярные выражения при создании сайта, вовсе не обязательно прибегать к использованию Perl или PHP, — JavaScript вполне может справится с этой работой. В этой статье мы рассмотрим объект RegExp в JavaScript и его методы. Я уверен, что после прочтения, JaveScript представится вам в другом свете.
Настоящий Мир
Большинство Настоящих Программистов относятся к JavaScript, словно к бедной родственнице из деревни — иногда полезна, но не очень важна. Настоящих Программистов не интересует язык, чье предназначение в том, чтобы заменять одно изображение другим или рисовать след за мышью по веб-странице. У Настоящих Программистов есть более важные дела.
Что ж, JavaScript ограничен стороной клиента в веб-транзакции, но на этой территории, он безусловный король. Нет языка, настолько простого к изучению или дающего возможность сделать столько полезных вещей с минимальными усилиями. И одно из того, что вы можете делать, это использовать регулярные выражения.
По ходу этой статьи, я собираюсь дать вам представление о регулярных выражениях в контексте JavaScript. Я покажу, как использовать объект String в JavaScript для простейших возможностей поиска соответствия или осуществления замены, так же, как и для более сложных действий со строками. И я представлю вам объект RegExp, который предоставляет удобный путь для создания более эффективного кода распространенной проверки входящих данных на стороне клиента. Итак, приступим; независимо от того, Настоящий ли вы Программист, или только пытаетесь им стать, я уверен, что вы найдете для себя кое-что полезное ниже.
Входим в Матрицу
Регулярные выражения, также известные как «регэкспы» среди программистов — мощный инструмент, использующийся для поиска по шаблону и замены подстрок. Они тесно связаны с почти всеми основанными на *nix утилитами, включая редакторы, вроде vi, скриптовые языки вроде Perl или PHP и консольными программами, такими, как awk и sed.
Регулярные выражения дают вам возможность построить шаблоны, используя набор специальных символов; эти шаблоны затем могут сопоставляться с текстом из файла, введенной в приложение датой или данными из формы, заполненной пользователем на сайте. В зависимости от того, есть ли совпадение, или нет, принимается соответствующее действие и вызывается соответственный программный код.
Например, одно из самых распространенных приложений регулярных выражений — проверка того, верен или нет введенным в форму пользователем адрес электронной почты; если да, форма будет принята, а если нет, появится сообщение, просящее пользователя исправить ошибку. Здесь регулярные выражения играют большую роль в процедурах интернет-приложений, принимающих решение, хотя, как вы увидите позже, они также могут иметь огромный эффект в сложных операциях поиска и замены.
Регулярное выражение обычно имеет вид, подобный этому:
/matrix/
Нахождение шаблона «matrix» это все, что оно делает. Как насчет чего-то более сложного? Попробуйте это:
/mat+/
Это найдет слова «matting» и «mattres», но не «matrix». Почему? Потому что символ «+» используется для вхождения одного или более символа, расположенного перед ним. В нашем случае, за символами «ma» следует одно или более вхождение буквы «t».
Также есть мета-символы (это официальный термин), похожие на «+»: «*» и «?». Они используются для обозначения от нуля и более количества вхождений и нуля или одного вхождения предыдущего символа, соответственно. Поэтому:
/eg*/
может найти «easy», «egocentric» и «egg», в то время, как
/Wil?/
может найти «Winne», «Wimpy», «Wilson» и «William», но не «Wendy» или «Wolf». Если все это будет немного неточным, вы можете также указать интервал чисел совпадений. Например, регулярное выражение
/jim{2,6}/
может найти «jimmy» и «jimmmmmy!», но не «jim». Числа в фигурных скобках указывают минимальное и максимальное значения интервала соответствия; вы можете не указывать верхний предел для бесконечного количества соответствий.
Двое для Танго
А сейчас вы узнаете, какие регулярные выражения есть, давайте посмотрим на их использование в скрипте. Объект String в JavaScript дает набор методов, которые поддерживают регулярные выражения. Первый из них — это метод search(), используемый для поиска строки для соответствия определенному регулярному выражению. Посмотрите на следующий пример:
<script language="JavaScript"> // определяем строку для поиска var str = "The Matrix"; // определяем шаблон поиска var pattern = /trinity/; // ищем и возвращаем результат if(str.search(pattern) == -1) { alert("Тринити не в Матрице"); } else { alert("Трините в Матрице на символе " + str.search(pattern)); } </script>
Если вы выполните этот скрипт, вы увидете следующее:
Тринити не в Матрице
Метод search() возвращает позицию подстроки, соответствующую регулярному выражению или -1 в случае отсутствия такого соответствия. В нашем примере видно, что шаблона «trinity» в строке «The Matrix» нет, поэтому мы и получаем сообщение об ошибке. А теперь посмотрим, что будет, если изменить регулярное выражения так, чтобы результат был положительным:
<script language="JavaScript"> // определяем строку для поиска var str = "The Matrix"; // определяем шаблон поиска var pattern = /tri/; // ищем и возвращаем результат if(str.search(pattern) == -1) { alert("Тринити не в Матрице"); } else { alert("Трините в Матрице на символе " + str.search(pattern)); } </script>
В этот раз, интерпретатор JavaScript найдет соответствие (и место, где его нашел). Вот результат:
Trinity located in The Matrix at character 7
Игра, Установка, Соответствие
Объект String также предоставляет метод match(), который может расцениваться, как близкий родственник метода search(). В чем же разница? Что ж, вы уже увидели, что метод search() возвращает позицию, где находится соответствие. Метод match() работает немного по-другому: он применяет шаблон к строке и возвращает массив найденных значений.
Смущены? Посмотрите на следующий пример:
<script language="JavaScript"> // определяем строку var str = "Mississippi"; // определяем шаблон var pattern = /is./; // проверяем на вхождение // помещаем результат в массив var result = str.match(pattern); // display matches for(i = 0; i < result.length; i++) { alert("Соответствие #" + (i+1) + ": " + result[i]); } </script>
Просмотрите этот пример в браузере, и вы получите сообщение, показывающее первый результат соответствия. Вот такой:
Соответствие #1: iss
В этом примере, я залал регулярное выражение «is.». Он найдет строку «is», за которой следует любой символ (оператор «.» в конце шаблона находит все что угодно в строке). Если вы посмотрите на строку, в которой мы производили поиск, вы увидите, что в ней есть два вхождения этого шаблона. В то время, как код возвращает только 1.
Почему?
Ответ прост — я «забыл» добавить модификатор «g» (для поиска одного и более вхождений) в шаблон. В отличие от следующего примера, который отличается от предыдущего тем, что в нем добавлен этот оператор:
<script language="JavaScript"> // определяем строку var str = "Mississippi"; // определяем шаблон // и глобальный модификатор var pattern = /is./g; // проверяем на вхождение // помещаем результат в массив var result = str.match(pattern); // display matches for(i = 0; i < result.length; i++) { alert("Соответствие #" + (i+1) + ": " + result[i]); } </script>
И теперь, когда вы попробуете этот пример, вы должны увидеть два сообщения, показывающих нахождение двух вхождений этого шаблона в строку. Добавленный модификатор «g» обеспечивает нахождение всех вхождений шаблона в строку и сохранение в возвращаемый массив. Далее в этой статье, я покажу вам несколько других модификаторов.
Найти и Уничтожить
Предыдущий набор примеров демонстрировал возможности поиска объекта String. Но это ещё не все! Вы также можете осуществлять операции поиска и замены с помощью метод replace(), который принимает регулярное выражение и значение для его замены. Вот так:
<script language="JavaScript"> // определяем строку var str = "Welcome to the Matrix, Mr. Anderson"; // заменяем одну строку на другую str = str.replace(/Anderson/,"Smith"); // показываем новую строку alert(str); </script>
Если вы загрузите этот пример в браузере, вы увидите, что подстрока «Anderson» была занесена строкой «Smith». Что и иллюстрирует результат:
Welcome to the Matrix, Mr. Smith
Помните, как мы использовали модификатор «g» для поиска нескольких вхождений шаблона в строку? Переходим на следующую ступень — вы можете использовать его для замены нескольких вхождений шаблона в строку:
<script language="JavaScript"> // определяем строку var str = "yo ho ho and a bottle of gum"; // возвращает "yoo hoo hoo and a bottle of gum" alert(str.replace(/os/g, "oo ")); </script>
Здесь мета-символ «s» обозначает пробелы после «yo» и «ho» и заменяет на «oo».
Также, вы можете использовать нечувствительный к регистру поиск по шаблону — просто добавьте модификатор «i» в конце шаблона. Следующий пример демонстрирует, как это делается:
<script language="JavaScript"> // определяем строку var str = "he He hE HE"; // возвращает ho ho ho ho alert(str.replace(/he/gi, "ho")); </script>
Разделяя
Объект String также предоставляет метод split(), который может быть использован для разделения одной строки на отдельные части на основе особого значения разделения. Эти части затем помещаются в массив для дальнейшей обработки. Демонстрирует это следующий пример:
<script language="JavaScript"> // определяем строку var friends = "Joey, Rachel, Monica, Chandler, Ross, Phoebe"; // разделяем на части с помощью запятых var arr = friends.split(", "); // проходим по массиву и выводим // каждое значение for (x=0; x < arr.length; x++) { alert("Hiya, " + arr[x]); } </script>
В JavaScript версии 1.1 и ниже, вы можете использовать только строковые значения в качестве разделителей. JavaScript 1.2 меняет все это, теперь вы можете разделять строки даже на основе регулярных выражений.
Чтобы лучше это понять, рассмотрим следующую строку, которая демонстрирует распространенную проблему: неравное количество пробелов между значениями разделения:
Neo | Trinity |Morpheus | Smith| Tank
Здесь символ «|» используется для разделения различных имен. И количество пробелов между разными «|» разное — это означает, что прежде, чем вы сможете использовать разные элементы строки, вы вынуждены удалить лишние пробелы вокруг них. Разделение с использованием регулярного выражения в качестве разделителя является элегантным решением этой проблемы, что мы и видим на следующем примере:
<script language="JavaScript"> // определяем строку var str = "Neo| Trinity |Morpheus | Smith| Tank"; // определяем шаблон var pattern = /s*|s*/; // разделяем строку с помощью // регулярного выражения в // качестве разделителя result = str.split(pattern); // проходим получившийся массив for(i = 0; i < result.length; i++) { alert("Символ #" + (i+1) + ": " + result[i]); } </script>
Результатом работы этого кода будет массив, содержащий имена, без всякого удаления пробелов.
Объекты в зеркале заднего вида
Итак, все примеры в этой статье связаны с объектом String для демонстрации мощи реализации регулярных выражений в JavaScript. Но JavaScript также предоставляет базовый объект, RegExp, смысл существования которого — поиск по шаблону в строках и переменных.
Этот объект имеет три полезных метода. Вот они:
test() — проверяет строку на вхождение по шаблону.
exec() — возвращает массив найденных вхождений в строке, позволяя расширенную работу с регулярными выражениями
compile() — после того, как регулярное выражение связано с объектом RegExp.
Давайте рассмотрим простой пример:
<script language="JavaScript"> // определяем строку var str = "The Matrix"; // создаем объект RegExp var character = new RegExp("tri"); // ищем по шаблону в строке if(character.test(str)) { alert("User located in The Matrix."); } else { alert("Sorry, user is not in The Matrix."); } </script>
Это похоже на один из самых первых примеров этой статье. Тем не менее, как вы видите, он имеет совершенно другую реализацию.
Основное отличие находится в том, что создается объект RegExp для поиска с помощью регулярного выражения. Он создается с помощью ключевого слова «new», следующего за конструктором объекта. По определению, конструктор принимает два параметра: шаблон для поиска и модификаторы, если они имеют место быть (в этом примере их нет).
Следующим шагом после создания объекта, является его использование. Здесь мы использовали метод test() для поиска вхождения по шаблону. По умолчанию, этот метод принимает строковую переменную и сравнивает её с шаблоном, переданным в конструктор объекта. В случае нахождения соответствия, он возвращает true, в противном же случае false. Очевидно, что это более логичная реализация, чем использование метода search() объекта String.
Раз Миссиссипи, два Миссиссипи…
Следующий метод, который мы рассмотрим — это exec(). Поведение этого метода похоже на то, что делает метод match() объекта String. Посмотрим:
<script language="JavaScript"> // определяем строку var place = "Mississippi"; // указываем шаблон var obj = /is./; // ищем вхождение, // помещаем результат в массив result = obj.exec(place); // показываем результат if(result != null) { alert("Found " + result[0] + " at " + result.index); } </script>
Метод exec() возвращает соответствие указанному регулярному выражению, если такое имеется, как массив. Вы можете обратиться к первому элементу массива, чтобы получить найденную подстроку, а также её расположение с помощью метода index().
Главное различие между методами match() и exec() в передаваемых параметрах. Первый требует шаблон в качестве аргумента, второй же требует строку для проверки.
И это ещё не все. У метода exec() есть возможность продолжить поиск по строке для нахождения аналогичного вхождения без указания модификатора «g». Протестируем эту возможность с помощью следующего примера:
<script language="JavaScript"> // определяем строку var place = "Mississippi"; // определяем шаблон var obj = /is./; // ищем все вхождения в строку // показываем результат while((result = obj.exec(place)) != null) { alert("Found " + result[0] + " at " + result.index); } </script>
Итак, что у нас есть здесь? Для начинающих: я использовал цикл «while» для вызова метода exec() до тех пор, пока не достигнут конец строки (на котором объект вернет null и цикл закончится). Это возможно, потому что каждый раз, вызывая exec(), объект RegExp продолжит поиск с того места, на котором закончил.
Но это все теория — такой код не будет работать ни в Internet Explorer, ни в NetScape Navigator, так что используйте его осторожно. Так что этот код чисто теоретический, по крайней мере до тех пор, пока не исправили ошибку (наверняка уже исправили — примеч. переводчика).
Другая интересная особенность этого кода заключается в создании объекта RegExp. Вы наверняка заметили, что, в отличие от предыдущего примера, здесь не используется конструктор для создания объекта. Вместо этого, шаблон просто применяется к переменной. Думайте об этом просто как о более коротком способе создания объекта RegExp.
Замены в ходе работы
Вы могли заметить в предыдущих примерах использования объекта RegExp, что регулярное выражение указывается во время создания объекта. Поэтому вы можете заинтересоваться, а что если требуется поменять шаблон позже?
Что ж, это не проблема. Метод compile() позволяет пользователю изменить шаблон объекта RegExp в выполнении поиска. Посмотрим:
<script language="JavaScript"> // определяем строку var str = "The Matrix"; // определяем шаблон var pattern = "trinity"; // define object var character = new RegExp(pattern); // смотрим вхождения if(character.test(str)) { alert("Looking for " + pattern + "...User located in The Matrix"); } else { alert("Looking for " + pattern + "...Sorry, user is not in The Matrix"); } // меняем шаблон var pattern = "tri"; character.compile(pattern); // смотрим на вхождение и показываем результат if(character.test(str)) { alert("Looking for " + pattern + "...User located in The Matrix"); } else { alert("Looking for " + pattern + "...Sorry, user is not in The Matrix"); } </script>
Учтите, что использование метода compile() для динамического изменения шаблона, связано с объектом RegExp.
Работаем с формами
Теперь, когда вы знаете, как это все работает, давайте рассмотрим более практичный пример того, как вы можете применить полученные знания с пользой. Следующий пример, который показывает HTML-форму, запрашивающую у пользователя информацию о кредитной карте и электронной почты для выполнения покупки:
<html> <head> <script language="Javascript"> // требует регулярное выражение // в качестве параметра function checkField(theForm, theField, theFieldDisplay, objRegex) { objField = eval("document." + theForm + "." + theField); if(!objRegex.test(objField.value)) { alert ("Please enter a valid " + theFieldDisplay + ""); objField.select(); objField.focus(); return (false); } return (true); } // регулярные выражения для различных // полей формы // номер кредитной карты // должен содержать 20 цифр objPatCCNum = /^[0-9]{20}$/; // дата окончания действия // кредитной карты // должна состоять из месяца // от 01 до 12 и года от 2003 до 2010 objPatCCDOE = /^([0][1-9]|[1][1-2])/20(0[3-9]|10)$/; // пин-код кредитной карты // должен быть численным objPatCCPin = /^[0-9]+$/; // адрес e-mail // должен быть в формате user@host objPatCCEmail = /^([a-zA-Z0-9])+([.a-zA-Z0-9_-])*@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-]+)+/; // проверяем различные поля формы function checkForm(theForm) { if(checkField(theForm, "cc_num", "Credit card number", objPatCCNum) && checkField(theForm, "cc_doe", "Date of expiry", objPatCCDOE) && checkField(theForm, "cc_pin", "PIN code", objPatCCPin) && checkField(theForm, "cc_email", "Email address", objPatCCEmail)) { return true; } else { return false; } } </script> </head> <body> <h2>Информация о кредитной карте</h2> <form name="frmCCValidation" onSubmit="return checkForm('frmCCValidation');"> Номер кредитной карты:<br> <input name="cc_num" type="text"> <p> Тип кредитной карты <br> <select name="cc_type"> <option value="Visa">Visa</option> <option value="Mastercard">Mastercard</option> <option value="AmericanExpress">American Express</option> </select> <p> Дата окончания действия (мм/гггг) <br> <input name="cc_doe" type="text"> <p> Пин-код <br> <input name="cc_pin" type="text"> <p> Адрес электронной почты<br> <input name="cc_email" type="text"> <p> <input type="submit" value="Отправить"> </form> </body> </html>
В этом примере используется несколько регулярных выражений для проверки данных, введенных в форму пользователем, чтобы проверить правильность их формата. Этот тип проверки на стороне клиента крайне важен в Сети для того, чтобы быть увереным в правильности и безопасности поступающих данных.
В заключение
После прочтения, я почти уверен, что вы взглянули на JavaScript совсем с другой стороны. Язык, который вы увидели, не был наиболее часто используемым в замене изображений и определении браузера. Теперь это мощный инструмент, помогающий выполнять поиск по шаблону на стороне клиента быстро и эффективно.
Я начинал с простого введения в регулярные выражения и быстро перешел к методам search() и replace() объекта String. Эти функции принимают регулярное выражение в качестве параметра и позволяют производить операции поиска и замены со строковыми переменными. Это привело нас к рассказу об объекте RegExp. Этот объект дает набор методов и свойств, позволяющих программистом повысить уровень мощности использования регулярных выражений в JavaScript.
В завершение статьи, я показал простой пример, демонстрирующий использование сложных регулярных выражений для проверки данных, поступающих из формы — обычные процедуры для веб-приложений. Если вы делаете это часто, то для вас будет полезно создать хорошую библиотеку регулярных выражений для часто использующих проверок.
Вот несколько дополнительных ссылок, которые помогут вам понять концепцию регулярных выражений ближе:
«Pattern Matching and Regular Expressions»
«Regular Expressions for client-side JavaScript, a free online quick reference»