Алгоритм останавливается, как только найдёт полное совпадение. Это значит, что даже если несколько вариантов могут подойти, используется только один из них (в том порядке, в каком они появляются в регулярке).
Откаты случаются при использовании операторов повторения, таких, как +
и *
. Если вы ищете /^.*x/
в строке "abcxe"
, часть регулярки .*
попробует поглотить всю строчку. Алгоритм затем сообразит, что ему нужен ещё и “x”
. Так как никакого “x”
после конца строки нет, алгоритм попробует поискать совпадение, откатившись на один символ. После abcx
тоже нет x
, тогда он снова откатывается, уже к подстроке abc
. И после строчки он находит x
и докладывает об успешном совпадении, на позициях с 0 по 4.
Можно написать регулярку, которая приведёт ко множественным откатам. Такая проблема возникает, когда шаблон может совпасть с входными данными множеством разных способов. Например, если мы ошибёмся при написании регулярки для двоичных чисел, мы можем случайно написать что-то вроде /([01]+)+b/
.
Если алгоритм будет искать такой шаблон в длинной строке из нолей и единиц, не содержащей в конце “b”, он сначала пройдёт по внутренней петле, пока у него не кончатся цифры. Тогда он заметит, что в конце нет “b”, сделает откат на одну позицию, пройдёт по внешней петле, опять сдастся, попытается откатиться на ещё одну позицию по внутренней петле… И будет дальше искать таким образом, задействуя обе петли. То есть, количество работы с каждым символом строки будет удваиваться. Даже для нескольких десятков символов поиск совпадения займёт очень долгое время.
У строк есть метод replace
, который может заменять часть строки другой строкой.
console.log("папа".replace("п", "м"));
// → мапа
Первый аргумент может быть и регуляркой, в каком случае заменяется первое вхождение регулярки в строке. Когда к регулярке добавляется опция g
(global, всеобщий), заменяются все вхождения, а не только первое.
console.log("Borobudur".replace(/[ou]/, "a"));
// → Barobudur
console.log("Borobudur".replace(/[ou]/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+
. И затем вносит изменения в слово, на случай, если остался всего один предмет или ни одного.
Несложно при помощи replace
написать функцию, убирающую все комментарии из кода JavaScript. Вот первая попытка:
Читать дальше