Javascript регулярные выражения число символов. Регулярные выражения JavaScript. Шаблоны регулярных выражений

Некоторые люди, столкнувшись с проблемой, думают: «О, а использую-ка я регулярные выражения». Теперь у них есть две проблемы.
Джейми Завински

Юан-Ма сказал: «Требуется большая сила, чтобы резать дерево поперёк структуры древесины. Требуется много кода, чтобы программировать поперёк структуры проблемы.
Мастер Юан-Ма, «Книга программирования»

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

В этой главе мы обсудим такой инструмент – регулярные выражения. Это способ описывать шаблоны в строковых данных. Они создают небольшой отдельный язык, который входит в JavaScript и во множество других языков и инструментов.

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

Создаём регулярное выражение

Регулярка – тип объекта. Её можно создать, вызвав конструктор RegExp, или написав нужный шаблон, окружённый слешами.

Var re1 = new RegExp("abc"); var re2 = /abc/;

Оба этих регулярных выражения представляют один шаблон: символ “a”, за которым следует символ “b”, за которым следует символ “c”.

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

Вторая запись, где шаблон находится между слешами, обрабатывает обратные слеши по-другому. Во-первых, так как шаблон заканчивается прямым слешем, то нужно ставить обратный слеш перед прямым слешем, который мы хотим включить в наш шаблон. Кроме того, обратные слеши, не являющиеся частью специальных символов типа \n, будут сохранены (а не проигнорированы, как в строках), и изменят смысл шаблона. У некоторых символов, таких, как знак вопроса или плюс, есть особое значение в регулярках, и если вам нужно найти такой символ, его также надо предварять обратным слешем.

Var eighteenPlus = /eighteen\+/;

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

Проверяем на совпадения

У регулярок есть несколько методов. Простейший – test. Если передать ему строку, он вернёт булевское значение, сообщая, содержит ли строка вхождение заданного шаблона.

Console.log(/abc/.test("abcde")); // → true console.log(/abc/.test("abxde")); // → false

Регулярка, состоящая только из неспециальных символов, просто представляет собой последовательность этих символов. Если abc есть где-то в строке, которую мы проверяем (не только в начале), test вернёт true.

Ищем набор символов

Выяснить, содержит ли строка abc, можно было бы и при помощи indexOf. Регулярки позволяют пройти дальше и составлять более сложные шаблоны.

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

Оба выражения находятся в строчках, содержащих цифру.

Console.log(//.test("in 1992")); // → true console.log(//.test("in 1992")); // → true

В квадратных скобках тире между двумя символами используется для задания диапазона символов, где последовательность задаётся кодировкой Unicode. Символы от 0 до 9 находятся там просто подряд (коды с 48 до 57), поэтому захватывает их все и совпадает с любой цифрой.

У нескольких групп символов есть свои встроенные сокращения.

\d Любая цифра
\w Алфавитно-цифровой символ
\s Пробельный символ (пробел, табуляция, перевод строки, и т.п.)
\D не цифра
\W не алфавитно-цифровой символ
\S не пробельный символ
. любой символ, кроме перевода строки

Таким образом можно задать формат даты и времени вроде 30-01-2003 15:20 следующим выражением:

Var dateTime = /\d\d-\d\d-\d\d\d\d \d\d:\d\d/; console.log(dateTime.test("30-01-2003 15:20")); // → true console.log(dateTime.test("30-jan-2003 15:20")); // → false

Выглядит ужасно, не так ли? Слишком много обратных слешей, которые затрудняют понимание шаблона. Позже мы слегка улучшим его.

Обратные слеши можно использовать и в квадратных скобках. Например, [\d.] означает любую цифру или точку. Заметьте, что точка внутри квадратных скобок теряет своё особое значение и превращается просто в точку. То же касается и других специальных символов, типа +.

Инвертировать набор символов – то есть, сказать, что вам надо найти любой символ, кроме тех, что есть в наборе – можно, поставив знак ^ сразу после открывающей квадратной скобки.

Var notBinary = /[^01]/; console.log(notBinary.test("1100100010100110")); // → false console.log(notBinary.test("1100100010200110")); // → true

Повторяем части шаблона

Мы знаем, как найти одну цифру. А если нам надо найти число целиком – последовательность из одной или более цифр?

Если поставить после чего-либо в регулярке знак +, это будет означать, что этот элемент может быть повторён более одного раза. /\d+/ означает одну или несколько цифр.

Console.log(/"\d+"/.test(""123"")); // → true console.log(/"\d+"/.test("""")); // → false console.log(/"\d*"/.test(""123"")); // → true console.log(/"\d*"/.test("""")); // → true

У звёздочки * значение почти такое же, но она разрешает шаблону присутствовать ноль раз. Если после чего-то стоит звёздочка, то оно никогда не препятствует нахождению шаблона в строке – оно просто находится там ноль раз.

Знак вопроса делает часть шаблона необязательной, то есть она может встретиться ноль или один раз. В следующем примере символ u может встречаться, но шаблон совпадает и тогда, когда его нет.

Var neighbor = /neighbou?r/; console.log(neighbor.test("neighbour")); // → true console.log(neighbor.test("neighbor")); // → true

Чтобы задать точное количество раз, которое шаблон должен встретиться, используются фигурные скобки. {4} после элемента означает, что он должен встретиться в строке 4 раза. Также можно задать промежуток: {2,4} означает, что элемент должен встретиться не менее 2 и не более 4 раз.

Ещё одна версия формата даты и времени, где разрешены дни, месяцы и часы из одной или двух цифр. И ещё она чуть более читаема.

Var dateTime = /\d{1,2}-\d{1,2}-\d{4} \d{1,2}:\d{2}/; console.log(dateTime.test("30-1-2003 8:45")); // → true

Можно использовать промежутки с открытым концом, опуская одно из чисел. {,5} означает, что шаблон может встретиться от нуля до пяти раз, а {5,} – от пяти и более.

Группировка подвыражений

Чтобы использовать операторы * или + на нескольких элементах сразу, можно использовать круглые скобки. Часть регулярки, заключённая в скобки, считается одним элементом с точки зрения операторов.

Var cartoonCrying = /boo+(hoo+)+/i; console.log(cartoonCrying.test("Boohoooohoohooo")); // → true

Первый и второй плюсы относятся только ко вторым буквам о в словах boo и hoo. Третий + относится к целой группе (hoo+), находя одну или несколько таких последовательностей.

Буква i в конце выражения делает регулярку нечувствительной к регистру симолов – так, что B совпадает с b.

Совпадения и группы

Метод test – самый простой метод проверки регулярок. Он только сообщает, было ли найдено совпадение, или нет. У регулярок есть ещё метод exec, который вернёт null, если ничего не было найдено, а в противном случае вернёт объект с информацией о совпадении.

Var match = /\d+/.exec("one two 100"); console.log(match); // → ["100"] console.log(match.index); // → 8

У возвращаемого exec объекта есть свойство index, где содержится номер символа, с которого случилось совпадение. А вообще объект выглядит как массив строк, где первый элемент – строка, которую проверяли на совпадение. В нашем примере это будет последовательность цифр, которую мы искали.

У строк есть метод match, работающий примерно так же.

Console.log("one two 100".match(/\d+/)); // → ["100"]

Когда в регулярке содержатся подвыражения, сгруппированные круглыми скобками, текст, совпавший с этими группами, тоже появится в массиве. Первый элемент всегда совпадение целиком. Второй – часть, совпавшая с первой группой (той, у кого круглые скобки встретились раньше всех), затем со второй группой, и так далее.

Var quotedText = /"([^"]*)"/; console.log(quotedText.exec("she said "hello"")); // → [""hello"", "hello"]

Когда группа не найдена вообще (например, если за ней стоит знак вопроса), её позиция в массиве содержит undefined. Если группа совпала несколько раз, то в массиве будет только последнее совпадение.

Console.log(/bad(ly)?/.exec("bad")); // → ["bad", undefined] console.log(/(\d)+/.exec("123")); // → ["123", "3"]

Группы полезны для извлечения частей строк. Если нам не просто надо проверить, есть ли в строке дата, а извлечь её и создать представляющий дату объект, мы можем заключить последовательности цифр в круглые скобки и выбрать дату из результата exec.

Но для начала небольшое отступление, в котором мы узнаем предпочтительный способ хранения даты и времени в JavaScript.

Тип даты

В JavaScript есть стандартный тип объекта для дат – а точнее, моментов во времени. Он называется Date. Если просто создать объект даты через new, вы получите текущие дату и ремя.

Console.log(new Date()); // → Sun Nov 09 2014 00:07:57 GMT+0300 (CET)

Также можно создать объект, содержащий заданное время

Console.log(new Date(2015, 9, 21)); // → Wed Oct 21 2015 00:00:00 GMT+0300 (CET) console.log(new Date(2009, 11, 9, 12, 59, 59, 999)); // → Wed Dec 09 2009 12:59:59 GMT+0300 (CET)

JavaScript использует соглашение, в котором номера месяцев начинаются с нуля, а номера дней – с единицы. Это глупо и нелепо. Поберегитесь.

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

Метки времени хранятся как количество миллисекунд, прошедших с начала 1970 года. Для времени до 1970 года используются отрицательные числа (это связано с соглашением по Unix time, которое было создано примерно в то время). Метод getTime объекта даты возвращает это число. Оно, естественно, большое.
console.log(new Date(2013, 11, 19).getTime()); // → 1387407600000 console.log(new Date(1387407600000)); // → Thu Dec 19 2013 00:00:00 GMT+0100 (CET)

Если задать конструктору Date один аргумент, он воспринимается как это количество миллисекунд. Можно получить текущее значение миллисекунд, создав объект Date и вызвав метод getTime, или же вызвав функцию Date.now.

У объекта Date для извлечения его компонентов есть методы getFullYear, getMonth, getDate, getHours, getMinutes, и getSeconds. Есть также метод getYear, возвращающий довольно бесполезный двузначный код, типа 93 или 14.

Заключив нужные части шаблона в круглые скобки, мы можем создать объект даты прямо из строки.

Function findDate(string) { var dateTime = /(\d{1,2})-(\d{1,2})-(\d{4})/; var match = dateTime.exec(string); return new Date(Number(match), Number(match) - 1, Number(match)); } console.log(findDate("30-1-2003")); // → Thu Jan 30 2003 00:00:00 GMT+0100 (CET)

Границы слова и строки

К сожалению, findDate так же радостно извлечёт бессмысленную дату 00-1-3000 из строки «100-1-30000». Совпадение может случиться в любом месте строки, так что в данном случае он просто начнёт со второго символа и закончит на предпоследнем.

Если нам надо принудить совпадение взять всю строку целиком, мы используем метки ^ и $. ^ совпадает с началом строки, а $ с концом. Поэтому /^\d+$/ совпадает со строкой, состоящей только из одной или нескольких цифр, /^!/ совпадает со сторокой, начинающейся с восклицательного знака, а /x^/ не совпадает ни с какой строчкой (перед началом строки не может быть x).

Если, с другой стороны, нам просто надо убедиться, что дата начинается и заканчивается на границе слова, мы используем метку \b. Границей слова может быть начало или конец строки, или любое место строки, где с одной стороны стоит алфавитно-цифровой символ \w, а с другой – не алфавитно-цифровой.

Console.log(/cat/.test("concatenate")); // → true console.log(/\bcat\b/.test("concatenate")); // → false

Отметим, что метка границы не представляет из себя символ. Это просто ограничение, обозначающее, что совпадение происходит только если выполняется определённое условие.

Шаблоны с выбором

Допустим, надо выяснить, содержит ли текст не просто номер, а номер, за которым следует pig, cow, или chicken в единственном или множественном числе.

Можно было бы написать три регулярки и проверить их по очереди, но есть способ лучше. Символ | обозначает выбор между шаблонами слева и справа от него. И можно сказать следующее:

Var animalCount = /\b\d+ (pig|cow|chicken)s?\b/; console.log(animalCount.test("15 pigs")); // → true console.log(animalCount.test("15 pigchickens")); // → false

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

Механизм поиска

Регулярные выражения можно рассматривать как блок-схемы. Следующая диаграмма описывает последний животноводческий пример.

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

Значит, проверка совпадения нашей регулярки в строке «the 3 pigs» при прохождении по блок-схеме выглядит так:

На позиции 4 есть граница слова, и проходим первый прямоугольник
- начиная с 4 позиции находим цифру, и проходим второй прямоугольник
- на позиции 5 один путь замыкается назад перед вторым прямоугольником, а второй проходит далее к прямоугольнику с пробелом. У нас пробел, а не цифра, и мы выбираем второй путь.
- теперь мы на позиции 6, начало “pigs”, и на тройном разветвлении путей. В строке нет “cow” или “chicken”, зато есть “pig”, поэтому мы выбираем этот путь.
- на позиции 9 после тройного разветвления, один путь обходит “s” и направляется к последнему прямоугольнику с границей слова, а второй проходит через “s”. У нас есть “s”, поэтому мы идём туда.
- на позиции 10 мы в конце строки, и совпасть может только граница слова. Конец строки считается границей, и мы проходим через последний прямоугольник. И вот мы успешно нашли наш шаблон.

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

Откаты

Регулярка /\b(+b|\d+|[\da-f]h)\b/ совпадает либо с двоичным числом, за которым следует b, либо с десятичным числом без суффикса, либо шестнадцатеричным (цифры от 0 до 9 или символы от a до h), за которым идёт h. Соответствующая диаграмма:

В поисках совпадения может случиться, что алгоритм пошёл по верхнему пути (двоичное число), даже если в строке нет такого числа. Если там есть строка “103”, к примеру, понятно, что только достигнув цифры 3 алгоритм поймёт, что он на неправильном пути. Вообще строка совпадает с регуляркой, просто не в этой ветке.

Тогда алгоритм совершает откат. На развилке он запоминает текущее положение (в нашем случае, это начало строки, сразу после границы слова), чтобы можно было вернуться назад и попробовать другой путь, если выбранный не срабатывает. Для строки “103” после встречи с тройкой он вернётся и попытается пройти путь для десятичных чисел. Это сработает, поэтому совпадение будет найдено.

Алгоритм останавливается, как только найдёт полное совпадение. Это значит, что даже если несколько вариантов могут подойти, используется только один из них (в том порядке, в каком они появляются в регулярке).

Откаты случаются при использовании операторов повторения, таких, как + и *. Если вы ищете /^.*x/ в строке «abcxe», часть регулярки.* попробует поглотить всю строчку. Алгоритм затем сообразит, что ему нужен ещё и “x”. Так как никакого “x” после конца строки нет, алгоритм попробует поискать совпадение, откатившись на один символ. После abcx тоже нет x, тогда он снова откатывается, уже к подстроке abc. И после строчки он находит x и докладывает об успешном совпадении, на позициях с 0 по 4.

Можно написать регулярку, которая приведёт ко множественным откатам. Такая проблема возникает, когда шаблон может совпасть с входными данными множеством разных способов. Например, если мы ошибёмся при написании регулярки для двоичных чисел, мы можем случайно написать что-то вроде /(+)+b/.

Если алгоритм будет искать такой шаблон в длинной строке из нолей и единиц, не содержащей в конце “b”, он сначала пройдёт по внутренней петле, пока у него не кончатся цифры. Тогда он заметит, что в конце нет “b”, сделает откат на одну позицию, пройдёт по внешней петле, опять сдастся, попытается откатиться на ещё одну позицию по внутренней петле… И будет дальше искать таким образом, задействуя обе петли. То есть, количество работы с каждым символом строки будет удваиваться. Даже для нескольких десятков символов поиск совпадения займёт очень долгое время.

Метод replace

У строк есть метод replace, который может заменять часть строки другой строкой.

Console.log("папа".replace("п", "м")); // → мапа

Первый аргумент может быть и регулярной, в каком случае заменяется первое вхождение регулярки в строке. Когда к регулярке добавляется опция “g” (global, всеобщий), заменяются все вхождения, а не только первое

Console.log("Borobudur".replace(//, "a")); // → Barobudur console.log("Borobudur".replace(//g, "a")); // → Barabadar

Имело бы смысл передавать опцию «заменить все» через отдельный аргумент, или через отдельный метод типа replaceAll. Но к сожалению, опция передаётся через саму регулярку.

Вся сила регулярок раскрывается, когда мы используем ссылки на найденные в строке группы, заданные в регулярке. Например, у нас есть строка, содержащая имена людей, одно имя на строчку, в формате «Фамилия, Имя». Если нам надо поменять их местами и убрать запятую, чтобы получилось «Имя Фамилия», мы пишем следующее:

Console.log("Hopper, Grace\nMcCarthy, John\nRitchie, Dennis" .replace(/([\w ]+), ([\w ]+)/g, "$2 $1")); // → Grace Hopper // John McCarthy // Dennis Ritchie

$1 и $2 в строчке на замену ссылаются на группы символов, заключённые в скобки. $1 заменяется текстом, который совпал с первой группой, $2 – со второй группой, и так далее, до $9. Всё совпадение целиком содержится в переменной $&.

Также можно в качестве второго аргумента передавать и функцию. Для каждой замены будет вызвана функция, аргументами которой будут найденные группы (и вся совпадающая часть строки целиком), а её результат будет вставлен в новую строку.

Простой пример:

Var s = "the cia and fbi"; console.log(s.replace(/\b(fbi|cia)\b/g, function(str) { return str.toUpperCase(); })); // → the CIA and FBI

А вот более интересный:

Var stock = "1 lemon, 2 cabbages, and 101 eggs"; function minusOne(match, amount, unit) { amount = Number(amount) - 1; if (amount == 1) // остался только один, удаляем "s" в конце unit = unit.slice(0, unit.length - 1); else if (amount == 0) amount = "no"; return amount + " " + unit; } console.log(stock.replace(/(\d+) (\w+)/g, minusOne)); // → no lemon, 1 cabbage, and 100 eggs

Код принимает строку, находит все вхождения чисел, за которыми идёт слово, и возвращает строчку, где каждое число уменьшено на единицу.

Группа (\d+) попадает в аргумент amount, а (\w+) – в unit. Функция преобразовывает amount в число – и это всегда срабатывает, потому что наш шаблон как раз \d+. И затем вносит изменения в слово, на случай если остался всего 1 предмет.

Жадность

Несложно при помощи replace написать функцию, убирающую все комментарии из кода JavaScript. Вот первая попытка:

Function stripComments(code) { return code.replace(/\/\/.*|\/\*[^]*\*\//g, ""); } console.log(stripComments("1 + /* 2 */3")); // → 1 + 3 console.log(stripComments("x = 10;// ten!")); // → x = 10; console.log(stripComments("1 /* a */+/* b */ 1")); // → 1 1

Часть перед оператором «или» совпадает с двумя слешами, за которыми идут любое количество символов, кроме символов перевода строки. Часть, убирающая многострочные комментарии, более сложна. Мы используем [^], т.е. любой символ, не являющийся пустым, в качестве способа найти любой символ. Мы не можем использовать точку, потому что блочные комментарии продолжаются и на новой строке, а символ перевода строки не совпадает с точкой.

Но вывод предыдущего примера неправильный. Почему?

Часть [^]* сначала попытается захватить столько символов, сколько может. Если из-за этого следующая часть регулярки не найдёт себе совпадения, произойдёт откат на один символ и попробует снова. В примере, алгоритм пытается захватить всю строку, и затем откатывается. Откатившись на 4 символа назад, он найдёт в строчке */ - а это не то, чего мы добивались. Мы-то хотели захватить только один комментарий, а не пройти до конца строки и найти последний комментарий.

Из-за этого мы говорим, что операторы повторения (+, *, ?, and {}) жадные, то есть они сначала захватывают, сколько могут, а потом идут назад. Если вы поместите вопрос после такого оператора (+?, *?, ??, {}?), они превратятся в нежадных, и начнут находить самые маленькие из возможных вхождений.

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

Function stripComments(code) { return code.replace(/\/\/.*|\/\*[^]*?\*\//g, ""); } console.log(stripComments("1 /* a */+/* b */ 1")); // → 1 + 1

Множество ошибок возникает при использовании жадных операторов вместо нежадных. При использовании оператора повтора сначала всегда рассматривайте вариант нежадного оператора.

Динамическое создание объектов RegExp

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

Но вы можете построить строку и использовать конструктор RegExp. Вот пример:

Var name = "гарри"; var text = "А у Гарри на лбу шрам."; var regexp = new RegExp("\\b(" + name + ")\\b", "gi"); console.log(text.replace(regexp, "_$1_")); // → А у _Гарри_ на лбу шрам.

При создании границ слова приходится использовать двойные слеши, потому что мы пишем их в нормальной строке, а не в регулярке с прямыми слешами. Второй аргумент для RegExp содержит опции для регулярок – в нашем случае “gi”, т.е. глобальный и регистро-независимый.

Но что, если имя будет «dea+hlrd» (если наш пользователь – кульхацкер)? В результате мы получим бессмысленную регулярку, которая не найдёт в строке совпадений.

Мы можем добавить обратных слешей перед любым символом, который нам не нравится. Мы не можем добавлять обратные слеши перед буквами, потому что \b или \n – это спецсимволы. Но добавлять слеши перед любыми не алфавитно-цифровыми символами можно без проблем.

Var name = "dea+hlrd"; var text = "Этот dea+hlrd всех достал."; var escaped = name.replace(/[^\w\s]/g, "\\$&"); var regexp = new RegExp("\\b(" + escaped + ")\\b", "gi"); console.log(text.replace(regexp, "_$1_")); // → Этот _dea+hlrd_ всех достал.

Метод search

Метод indexOf нельзя использовать с регулярками. Зато есть метод search, который как раз ожидает регулярку. Как и indexOf, он возвращает индекс первого вхождения, или -1, если его не случилось.

Console.log(" word".search(/\S/)); // → 2 console.log(" ".search(/\S/)); // → -1

К сожалению, никак нельзя задать, чтобы метод искал совпадение, начиная с конкретного смещения (как это можно сделать с indexOf). Это было бы полезно.

Свойство lastIndex

Метод exec тоже не даёт удобного способа начать поиск с заданной позиции в строке. Но неудобный способ даёт.

У объекта регулярок есть свойства. Одно из них – source, содержащее строку. Ещё одно – lastIndex, контролирующее, в некоторых условиях, где начнётся следующий поиск вхождений.

Эти условия включают необходимость присутствия глобальной опции g, и то, что поиск должен идти с применением метода exec. Более разумным решением было бы просто допустить дополнительный аргумент для передачи в exec, но разумность – не основополагающая черта в интерфейсе регулярок JavaScript.

Var pattern = /y/g; pattern.lastIndex = 3; var match = pattern.exec("xyzzy"); console.log(match.index); // → 4 console.log(pattern.lastIndex); // → 5

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

При использовании глобальной переменной-регулярки и нескольких вызовов exec эти автоматические обновления lastIndex могут привести к проблемам. Ваша регулярка может начать поиск с позиции, оставшейся с предыдущего вызова.

Var digit = /\d/g; console.log(digit.exec("here it is: 1")); // → ["1"] console.log(digit.exec("and now: 1")); // → null

Ещё один интересный эффект опции g в том, что она меняет работу метода match. Когда он вызывается с этой опцией, вместо возврата массива, похожего на результат работы exec, он находит все вхождения шаблона в строке и возвращает массив из найденных подстрок.

Console.log("Банан".match(/ан/g)); // → ["ан", "ан"]

Так что поосторожнее с глобальными переменными-регулярками. В случаях, когда они необходимы – вызовы replace или места, где вы специально используете lastIndex – пожалуй и все случаи, в которых их следует применять.

Циклы по вхождениям

Типичная задача – пройти по всем вхождениям шаблона в строку так, чтобы иметь доступ к объекту match в теле цикла, используя lastIndex и exec.

Var input = "Строчка с 3 числами в ней... 42 и 88."; var number = /\b(\d+)\b/g; var match; while (match = number.exec(input)) console.log("Нашёл ", match, " на ", match.index); // → Нашёл 3 на 14 // Нашёл 42 на 33 // Нашёл 88 на 40

Используется тот факт, что значением присвоения является присваиваемое значение. Используя конструкцию match = re.exec(input) в качестве условия в цикле while, мы производим поиск в начале каждой итерации, сохраняем результат в переменной, и заканчиваем цикл, когда все совпадения найдены.

Разбор INI файлы

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

Searchengine=http://www.google.com/search?q=$1 spitefulness=9.7 ; перед комментариями ставится точка с запятой; каждая секция относится к отдельному врагу fullname=Larry Doe type=бычара из детсада website=http://www.geocities.com/CapeCanaveral/11451 fullname=Gargamel type=злой волшебник outputdir=/home/marijn/enemies/gargamel

Точный формат файла (который довольно широко используется, и обычно называется INI), следующий:

Пустые строки и строки, начинающиеся с точки с запятой, игнорируются
- строки, заключённые в квадратные скобки, начинают новую секцию
- строки, содержащие алфавитно-цифровой идентификатор, за которым следует =, добавляют настройку в данной секции

Всё остальное – неверные данные.

Наша задача – преобразовать такую строку в массив объектов, каждый со свойством name и массивом настроек. Для каждой секции нужен один объект, и ещё один – для глобальных настроек сверху файла.

Так как файл надо разбирать построчно, неплохо начать с разбиения файла на строки. Для этого в главе 6 мы использовали string.split("\n"). Некоторые операционки используют для перевода строки не один символ \n, а два - \r\n. Так как метод split принимает регулярки в качестве аргумента, мы можем делить линии при помощи выражения /\r?\n/, разрешающего и одиночные \n и \r\n между строками.

Function parseINI(string) { // Начнём с объекта, содержащего настройки верхнего уровня var currentSection = {name: null, fields: }; var categories = ; string.split(/\r?\n/).forEach(function(line) { var match; if (/^\s*(;.*)?$/.test(line)) { return; } else if (match = line.match(/^\[(.*)\]$/)) { currentSection = {name: match, fields: }; categories.push(currentSection); } else if (match = line.match(/^(\w+)=(.*)$/)) { currentSection.fields.push({name: match, value: match}); } else { throw new Error("Строчка "" + line + "" содержит неверные данные."); } }); return categories; }

Код проходит все строки, обновляя объект текущей секции “current section”. Сначала он проверяет, можно ли игнорировать строчку, при помощи регулярки /^\s*(;.*)?$/. Соображаете, как это работает? Часть между скобок совпадает с комментариями, а? делает так, что регулярка совпадёт и со строчками, состоящими из одних пробелов.

Если строка – не комментарий, код проверяет, начинает ли она новую секцию. Если да, он создаёт новый объект для текущей секции, к которому добавляются последующие настройки.

Последняя осмысленная возможность – строка является обычной настройкой, и в этом случае она добавляется к текущему объекту.

Если ни один вариант не сработал, функция выдаёт ошибку.

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

Конструкция if (match = string.match(...)) похожа на трюк, использующий присвоение как условие в цикле while. Часто вы не знаете, что вызов match будет успешным, поэтому вы можете получить доступ к результирующему объекту только внутри блока if, который это проверяет. Чтоб не разбивать красивую цепочку проверок if, мы присваиваем результат поиска переменной, и сразу используем это присвоение как проверку.

Международные символы

Из-за изначально простой реализации языка, и последующей фиксации такой реализации «в граните», регулярки JavaScript тупят с символами, не встречающимися в английском языке. К примеру, символ «буквы» с точки зрения регулярок JavaScript, может быть одним из 26 букв английского алфавита, и почему-то ещё подчёркиванием. Буквы типа é или β, однозначно являющиеся буквами, не совпадают с \w (и совпадут с \W, то есть с не-буквой).

По странному стечению обстоятельств, исторически \s (пробел) совпадает со всеми символами, которые в Unicode считаются пробельными, включая такие штуки, как неразрывный пробел или монгольский разделитель гласных.

У некоторых реализаций регулярок в других языках есть особый синтаксис для поиска специальных категорий символов Unicode, типа «все прописные буквы», «все знаки препинания» или «управляющие символы». Есть планы по добавлению таких категорий и в JavaScript, но они, видимо, будут реализованы не скоро.

Итог

Регулярки – это объекты, представляющие шаблоны поиска в строках. Они используют свой синтаксис для выражения этих шаблонов.

/abc/ Последовательность символов
// Любой символ из списка
/[^abc]/ Любой символ, кроме символов из списка
// Любой символ из промежутка
/x+/ Одно или более вхождений шаблона x
/x+?/ Одно или более вхождений, нежадное
/x*/ Ноль или более вхождений
/x?/ Ноль или одно вхождение
/x{2,4}/ От двух до четырёх вхождений
/(abc)/ Группа
/a|b|c/ Любой из нескольких шаблонов
/\d/ Любая цифра
/\w/ Любой алфавитно-цифровой символ («буква»)
/\s/ Любой пробельный символ
/./ Любой символ, кроме переводов строки
/\b/ Граница слова
/^/ Начало строки
/$/ Конец строки

У регулярки есть метод test, для проверки того, есть ли шаблон в строке. Есть метод exec, возвращающий массив, содержащий все найденные группы. У массива есть свойство index, где содержится номер символа, с которого случилось совпадение.

У строк есть метод match для поиска шаблонов, и метод search, возвращающий только начальную позицию вхождения. Метод replace может заменять вхождения шаблона на другую строку. Кроме этого, вы можете передать в replace функцию, которая будет строить строчку на замену, основываясь на шаблоне и найденных группах.

У регулярок есть настройки, которые пишут после закрывающего слеша. Опция i делает регулярку регистронезависимой, а опция g делает её глобальной, что, кроме прочего, заставляет метод replace заменять все найденные вхождения, а не только первое.

Конструктор RegExp можно использовать для создания регулярок из строк.

Регулярки – острый инструмент с неудобной ручкой. Они сильно упрощают одни задачи, и могут стать неуправляемыми при решении других, сложных задач. Часть умения пользоваться регулярками состоит в том, чтобы уметь сопротивляться искушению запихнуть в них задачу, для которой они не предназначены.

Упражнения

Неизбежно при решении задач у вас возникнут непонятные случаи, и вы можете иногда отчаиваться, видя непредсказуемое поведение некоторых регулярок. Иногда помогает изучить поведение регулярки через онлайн-сервис типа debuggex.com, где можно посмотреть её визуализацию и сравнить с желаемым эффектом.
Регулярный гольф
«Гольфом» в коде называют игру, где нужно выразить заданную программу минимальным количеством символов. Регулярный гольф – практическое упражнение по написанию наименьших возможных регулярок для поиска заданного шаблона, и только его.

Для каждой из подстрочек напишите регулярку для проверки их нахождения в строке. Регулярка должна находить только эти указанные подстроки. Не волнуйтесь насчёт границ слов, если это не упомянуто особо. Когда у вас получится работающая регулярка, попробуйте её уменьшить.

Car и cat
- pop и prop
- ferret, ferry, и ferrari
- Любое слово, заканчивающееся на ious
- Пробел, за которым идёт точка, запятая, двоеточие или точка с запятой.
- Слово длинее шести букв
- Слово без букв e

// Впишите свои регулярки verify(/.../, ["my car", "bad cats"], ["camper", "high art"]); verify(/.../, ["pop culture", "mad props"], ["plop"]); verify(/.../, ["ferret", "ferry", "ferrari"], ["ferrum", "transfer A"]); verify(/.../, ["how delicious", "spacious room"], ["ruinous", "consciousness"]); verify(/.../, ["bad punctuation ."], ["escape the dot"]); verify(/.../, ["hottentottententen"], ["no", "hotten totten tenten"]); verify(/.../, ["red platypus", "wobbling nest"], ["earth bed", "learning ape"]); function verify(regexp, yes, no) { // Ignore unfinished exercises if (regexp.source == "...") return; yes.forEach(function(s) { if (!regexp.test(s)) console.log("Не нашлось "" + s + """); }); no.forEach(function(s) { if (regexp.test(s)) console.log("Неожиданное вхождение "" + s + """); }); }

Кавычки в тексте
Допустим, вы написали рассказ, и везде для обозначения диалогов использовали одинарные кавычки. Теперь вы хотите заменить кавычки диалогов на двойные, и оставить одинарные в сокращениях слов типа aren’t.

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

Снова числа
Последовательности цифр можно найти простой регуляркой /\d+/.

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

// Впишите сюда регулярку. var number = /^...$/; // Tests: ["1", "-1", "+15", "1.55", ".5", "5.", "1.3e2", "1E-4", "1e+12"].forEach(function(s) { if (!number.test(s)) console.log("Не нашла "" + s + """); }); ["1a", "+-1", "1.2.3", "1+1", "1e4.5", ".5.", "1f5", "."].forEach(function(s) { if (number.test(s)) console.log("Неправильно принято "" + s + """); });

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

Когда вам нужно что-то найти в каком-то тексте, то для описания того, что вы ищите, и используется шаблон поиска.

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

Регулярные выражения могут использоваться для выполнения всех видов операций текстового поиска и замены.

Синтаксис:

/шаблон/модификаторы;

Var patt = /msiter/i;

Объяснение примера:

  • /msiter/i - регулярное выражение.
  • msiter – шаблон, используемый в операции поиска.
  • i - модификатор (определяет, что поиск должен быть регистронезависимым).

Использование методов строки

В JavaScript регулярные выражения часто используют в двух методах строки: search() и replace() .

Метод search() использует выражение для поиска совпадения и возвращает позицию найденного совпадения.

Метод replace() возвращает измененную строку, где произведена замена шаблона.

Метод search() с регулярным выражением

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

Var str = "Посетите сайт MSiter"; var n = str.search(/msiter/i);

В результате в переменную n будет возвращено 14.

Метод search() со строкой

Метод search() в качестве параметра также может принимать строку. При этом строковый параметр преобразуется в регулярное выражение:

В следующем примере для поиска используется строка "MSiter":

Var str = "Посетите сайт MSiter!"; var n = str.search("MSiter");

Метод replace() с регулярным выражением

В следующем примере, чтобы заменить подстроку "Microsoft" на "Msiter" без учета регистра, используется регулярное выражение:

Var str = "Посетите сайт Microsoft!"; var res = str.replace(/microsoft/i, "MSiter");

В результате в переменной res будет строка "Посетите сайт MSiter!".

Метод replace() со строкой

Метод replace() в качестве параметра также может принимать строку:

Var str = "Посетите сайт Microsoft!"; var res = str.replace("Microsoft", "MSiter");

А вы заметили, что

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

Модификаторы регулярного выражения

Модификаторы позволяют расширить область поиска:

Шаблоны регулярных выражений

Квадратные скобки используются для поиска в диапазоне символов:

Метасимволы - это символы со специальным значением:

Квантификаторы определяют количество повторений:

Объект RegExp

В JavaScript объект RegExp - это объект регулярного выражения с предопределенными свойствами и методами.

Метод test()

Метод test() объекта RegExp используется для поиска шаблона в заданной строке. В зависимости от результата он возвращает true или false.

Var patt = /e/; patt.test("The best things in life are free!");

Так как в этом примере строка содержит символ "e", результат будет true.

Для работы с объектом RegExp не обязательно сначала помещать регулярное выражение в переменную. Две строчки кода предыдущего примера можно сократить до одной:

/e/.test("The best things in life are free!");

Метод exec()

Метод exec() объекта RegExp используется для поиска шаблона в заданной строке. Он возвращает найденный текст. Если ничего не было найдено, то возвращается null.

В следующем примере в строке ищется символ "e":

/e/.exec("The best things in life are free!");

Так как в этом примере строка содержит символ "e", результат будет - e.

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

Регулярное выражение - это объект, описывающий символьный шаблон. Класс RegExp в JavaScript представляет регулярные выражения, а объекты классов String и RegExp определяют методы, использующие регулярные выражения для выполнения поиска по шаблону и операций поиска в тексте с заменой. Грамматика регулярных выражений в языке JavaScript содержит достаточно полное подмножество синтаксиса регулярных выражений, используемого в языке Perl 5, поэтому, если вы имеете опыт работы с языком Perl, то вы без труда сможете описывать шаблоны в программах на языке JavaScript.

В число особенностей регулярных выражений языка Perl, которые не поддерживаются в ECMAScript, входят флаги s (однострочный режим) и x (расширенный синтаксис); управляющие последовательности \a, \e, \l, \u, \L, \U, \E, \Q, \A, \Z, \z и \G и другие расширенные конструкции, начинающиеся с (?.

Определение регулярных выражений

В JavaScript регулярные выражения представлены объектами RegExp . Объекты RegExp могут быть созданы посредством конструктора RegExp(), но чаще они создаются с помощью специального синтаксиса литералов. Так же как строковые литералы задаются в виде символов, заключенных в кавычки, литералы регулярных выражений задаются в виде символов, заключенных в пару символов слэша (/). Таким образом, JavaScript-код может содержать строки, похожие на эту:

Var pattern = /s$/;

Эта строка создает новый объект RegExp и присваивает его переменной pattern. Данный объект RegExp ищет любые строки, заканчивающиеся символом "s". Это же регулярное выражение может быть определено с помощью конструктора RegExp():

Var pattern = new RegExp("s$");

Спецификация шаблона регулярного выражения состоит из последовательности символов. Большая часть символов, включая все алфавитно-цифровые, буквально описывают символы, которые должны присутствовать. То есть регулярное выражение /java/ совпадает со всеми строками, содержащими подстроку «java».

Другие символы в регулярных выражениях не предназначены для поиска их точных эквивалентов, а имеют особое значение. Например, регулярное выражение /s$/ содержит два символа. Первый символ s обозначает поиск буквального символа. Второй, $ - это специальный метасимвол, обозначающий конец строки. Таким образом, это регулярное выражение соответствует любой строке, заканчивающейся символом s.

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

Символы литералов

Как отмечалось ранее, все алфавитные символы и цифры в регулярных выражениях соответствуют сами себе. Синтаксис регулярных выражений в JavaScript также поддерживает возможность указывать некоторые неалфавитные символы с помощью управляющих последовательностей, начинающихся с символа обратного слэша (\). Например, последовательность \n соответствует символу перевода строки. Эти символы перечислены в таблице ниже:

Некоторые знаки препинания имеют в регулярных выражениях особый смысл:

^ $ . * + ? = ! : | \ / () { } -

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

Если вы не можете точно вспомнить, каким из символов должен предшествовать символ \, можете спокойно помещать обратный слэш перед любым из символов. Однако имейте в виду, что многие буквы и цифры вместе с символом слэша обретают специальное значение, поэтому тем буквам и цифрам, которые вы ищете буквально, не должен предшествовать символ \. Чтобы включить в регулярное выражение сам символ обратного слэша, перед ним, очевидно, следует поместить другой символ обратного слэша. Например, следующее регулярное выражение соответствует любой строке, содержащей символ обратного слэша: /\\/.

Классы символов

Отдельные символы литералов могут объединяться в классы символов путем помещения их в квадратные скобки. Класс символов соответствует любому символу, содержащемуся в этом классе. Следовательно, регулярное выражение // соответствует одному из символов a, b или c.

Могут также определяться классы символов с отрицанием, соответствующие любому символу, кроме тех, которые указаны в скобках. Класс символов с отрицанием задается символом ^ в качестве первого символа, следующего за левой скобкой. Регулярное выражение /[^abc]/ соответствует любому символу, отличному от a, b или c. В классах символов диапазон символов может задаваться при помощи дефиса. Поиск всех символов латинского алфавита в нижнем регистре осуществляется посредством выражения //, а любую букву или цифру из набора символов Latin можно найти при помощи выражения //.

Некоторые классы символов используются особенно часто, поэтому синтаксис регулярных выражений в JavaScript включает специальные символы и управляющие (escape) последовательности для их обозначения. Так, \s соответствует символам пробела, табуляции и любым пробельным символам из набора Unicode, а \S - любым символам, не являющимся пробельными символами из набора Unicode.

В таблице ниже приводится перечень этих спецсимволов и синтаксиса классов символов. (Обратите внимание, что некоторые из управляющих последовательностей классов символов соответствуют только ASCII-символам и не расширены для работы с Unicode-символами. Можно явно определить собственные классы Unicode-символов, например, выражение /[\u0400-\u04FF]/ соответствует любому символу кириллицы.)

Классы символов регулярных выражений JavaScript
Символ Соответствие
[...] Любой из символов, указанных в скобках
[^...] Любой из символов, не указанных в скобках
. Любой символ, кроме перевода строки или другого разделителя Unicode-строки
\w Любой текстовый ASCII-символ. Эквивалентно
\W Любой символ, не являющийся текстовым ASCII-символом. Эквивалентно [^a-zA-Z0-9_]
\s Любой пробельный символ из набора Unicode
\S Любой непробельный символ из набора Unicode. Обратите внимание, что символы \w и \S - это не одно и то же
\d Любые ASCII-цифры. Эквивалентно
\D Любой символ, отличный от ASCII-цифр. Эквивалентно [^0-9]
[\b] Литерал символа «забой»

Обратите внимание, что управляющие последовательности специальных символов классов могут находиться в квадратных скобках. \s соответствует любому пробельному символу, а \d соответствует любой цифре, следовательно, /[\s\d]/ соответствует любому пробельному символу или цифре.

Повторение

Имея знания синтаксиса регулярных выражений, полученные к настоящему моменту, мы можем описать число из двух цифр как /\d\d/ или из четырех цифр как /\d\d\d\d/, но не сможем, например, описать число, состоящее из любого количества цифр, или строку из трех букв, за которыми следует необязательная цифра. Эти более сложные шаблоны используют синтаксис регулярных выражений, указывающий, сколько раз может повторяться данный элемент регулярного выражения.

Символы, обозначающие повторение, всегда следуют за шаблоном, к которому они применяются. Некоторые виды повторений используются довольно часто, и для обозначения этих случаев имеются специальные символы. Например, + соответствует одному или нескольким экземплярам предыдущего шаблона. В следующей таблице приведена сводка синтаксиса повторений:

Следующие строки демонстрируют несколько примеров:

Var pattern = /\d{2,4}/; // Соответствует числу, содержащему от двух до четырех цифр pattern = /\w{3}\d?/; // Соответствует в точности трем символам слова и одной необязательной цифре pattern = /\s+java\s+/; // Соответствует слову "java" с одним или более пробелами // до и после него pattern = /[^(]*/; // Соответствует нулю или более символам, отличным от открывающей круглой скобки

Будьте внимательны при использовании символов повторения * и?. Они могут соответствовать отсутствию указанного перед ними шаблона и, следовательно, отсутствию символов. Например, регулярному выражению /a*/ соответствует строка "bbbb", поскольку в ней нет символа a.

Символы повторения, перечисленные в таблице, соответствуют максимально возможному количеству повторений, при котором обеспечивается поиск последующих частей регулярного выражения. Мы говорим, что это - «жадное» повторение. Имеется также возможность реализовать повторение, выполняемое «нежадным» способом. Достаточно указать после символа (или символов) повторения вопросительный знак: ??, +?, *? или даже {1,5}?.

Например, регулярное выражение /a+/ соответствует одному или более экземплярам буквы a. Примененное к строке «aaa», оно соответствует всем трем буквам. С другой стороны, выражение /a+?/ соответствует одному или более экземплярам буквы a и выбирает наименее возможное число символов. Примененный к той же строке, этот шаблон соответствует только первой букве a.

«Нежадное» повторение не всегда дает ожидаемый результат. Рассмотрим шаблон /a+b/, соответствующий одному или более символам a, за которыми следует символ b. Применительно к строке «aaab» ему соответствует вся строка.

Теперь проверим «нежадную» версию /a+?b/. Можно было бы подумать, что она должна соответствовать символу b, перед которым стоит только один символ a. В случае применения к той же строке «aaab» можно было бы ожидать, что она совпадет с единственным символом a и последним символом b. Однако на самом деле этому шаблону соответствует вся строка, как и в случае «жадной» версии. Дело в том, что поиск по шаблону регулярного выражения выполняется путем нахождения первой позиции в строке, начиная с которой соответствие становится возможным. Так как соответствие возможно, начиная с первого символа строки, более короткие соответствия, начинающиеся с последующих символов, даже не рассматриваются.

Альтернативы, группировка и ссылки

Грамматика регулярных выражений включает специальные символы определения альтернатив, подвыражений группировки и ссылок на предыдущие подвыражения. Символ вертикальной черты | служит для разделения альтернатив. Например, /ab|cd|ef/ соответствует либо строке «ab», либо строке «cd», либо строке «ef», а шаблон /\d{3}|{4}/ - либо трем цифрам, либо четырем строчным буквам.

Обратите внимание, что альтернативы обрабатываются слева направо до тех пор, пока не будет найдено соответствие. При обнаружении совпадения с левой альтернативой правая игнорируется, даже если можно добиться «лучшего» соответствия. Поэтому, когда к строке «ab» применяется шаблон /a|ab/, он будет соответствовать только первому символу.

Круглые скобки имеют в регулярных выражениях несколько значений. Одно из них - группировка отдельных элементов в одно подвыражение, так что элементы при использовании специальных символов |, *, +, ? и других рассматриваются как одно целое. Например, шаблон /java(script)?/ соответствует слову «java», за которым следует необязательное слово «script», а /(ab|cd)+|ef)/ соответствует либо строке «ef», либо одному или более повторений одной из строк «ab» или «cd».

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

Предположим, что требуется отыскать одну или более букв в нижнем регистре, за которыми следует одна или несколько цифр. Для этого можно воспользоваться шаблоном /+\d+/. Но предположим также, что нам нужны только цифры в конце каждого соответствия. Если поместить эту часть шаблона в круглые скобки (/+(\d+)/), то можно будет извлечь цифры из любых найденных нами соответствий. Как это делается, будет описано ниже.

С этим связано еще одно применение подвыражений в скобках, позволяющее ссылаться на подвыражения из предыдущей части того же регулярного выражения. Это достигается путем указания одной или нескольких цифр после символа \. Цифры ссылаются на позицию подвыражения в скобках внутри регулярного выражения. Например, \1 ссылается на первое подвыражение, а \3 - на третье. Обратите внимание, что подвыражения могут быть вложены одно в другое, поэтому при подсчете используется позиция левой скобки. Например, в следующем регулярном выражении ссылка на вложенное подвыражение (cript) будет выглядеть как \2:

/(ava(cript)?)\sis\s(fun\w*)/

Ссылка на предыдущее подвыражение указывает не на шаблон этого подвыражения, а на найденный текст, соответствующий этому шаблону. Поэтому ссылки могут использоваться для наложения ограничения, выбирающего части строки, содержащие точно такие же символы. Например, следующее регулярное выражение соответствует нулю или более символам внутри одинарных или двойных кавычек. Однако оно не требует, чтобы открывающие и закрывающие кавычки соответствовали друг другу (т.е. чтобы обе кавычки были одинарными или двойными):

/[""][^""]*[""]/

Соответствия кавычек мы можем потребовать посредством такой ссылки:

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

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

/(ava(?:cript)?)\sis\s(fun\w*)/

Здесь подвыражение (?:cript) необходимо только для группировки, чтобы к группе мог быть применен символ повторения?. Эти модифицированные скобки не создают ссылку, поэтому в данном регулярном выражении \2 ссылается на текст, соответствующий шаблону (fun\w*).

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

Символы регулярных выражений выбора из альтернатив, группировки и ссылки JavaScript
Символ Значение
| Альтернатива. Соответствует либо подвыражению слева, либо подвыражению справа.
(...) Группировка. Группирует элементы в единое целое, которое может использоваться с символами *, +, ?, | и т.п. Также запоминает символы, соответствующие этой группе для использования в последующих ссылках.
(?:...) Только группировка. Группирует элементы в единое целое, но не запоминает символы, соответствующие этой группе.
\number Соответствует тем же символам, которые были найдены при сопоставлении с группой с номером number. Группы - это подвыражения внутри скобок (возможно, вложенных). Номера группам присваиваются путем подсчета левых скобок слева направо. Группы, сформированные с помощью символов (?:, не нумеруются.

Указание позиции соответствия

Как описывалось ранее, многие элементы регулярного выражения соответствуют одному символу в строке. Например, \s соответствует одному пробельному символу. Другие элементы регулярных выражений соответствуют позициям между символами, а не самим символам. Например, \b соответствует границе слова - границе между \w (текстовый ASCII-символ) и \W (нетекстовый символ) или границе между текстовым ASCII-символом и началом или концом строки.

Такие элементы, как \b, не определяют какие-либо символы, которые должны присутствовать в найденной строке, однако они определяют допустимые позиции для проверки соответствия. Иногда эти элементы называются якорными элементами регулярных выражений, потому что они закрепляют шаблон за определенной позицией в строке. Чаще других используются такие якорные элементы, как ^ и $, привязывающие шаблоны соответственно к началу и концу строки.

Например, слово «JavaScript», находящееся на отдельной строке, можно найти с помощью регулярного выражения /^JavaScript$/. Чтобы найти отдельное слово «Java» (а не префикс, например в слове «JavaScript»), можно попробовать применить шаблон /\sJava\s/, который требует наличия пробела до и после слова.

Но такое решение порождает две проблемы. Во-первых, оно найдет слово «Java», только если оно окружено пробелами с обеих сторон, и не сможет найти его в начале или в конце строки. Во-вторых, когда этот шаблон действительно найдет соответствие, возвращаемая им строка будет содержать ведущие и замыкающие пробелы, а это не совсем то, что нам нужно. Поэтому вместо шаблона, совпадающего с пробельными символами \s, мы воспользуемся шаблоном (или якорем), совпадающим с границами слова \b. Получится следующее выражение: /\bJava\b/.

Якорный элемент \B соответствует позиции, не являющейся границей слова. То есть шаблону /\Bcript/ будут соответствовать слова «JavaScript» и «postscript» и не будут соответствовать слова «script» или «Scripting».

В качестве якорных условий могут также выступать произвольные регулярные выражения. Если поместить выражение между символами (?= и), оно превратится в опережающую проверку на совпадение с последующими символами, требующую, чтобы эти символы соответствовали указанному шаблону, но не включались в строку соответствия.

Например, чтобы найти совпадение с названием распространенного языка программирования, за которым следует двоеточие, можно воспользоваться выражением /ava(cript)?(?=\:)/. Этому шаблону соответствует слово «JavaScript» в строке «JavaScript: The Definitive Guide», но ему не будет соответствовать слово «Java» в строке «Java in a Nutshell», потому что за ним не следует двоеточие.

Если же ввести условие (?!, то это будет негативная опережающая проверка на последующие символы, требующая, чтобы следующие символы не соответствовали указанному шаблону. Например, шаблону /Java(?!Script)(\w*)/ соответствует подстрока «Java», за которой следует заглавная буква и любое количество текстовых ASCII-символов при условии, что за подстрокой «Java» не следует подстрока «Script». Он совпадет со строкой «JavaBeans», но не совпадет со строкой «Javanese», совпадет со строкой «JavaScrip», но не совпадет со строками «JavaScript» или «JavaScripter».

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

Якорные символы регулярных выражений
Символ Значение
^ Соответствует началу строкового выражения или началу строки при многострочном поиске.
$ Соответствует концу строкового выражения или концу строки при многострочном поиске.
\b Соответствует границе слова, т.е. соответствует позиции между символом \w и символом \W или между символом \w и началом или концом строки. (Однако обратите внимание, что [\b] соответствует символу забоя.)
\B Соответствует позиции, не являющейся границей слов.
(?=p) Позитивная опережающая проверка на последующие символы. Требует, чтобы последующие символы соответствовали шаблону p, но не включает эти символы в найденную строку.
(?!p) Негативная опережающая проверка на последующие символы. Требует, чтобы следующие символы не соответствовали шаблону p.

Флаги

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

Флаг i указывает, что поиск по шаблону должен быть нечувствителен к регистру символов, а флаг g - что поиск должен быть глобальным, т.е. должны быть найдены все соответствия в строке. Флаг m выполняет поиск по шаблону в многострочном режиме. Если строковое выражение, в котором выполняется поиск, содержит символы перевода строк, то в этом режиме якорные символы ^ и $, помимо того, что они соответствуют началу и концу всего строкового выражения, также соответствуют началу и концу каждой текстовой строки. Например, шаблону /java$/im соответствует как слово «java», так и «Java\nis fun».

Эти флаги могут объединяться в любые комбинации. Например, чтобы выполнить поиск первого вхождения слова «java» (или «Java», «JAVA» и т.д.) без учета регистра символов, можно воспользоваться нечувствительным к регистру регулярным выражением /\bjava\b/i. А чтобы найти все вхождения этого слова в строке, можно добавить флаг g: /\bjava\b/gi.

Методы класса String для поиска по шаблону

До этого момента мы обсуждали грамматику создаваемых регулярных выражений, но не рассматривали, как эти регулярные выражения могут фактически использоваться в JavaScript-сценариях. В данном разделе мы обсудим методы объекта String, в которых регулярные выражения применяются для поиска по шаблону, а также для поиска с заменой. А затем продолжим разговор о поиске по шаблону с регулярными выражениями, рассмотрев объект RegExp, его методы и свойства.

Строки поддерживают четыре метода, использующие регулярные выражения. Простейший из них - метод search() . Он принимает в качестве аргумента регулярное выражение и возвращает либо позицию первого символа найденной подстроки, либо -1, если соответствие не найдено. Например, следующий вызов вернет 4:

Var result = "JavaScript".search(/script/i); // 4

Если аргумент метода search() не является регулярным выражением, он сначала преобразуется путем передачи конструктору RegExp. Метод search() не поддерживает глобальный поиск и игнорирует флаг g в своем аргументе.

Метод replace() выполняет операцию поиска с заменой. Он принимает в качестве первого аргумента регулярное выражение, а в качестве второго - строку замены. Метод отыскивает в строке, для которой он вызван, соответствие указанному шаблону.

Если регулярное выражение содержит флаг g, метод replace() заменяет все найденные совпадения строкой замены. В противном случае он заменяет только первое найденное совпадение. Если первый аргумент метода replace() является строкой, а не регулярным выражением, то метод выполняет буквальный поиск строки, а не преобразует его в регулярное выражение с помощью конструктора RegExp(), как это делает метод search().

В качестве примера мы можем воспользоваться методом replace() для единообразной расстановки прописных букв в слове «JavaScript» для всей строки текста:

// Независимо от регистра символов заменяем словом в нужном регистре var result = "javascript".replace(/JavaScript/ig, "JavaScript");

Метод replace() представляет собой более мощное средство, чем можно было бы предположить по этому примеру. Напомню, что подвыражения в скобках, находящиеся внутри регулярного выражения, нумеруются слева направо, и что регулярное выражение запоминает текст, соответствующий каждому из подвыражений. Если в строке замены присутствует знак $ с цифрой, метод replace() заменяет эти два символа текстом, соответствующим указанному подвыражению. Это очень полезная возможность. Мы можем использовать ее, например, для замены прямых кавычек в строке типографскими кавычками, которые имитируются ASCII-символами:

// Цитата - это кавычка, за которой следует любое число символов, // отличных от кавычек (их мы запоминаем), за этими символами // следует еще одна кавычка var quote = /"([^"]*)"/g; // Заменяем прямые кавычки типографскими и оставляем без изменений «$1» // содержимое цитаты, хранящееся в $1 var text = ""JavaScript" - интерпретируемый язык программирования."; var result = text.replace(quote, "«$1»"); // «JavaScript» - интерпретируемый язык программирования.

Важное, что следует отметить, - второй аргумент replace() может быть функцией, динамически вычисляющей строку замены.

Метод match() - это наиболее общий из методов класса String, использующих регулярные выражения. Он принимает в качестве единственного аргумента регулярное выражение (или преобразует свой аргумент в регулярное выражение, передав его конструктору RegExp()) и возвращает массив, содержащий результаты поиска. Если в регулярном выражении установлен флаг g, метод возвращает массив всех соответствий, присутствующих в строке. Например:

// вернет ["1", "2", "3"] var result = "1 плюс 2 равно 3".match(/\d+/g);

Если регулярное выражение не содержит флаг g, метод match() не выполняет глобальный поиск; он просто ищет первое совпадение. Однако match() возвращает массив, даже когда метод не выполняет глобальный поиск. В этом случае первый элемент массива - это найденная подстрока, а все оставшиеся элементы представляют собой подвыражения регулярного выражения. Поэтому если match() возвращает массив arr, то arr будет содержать найденную строку целиком, arr -подстроку, соответствующую первому подвыражению, и т.д. Проводя параллель с методом replace(), можно сказать, что в arr[n] заносится содержимое $n.

Например, взгляните на следующий программный код, выполняющий разбор URL-адреса:

Var url = /(\w+):\/\/([\w.]+)\/(\S*)/; var text = "Посетите наш сайт http://www..php"; var result = text.match(url); if (result != null) { var fullurl = result; // Содержит "http://www..php" var protocol = result; // Содержит "http" var host = result; // Содержит "www..php" }

Следует отметить, что для регулярного выражения, в котором не установлен флаг g глобального поиска, метод match() возвращает то же значение, что и метод exec() регулярного выражения: возвращаемый массив имеет свойства index и input, как описывается в обсуждении метода exec() ниже.

Последний из методов объекта String, в котором используются регулярные выражения - split() . Этот метод разбивает строку, для которой он вызван, на массив подстрок, используя аргумент в качестве разделителя. Например:

"123,456,789".split(","); // Вернет ["123","456","789"]

Метод split() может также принимать в качестве аргумента регулярное выражение. Это делает метод более мощным. Например, можно указать разделитель, допускающий произвольное число пробельных символов с обеих сторон:

"1, 2, 3 , 4 , 5".split(/\s*,\s*/); // Вернет ["1","2","3","4","5"]

Объект RegExp

Как было упомянуто, регулярные выражения представлены в виде объектов RegExp. Помимо конструктора RegExp(), объекты RegExp поддерживают три метода и несколько свойств.

Конструктор RegExp() принимает один или два строковых аргумента и создает новый объект RegExp. Первый аргумент конструктора - это строка, содержащая тело регулярного выражения, т.е. текст, который должен находиться между символами слэша в литерале регулярного выражения. Обратите внимание, что в строковых литералах и регулярных выражениях для обозначения управляющих последовательностей используется символ \, поэтому, передавая конструктору RegExp() регулярное выражение в виде строкового литерала, необходимо заменить каждый символ \ парой символов \\.

Второй аргумент RegExp() может отсутствовать. Если он указан, то определяет флаги регулярного выражения. Это должен быть один из символов g, i, m либо комбинация этих символов. Например:

// Находит все пятизначные числа в строке. Обратите внимание // на использование в этом примере символов \\ var zipcode = new RegExp("\\d{5}", "g");

Конструктор RegExp() удобно использовать, когда регулярное выражение создается динамически и поэтому не может быть представлено с помощью синтаксиса литералов регулярных выражений. Например, чтобы найти строку, введенную пользователем, надо создать регулярное выражение во время выполнения с помощью RegExp().

Свойства RegExp

Каждый объект RegExp имеет пять свойств. Свойство source - строка, доступная только для чтения, содержащая текст регулярного выражения. Свойство global - логическое значение, доступное только для чтения, определяющее наличие флага g в регулярном выражении. Свойство ignoreCase - это логическое значение, доступное только для чтения, определяющее наличие флага i в регулярном выражении. Свойство multiline - это логическое значение, доступное только для чтения, определяющее наличие флага m в регулярном выражении. И последнее свойство lastIndex - это целое число, доступное для чтения и записи. Для шаблонов с флагом g это свойство содержит номер позиции в строке, с которой должен быть начат следующий поиск. Как описано ниже, оно используется методами exec() и test().

Методы RegExp

Объекты RegExp определяют два метода, выполняющие поиск по шаблону; они ведут себя аналогично методам класса String, описанным выше. Основной метод класса RegExp, используемый для поиска по шаблону - exec() . Он похож на упоминавшийся метод match() класса String, за исключением того, что является методом класса RegExp, принимающим в качестве аргумента строку, а не методом класса String, принимающим аргумент RegExp.

Метод exec() выполняет регулярное выражение для указанной строки, т.е. ищет совпадение в строке. Если совпадение не найдено, метод возвращает null. Однако если соответствие найдено, он возвращает такой же массив, как массив, возвращаемый методом match() для поиска без флага g. Нулевой элемент массива содержит строку, соответствующую регулярному выражению, а все последующие элементы - подстроки, соответствующие всем подвыражениям. Кроме того, свойство index содержит номер позиции символа, которым начинается соответствующий фрагмент, а свойство input ссылается на строку, в которой выполнялся поиск.

В отличие от match(), метод exec() возвращает массив, структура которого не зависит от наличия в регулярном выражении флага g. Напомню, что при передаче глобального регулярного выражения метод match() возвращает массив найденных соответствий. А exec() всегда возвращает одно соответствие, но предоставляет о нем полную информацию. Когда exec() вызывается для регулярного выражения, содержащего флаг g, метод устанавливает свойство lastIndex объекта регулярного выражения равным номеру позиции символа, следующего непосредственно за найденной подстрокой.

Когда метод exec() вызывается для того же регулярного выражения второй раз, он начинает поиск с символа, позиция которого указана в свойстве lastIndex. Если exec() не находит соответствия, свойство lastIndex получает значение 0. (Вы также можете установить lastIndex в ноль в любой момент, что следует делать во всех тех случаях, когда поиск завершается до того, как будет найдено последнее соответствие в одной строке, и начинается поиск в другой строке с тем же объектом RegExp.) Это особое поведение позволяет вызывать exec() повторно для перебора всех соответствий регулярному выражению в строке. Например:

Var pattern = /Java/g; var text = "JavaScript - это более забавная штука, чем Java!"; var result; while((result = pattern.exec(text)) != null) { console.log("Найдено "" + result + """ + " в позиции " + result.index + "; следующий поиск начнется с " + pattern.lastIndex); }

Еще один метод объекта RegExp - test() , который намного проще метода exec(). Он принимает строку и возвращает true, если строка соответствует регулярному выражению:

Var pattern = /java/i; pattern.test("JavaScript"); // Вернет true

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

Регулярные выражения позволяют производить гибкий поиск в строках текста.

Создание

var expr = new RegExp(pattern [, flags]); // полная форма записи var expr = /pattern/flags; // сокращенная форма записи (литеральный формат)

Параметры

pattern Шаблон поиска (текст регулярного выражения). flags Способы поиска по шаблону:
  • g - глобальный поиск (обрабатываются все совпадения с шаблоном поиска);
  • i - не различать строчные и заглавные буквы;
  • m - многострочный поиск.
Порядок указания флагов не имеет значения.

Комментарии

Когда регулярное выражение создается при помощи конструктора new RegExp(…) , необходимо помнить, что обратные слеши (\) должны экранироваться, например:

Var expr = new RegExp("\\w", "ig");

При использовании литерального формата, этого делать не нужно:

Var expr = /\w/gi;

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

В регулярных выражениях различают следующие виды символов:

Обычные символы

Специальные символы

Спецсимволы в регулярном выражении

Символ Значение
\ Для обычных символов - делает их специальными. Например, выражение /s/ ищет просто символ "s". А если поставить \ перед s, то /\s/ уже обозначает пробельный символ. И наоборот, если символ специальный, например *, то \ сделает его просто обычным символом "звездочка". Например, /a*/ ищет 0 или больше подряд идущих символов "a". Чтобы найти а со звездочкой "a*" - поставим \ перед спец. символом: /a\*/ .
^ Обозначает начало входных данных. Если установлен флаг многострочного поиска ("m") , то также сработает при начале новой строки. Например, /^A/ не найдет "A" в "an A", но найдет первое "A" в "An A."
$ Обозначает конец входных данных. Если установлен флаг многострочного поиска, то также сработает в конце строки. Например, /t$/ не найдет "t" в "eater", но найдет - в "eat".
* Обозначает повторение 0 или более раз. Например, /bo*/ найдет "boooo" в "A ghost booooed" и "b" в "A bird warbled", но ничего не найдет в "A goat grunted".
+ Обозначает повторение 1 или более раз. Эквивалентно {1,} . Например, /a+/ найдет "a" в "candy" и все "a" в "caaaaaaandy".
? Обозначает, что элемент может как присутствовать, так и отсутствовать. Например, /e?le?/ найдет "el" в "angel" и "le" в "angle."Если используется сразу после одного из квантификаторов * , + , ? , или {} , то задает "нежадный" поиск (повторение минимально возможное количество раз, до ближайшего следующего элемента паттерна), в противоположность "жадному" режиму по умолчанию, при котором количество повторений максимально, даже если следующий элемент паттерна тоже подходит. Кроме того, ? используется в предпросмотре, который описан в таблице под (?=) , (?!) , и (?:) .
. (Десятичная точка) обозначает любой символ, кроме перевода строки: \n \r \u2028 or \u2029. (можно использовать [\s\S] для поиска любого символа, включая переводы строк). Например, /.n/ найдет "an" и "on" в "nay, an apple is on the tree", но не "nay".
(x) Находит x и запоминает. Это называется "запоминающие скобки". Например, /(foo)/ найдет и запомнит "foo" в "foo bar." Найденная подстрока хранится в массиве-результате поиска или в предопределенных свойствах объекта RegExp: $1, ..., $9 . Кроме того, скобки объединяют то, что в них находится, в единый элемент паттерна. Например, (abc)* - повторение abc 0 и более раз.
(?:x) Находит x , но не запоминает найденное. Это называется "незапоминающие скобки". Найденная подстрока не сохраняется в массиве результатов и свойствах RegExp. Как и все скобки, объединяют находящееся в них в единый подпаттерн.
x (?= y) Находит x , только если за x следует y . Например, /Jack(?=Sprat)/ найдет "Jack", только если за ним следует "Sprat". /Jack(?=Sprat|Frost)/ найдет "Jack", только если за ним следует "Sprat" или "Frost". Однако, ни "Sprat" ни "Frost" не войдут в результат поиска.
x (?! y) Находит x , только если за x не следует y . Например, /\d+(?!\.)/ найдет число, только если за ним не следует десятичная точка. /\d+(?!\.)/.exec("3.141") найдет 141, но не 3.141.
x | y Находит x или y . Например, /green|red/ найдет "green" в "green apple" и "red" в "red apple."
{n} Где n - положительное целое число. Находит ровно n повторений предшествующего элемента. Например, /a{2}/ не найдет "a" в "candy," но найдет оба a в "caandy," и первые два a в "caaandy."
{n,} Где n - положительное целое число. Находит n и более повторений элемента. Например, /a{2,} не найдет "a" в "candy", но найдет все "a" в "caandy" и в "caaaaaaandy."
{n,m} Где n и m - положительные целые числа. Находят от n до m повторений элемента.
Набор символов. Находит любой из перечисленных символов. Вы можете указать промежуток, используя тире. Например, - то же самое, что . Найдет "b" в "brisket" и "c" в "ache".
[^xyz] Любой символ, кроме указанных в наборе. Вы также можете указать промежуток. Например, [^abc] - то же самое, что [^a-c] . Найдет "r" в "brisket" и "h" в "chop."
[\b] Находит символ backspace. (Не путать с \b .)
\b Находит границу слов (латинских), например пробел. (Не путать с [\b]). Например, /\bn\w/ найдет "no" в "noonday"; /\wy\b/ найдет "ly" в "possibly yesterday."
\B Обозначает не границу слов. Например, /\w\Bn/ найдет "on" в "noonday", а /y\B\w/ найдет "ye" в "possibly yesterday."
\cX Где X - буква от A до Z. Обозначает контрольный символ в строке. Например, /\cM/ обозначает символ Ctrl-M.
\d находит цифру из любого алфавита (у нас же юникод). Используйте , чтобы найти только обычные цифры. Например, /\d/ или // найдет "2" в "B2 is the suite number."
\D Найдет нецифровой символ (все алфавиты). [^0-9] - эквивалент для обычных цифр. Например, /\D/ или /[^0-9]/ найдет "B" в "B2 is the suite number."
\f,\r,\n Соответствующие спецсимволы form-feed, line-feed, перевод строки.
\s Найдет любой пробельный символ, включая пробел, табуляцию, переводы строки и другие юникодные пробельные символы. Например, /\s\w*/ найдет " bar" в "foo bar."
\S Найдет любой символ, кроме пробельного. Например, /\S\w*/ найдет "foo" в "foo bar."
\t Символ табуляции.
\v Символ вертикальной табуляции.
\w Найдет любой словесный (латинский алфавит) символ, включая буквы, цифры и знак подчеркивания. Эквивалентно . Например, /\w/ найдет "a" в "apple," "5" в "$5.28," и "3" в "3D."
\W Найдет любой не-(лат.)словесный символ. Эквивалентно [^A-Za-z0-9_] . Например, /\W/ и /[^$A-Za-z0-9_]/ одинаково найдут "%" в "50%."
\n где n - целое число. Обратная ссылка на n-ю запомненную скобками подстроку. Например, /apple(,)\sorange\1/ найдет "apple, orange," в "apple, orange, cherry, peach.". За таблицей есть более полный пример.
\0 Найдет символ NUL. Не добавляйте в конец другие цифры.
\xhh Найдет символ с кодом hh (2 шестнадцатиричных цифры)
\uhhhh Найдет символ с кодом hhhh (4 шестнадцатиричных цифры).

Пример изменения формата строки

Var re = /(\w+)\s(\w+)/; var str = "John Smith"; var newstr = str.replace(re, "$2, $1"); alert(newstr); // "Smith, John"

Свойства

Методы

Выполняет поиск совпадения в своём строковом параметре.
Тестирует на совпадение в своём строковом параметре.

А также методы объекта String

match Выполняет поиск совпадения в строке. Возвращает массив информации, или null при отсутствии совпадения.
search Тестирует на наличие совпадений в строке. Возвращает индекс совпадения или -1, если поиск завершился неудачно.
replace Выполняет поиск совпадения в строке и заменяет найденные подстроки замещающей подстрокой.
split Использует регулярное выражение или фиксированную строку для разделения строки на массив подстрок.

global

Используется ли флаг "g " в регулярном выражении.

Синтаксис

regexp.global

global

Значение global будет true , если флаг "g g

//Определим произвольное регулярное выражение var regexp = /Шаблон пример/g; /* В случае если regexp содержит модификатор g выведем "Модификатор g установлен", в противном случае выведем "Модификатор g не установлен" */ if (regexp.global) alert("Модификатор g установлен"); else alert("Модификатор g не установлен");

ignoreCase

Используется ли флаг "i " в регулярном выражении.

Синтаксис

regexp.ignoreCase

Описание, комментарии, примеры

ignoreCase это свойство отдельного объекта регулярного выражения.

Вы не можете изменять это свойство явно.

Значение ignoreCase будет true , если флаг "i " используется; иначе - false . Флаг "i " указывает, что регистр символов должен игнорироваться при поиске совпадений в строке.

//Определим произвольное регулярное выражение var regexp = /Шаблон пример/i; /* В случае если regexp содержит модификатор i выведем "Модификатор i установлен", в противном случае выведем "Модификатор i не установлен" */ if (regexp.ignoreCase) alert("Модификатор i установлен"); else alert("Модификатор i не установлен");

lastIndex

Свойство lastIndex указывает позицию, с которой начнется следующий поиск.

Синтаксис

regexp.lastIndex

Комментарии

Это свойство будет работать только если в регулярном выражении установлен модификатор g .

Данное свойство возвращает целое число, которое обозначает позицию последнего символа найденного с помощью метода или .

Пример

//Определим произвольное регулярное выражение var str = "Я короткая строка"; //Зададим регулярное выражение var regexp = /о/g; //Совершим глобальный поиск "o" в строке текста и будем отображать //позицию после каждого найденного совпадения while (regexp.test(str)==true) { alert(regexp.lastIndex); }

multiline

Используется ли флаг "m " в регулярном выражении.

Синтаксис

regexp.global

Описание, комментарии, примеры

multiline это свойство отдельного объекта регулярного выражения.

Вы не можете изменять это свойство явно.

Значение multiline будет true , если флаг "g " используется; иначе - false . Флаг "g " указывает, что регулярное выражение должно проверяться относительно всех возможных совпадений в строке.

//Определим произвольное регулярное выражение var regexp = /Шаблон пример/m; /* В случае если regexp содержит модификатор g выведем "Модификатор g установлен", в противном случае выведем "Модификатор g не установлен" */ if (regexp.multiline) alert("Модификатор m установлен"); else alert("Модификатор g не установлен");

source

Свойство source возвращает содержимое шаблона регулярного выражения

В этой статье рассмотрены основы применения регулярного выражения в Javascript .

Введение

Что такое регулярное выражение?

Регулярное выражение JS – это последовательность символов, которая формирует правило поиска. Затем это правило можно использовать для поиска по тексту, а также его замены. На практике регулярное выражение может состоять даже из одного символа, но чаще встречаются более сложные шаблоны поиска.

В Javascript регулярные выражения также являются объектами. Это шаблоны, использующиеся для сопоставления последовательностей символов в строках. Они используются в методах exec() и test() объекта RegExp , а также в методах match() , replace() , search и split() объекта String .

Пример

var pattern = /example/i

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

Подготовка регулярного выражения

JS регулярные выражения состоят из шаблона и модификатора. Синтаксис будет примерно следующим:

/pattern/modifiers;

Шаблон задает правило поиска. Он состоит из простых символов наподобие /abc/ либо комбинации простых и специальных символов: /abc/ или /Chapter (d+).d/ .

Таблица шаблонов

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

Таблица модификаторов

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

Используем объект регулярного выражения

Создаем объект регулярного выражения

Этот объект описывает шаблон символов. Он используется для сопоставления шаблонов. Есть два способа сконструировать объект регулярного выражения.

Способ 1: используя литерал регулярного выражения, который состоит из шаблона, заключенного в слэши, например:

var reg = /ab+c/;

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

Способ 2: вызывая функцию-конструктор объекта RegExp , например:

var reg = new RegExp("ab+c");

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

Методы объекта регулярного выражения

Давайте познакомимся с несколькими распространенными методами объекта регулярного выражения:

  • compile() (устарел в версии 1.5 ) – компилирует регулярное выражение;
  • exec() – производит сопоставление в строке. Возвращает первое совпадение;
  • test() – производит сопоставление в строке. Возвращает значение true или false ;
  • toString() – возвращает строчное значение регулярного выражения.

Примеры

Применение test()

Метод test() – регулярное выражение объекта RegExp . Он производит поиск строки шаблона, и в зависимости от полученного результата возвращает значение true или false . Следующий JS регулярного выражения пример показывает, как происходит поиск в строке символа “e ”:

var patt = /e/; patt.test("Лучшие в мире вещи – бесплатны!");

Так как здесь в строке имеется “e ”, результатом данного кода будет значение true .

Регулярные выражения вовсе необязательно помещать в переменную. Такой же запрос можно провести в одну строку:

/e/.test("Лучшие в мире вещи – бесплатны!");

Применение exec()

Он производит поиск в строке по заданному правилу поиска, и возвращает найденный текст. Если совпадений найдено не было, то результатом будет null .

Посмотрим на метод в действии, на примере того же символа “e ”:

/e/.exec("Лучшие в мире вещи – бесплатны!");

Так как в строке имеется “e ”, результатом данного кода будет .e .

Применение регулярного выражения для строки

В Javascript эти выражения также можно использовать с двумя методами объекта String : search() и replace() . Они нужны для выполнения поиска и замены в тексте.

  • Метод search() — использует выражение для поиска соответствия, и возвращает информацию о расположении соответствия;
  • Метод replace() — возвращает модифицированную строку с замененным шаблоном.

Примеры

Применение регулярного выражения JS для осуществления чувствительного к регистру поиска фразы “w3schools ” в строке:

var str = "Visit W3Schools"; var n = str.search(/w3schools/i);

Результатом в n будет 6.

Метод search также принимает строку в качестве аргумента. Аргумент string будет преобразован в регулярное выражение:

Применение string для поиска фразы “W3schools ” в строке.

mob_info