Будьте в курсе последних событий, подпишитесь на обновления сайта

Особенности регулярных выражений в Javascript

Особенности регулярных выражений в Javascript

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

Особенности регулярных выражений в javascript

 

Точка и перенос строки

Для поиска в многострочном режиме почти все модификации  регэкспов используют специальный multiline-флаг.

И javascript тут не исключение.

Попробуем же сделать поиск и замену многострочного вхождения. Скажем, будем заменять [u] … [/u] на тэг подчеркивания: <u>:

function bbtagit(text) { 
text = text.replace(/\[u\](.*?)\[\/u\]/gim, '<u>$1</u>') 
return text } var line = "[u]мой\n текст[/u]"
 alert(bbtagit(line))

Попробуйте запустить. Заменяет? А вот и нет!

Дело в том, что в javascript мультилайн режим (флаг m) влияет только на символы начала и конца строки ^ и $, а не всего текста.

Точка это – любой символ, кроме новой строки. В javascript отсутствует флаг, который устанавливает мультилайн-режим для точки. Для того, чтобы найти совсем все что угодно – надо использовать [\s\S].

Вот вариант, который будет работать:

function bbtagit(text) { 
text = text.replace(/\[u\]([\s\S]*)\[\/u\]/gim, '<u>$1</u>') 
return text } 
var line = "[u]мой\n текст[/u]" 
alert(bbtagit(line))

Жадность

Это не совсем особенность, скорее фича, но все же достойная отдельного упоминания. Как говорится  это не баг —  это фича.

Все регулярные выражения в javascript – жадные.  Таким образом выражение старается всегда отхватить как можно больший кусок строки.

Например, мы хотим заменить все открывающие тэги <a>. На что и почему – не так уж важно.

var text = '1 <A href="#">...</A> 2'
 text = text.replace(/<A(.*)>/, 'TEST') 
alert(text)

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

Читайте также  Работа со строками в JavaScript

Это происходит из-за того, что точка-звездочка в «жадном» режиме пытается захватить как можно больше, в нашем случае – это как раз до последнего >.

Последний символ > точка-звездочка не захватывает, т.к. иначе не будет совпадения.

Как вариант решения можно использовать квадратные скобки: [^>]:

var text = '1 <A href="#">...</A> 2' 
text = text.replace(/<A([^>]*)>/, 'TEST') 
alert(text)

Это работает. Но самым удобным вариантом будет переключение точки-звездочки в нежадный режим. Это осуществляется простым добавлением знака  вопроса —  «?» после звездочки.

В нежадном режиме точка-звездочка пустит поиск дальше сразу, как только нашла совпадение:

var text = '1 <A href="#">...</A> 2' 
text = text.replace(/<A(.*?)>/, 'TEST') 
alert(text)

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

В javascript этого сделать нельзя… Вот такая вот особенность. А вопросительный знак после звездочки работает – уж можете мне поверить.

Обратные ссылки в шаблон и при замене

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

Например, при поиске BB-тегов, то есть строк вида [u]…[/u], [b]…[/b] и [s]…[/s]. Или при поиске атрибутов, которые могут быть в одинарных кавычках или двойных.

Обращение к предыдущей части шаблона в javascript осуществляется как \1, \2 и т.п., обратный + номер скобочной группы:

var text = ' [b]a [u]b[/u] c [/b] '; 
var reg = /\[([bus])\](.*?)\[\/\1\] /; 
text = text.replace(reg, '<$1>$2</$1>'); // <b>a [u]b[/u] c </b> 
alert(text);

Обращение же к скобочной группе в строке замены идет уже через доллар: $1. Не знаю, почему, ну наверное так удобнее…

Найти все / Заменить все

Эти две задачи решаются в javascript принципиально по-разному.

Начнем с «простого».

Заменить все

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

Читайте также  Жадные и ленивые квантификаторы в регулярных выражениях

Если первый аргумент – строка, то будет осуществлен поиск подстроки, без преобразования в регулярное выражение.

Попробуйте:

alert("2 ++ 1".replace("+", "*"))

Как видите, заменился только один плюс, а не все.

Чтобы заменить все вхождения, replace обязательно нужно использовать с регулярным выражением.

В режиме регулярного выражения плюс придётся экранировать, но зато replace заменит все вхождения (естественно при указании флага g):

alert("2 ++ 1".replace(/\+/g, "*"))

Вот такая особенность работы со строкой.

Заменить функцией

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

Следующий пример произведет операции вычитания:

var str = "count 36 - 26, 18 - 9" 
str = str.replace(/(\d+) - (\d+)/g, function(a,b,c) { return b-c }) alert(str)

Найти всё

В javascript нет единого универсального метода для поиска всех совпадений. Для поиска без запоминания скобочных групп – можно использовать  match:

var str = "count 36-26, 18-9"; 
var re = /(\d+)-(\d+)/g; 
var result = str.match(re); 
for (var i = 0; i < result.length; i++) { alert(result[i]); }

Как видите, оно исправно ищет совпадения (флаг «g» у регулярного выражения обязателен), но при этом не запоминает скобочные группы. Эдакий «лайт вариант».

Найти всё с учётом скобочных групп

В сложных задачах важны не только совпадения, но и скобочные группы. Чтобы их найти, предлагается использовать многократный вызов exec.
Для этого регулярное выражение должно использовать флаг «g». Тогда результат поиска, запомненный в свойстве lastIndex объекта RegExp используется как точка отсчета для следующего поиска:

var str = "count 36-26, 18-9" 
var re = /(\d+)-(\d+)/g 
var res while ((res = re.exec(str)) != null) {
 alert("Найдено " + res[0] + ":  (" + res[1] + ") и (" + res[2] + ")") 
alert("Дальше ищу с позиции " + re.lastIndex) }

Проверка while( (res = re.exec(str)) != null) нужна т.к. значение res = 0 является хорошим и означает, что вхождение найдено в самом начале строки (поиск успешен). Поэтому необходимо сравнивать именно с null.

Поделиться

Об авторе

admin administrator

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: