17 KiB
Методы RegExp и String
Регулярные выражения в JavaScript являются объектами класса RegExp.
Кроме того, методы для поиска по регулярным выражениям встроены прямо в обычные строки String
.
К сожалению, общая структура встроенных методов слегка запутана, поэтому мы сначала рассмотрим их по отдельности, а затем -- рецепты по решению стандартных задач с ними.
[cut]
str.search(reg)
Этот метод мы уже видели.
Он возвращает позицию первого совпадения или -1
, если ничего не найдено.
//+ run
var str = "Люблю регэкспы я, но странною любовью";
alert( str.search( *!*/лю/i*/!* ) ); // 0
Ограничение метода search
-- он всегда ищет только первое совпадение.
Нельзя заставить search
искать дальше первого совпадения, такой синтаксис попросту не предусмотрен. Но есть другие методы, которые это умеют.
str.match(reg) без флага g
Метод str.match
работает по-разному, в зависимости от наличия или отсутствия флага g
, поэтому сначала мы разберём вариант, когда его нет.
В этом случае str.match(reg)
находит только одно, первое совпадение.
Результат вызова -- это массив, состоящий из этого совпадения, с дополнительными свойствами index
-- позиция, на которой оно обнаружено и input
-- строка, в которой был поиск.
Например:
//+ run
var str = "ОЙ-Ой-ой";
var result = str.match( *!*/ой/i*/!* );
alert( result[0] ); // ОЙ (совпадение)
alert( result.index ); // 0 (позиция)
alert( result.input ); // ОЙ-Ой-ой (вся поисковая строка)
У этого массива не всегда только один элемент.
Если часть шаблона обозначена скобками, то она станет отдельным элементом массива.
Например:
//+ run
var str = "javascript - это такой язык";
var result = str.match( *!*/JAVA(SCRIPT)/i*/!* );
alert( result[0] ); // javascript (всё совпадение полностью)
alert( result[1] ); // script (часть совпадения, соответствующая скобкам)
alert( result.index ); // 0
alert( result.input ); // javascript - это такой язык
Благодаря флагу i
поиск не обращает внимание на регистр буквы, поэтому находит javascript
. При этом часть строки, соответствующая SCRIPT
, выделена в отдельный элемент массива.
Позже мы ещё вернёмся к скобочным выражениям, они особенно удобны для поиска с заменой.
str.match(reg) с флагом g
При наличии флага g
, вызов match
возвращает обычный массив из всех совпадений.
Никаких дополнительных свойств у массива в этом случае нет, скобки дополнительных элементов не порождают.
Например:
//+ run
var str = "ОЙ-Ой-ой";
var result = str.match( *!*/ой/ig*/!* );
alert( result ); // ОЙ, Ой, ой
Пример со скобками:
//+ run
var str = "javascript - это такой язык";
var result = str.match( *!*/JAVA(SCRIPT)/gi*/!* );
alert( result[0] ); // javascript
alert( result.length ); // 1
alert( result.index ); // undefined
Из последнего примера видно, что элемент в массиве ровно один, и свойства index
также нет. Такова особенность глобального поиска при помощи match
-- он просто возвращает все совпадения.
Для расширенного глобального поиска, который позволит получить все позиции и, при желании, скобки, нужно использовать метод RegExp#exec, которые будет рассмотрен далее.
[warn header="В случае, если совпадений не было, match
возвращает null
"]
Обратите внимание, это важно -- если match
не нашёл совпадений, он возвращает не пустой массив, а именно null
.
Это важно иметь в виду, чтобы не попасть в такую ловушку:
//+ run
var str = "Ой-йой-йой";
// результат match не всегда массив!
alert(str.match(/лю/gi).length) // ошибка! нет свойства length у null
[/warn]
str.split(reg|substr, limit)
Разбивает строку в массив по разделителю -- регулярному выражению regexp
или подстроке substr
.
Обычно мы используем метод split
со строками, вот так:
//+ run
alert('12-34-56'.split('-')) // [12, 34, 56]
Можно передать в него и регулярное выражение, тогда он разобьёт строку по всем совпадениям.
Тот же пример с регэкспом:
//+ run
alert('12-34-56'.split(/-/)) // [12, 34, 56]
str.replace(reg, str|func)
Швейцарский нож для работы со строками, поиска и замены любого уровня сложности.
Его простейшее применение -- поиск и замена подстроки в строке, вот так:
//+ run
// заменить дефис на двоеточие
alert('12-34-56'.replace("-", ":")) // 12:34-56
При вызове со строкой замены replace
всегда заменяет только первое совпадение.
Чтобы заменить все совпадения, нужно использовать для поиска не строку "-"
, а регулярное выражение /-/g
, причём обязательно с флагом g
:
//+ run
// заменить дефис на двоеточие
alert( '12-34-56'.replace( *!*/-/g*/!*, ":" ) ) // 12:34:56
В строке для замены можно использовать специальные символы:
Спецсимволы | Действие в строке замены |
---|---|
`$$` | Вставляет `"$"`. |
`$&` | Вставляет всё найденное совпадение. |
$` |
Вставляет часть строки до совпадения. |
$'
|
Вставляет часть строки после совпадения. |
$*n*
|
где `n` -- цифра или двузначное число, обозначает `n-ю` по счёту скобку, если считать слева-направо. |
Пример использования скобок и $1
, $2
:
//+ run
var str = "Василий Пупкин";
alert(str.replace(/(Василий) (Пупкин)/, '$2, $1')) // Пупкин, Василий
Ещё пример, с использованием $&
:
//+ run
var str = "Василий Пупкин";
alert(str.replace(/Василий Пупкин/, 'Великий $&!')) // Великий Василий Пупкин!
Для ситуаций, который требуют максимально "умной" замены, в качестве второго аргумента предусмотрена функция.
Она будет вызвана для каждого совпадения, и её результат будет вставлен как замена.
Например:
//+ run
var i = 0;
// заменить каждое вхождение "ой" на результат вызова функции
alert("ОЙ-Ой-ой".replace(/ой/gi, function() {
return ++i;
})); // 1-2-3
В примере выше функция просто возвращала числа по очереди, но обычно она основывается на поисковых данных.
Эта функция получает следующие аргументы:
- `str` -- найденное совпадение,
- `p1, p2, ..., pn` -- содержимое скобок (если есть),
- `offset` -- позиция, на которой найдено совпадение,
- `s` -- исходная строка.
Если скобок в регулярном выражении нет, то у функции всегда будет ровно 3 аргумента: replacer(str, offset, s)
.
Используем это, чтобы вывести полную информацию о совпадениях:
//+ run
// вывести и заменить все совпадения
function replacer(str, offset, s) {
alert( "Найдено: " + str + " на позиции: " + offset + " в строке: " + s );
return str.toLowerCase();
}
var result = "ОЙ-Ой-ой".replace(/ой/gi, replacer);
alert( 'Результат: ' + result ); // Результат: ой-ой-ой
С двумя скобочными выражениями -- аргументов уже 5:
//+ run
function replacer(str, name, surname, offset, s) {
return surname + ", " + name;
}
alert(str.replace(/(Василий) (Пупкин)/, replacer)) // Пупкин, Василий
Функция -- это самый мощный инструмент для замены, какой только может быть. Она владеет всей информацией о совпадении и имеет доступ к замыканию, поэтому может всё.
regexp.test(str)
Теперь переходим к методам класса RegExp
.
Метод test
проверяет, есть ли хоть одно совпадение в строке str
. Возвращает true/false
.
Работает, по сути, так же, как и проверка str.search(reg) != -1
, например:
//+ run
var str = "Люблю регэкспы я, но странною любовью";
// эти две проверки идентичны
alert( *!*/лю/i*/!*.test(str) ) // true
alert( str.search(*!*/лю/i*/!*) != -1 ) // true
Пример с отрицательным результатом:
//+ run
var str = "Ой, цветёт калина...";
alert( *!*/javascript/i*/!*.test(str) ) // false
alert( str.search(*!*/javascript/i*/!*) != -1 ) // false
regexp.exec(str)
Для поиска мы уже видели методы:
- `search` -- ищет индекс
- `match` -- если регэксп без флага `g` -- ищет совпадение с подрезультатами в скобках
- `match` -- если регэксп с флагом `g` -- ищет все совпадения, но без скобочных групп.
Метод regexp.exec
дополняет их. Он позволяет искать и все совпадения и скобочные группы в них.
Он ведёт себя по-разному, в зависимости от того, есть ли у регэкспа флаг g
.
- Если флага `g` нет, то `regexp.exec(str)` ищет и возвращает первое совпадение, является полным аналогом вызова `str.match(reg)`.
- Если флаг `g` есть, то вызов `regexp.exec` возвращает первое совпадение и *запоминает* его позицию в свойстве `regexp.lastIndex`. Последующий поиск он начнёт уже с этой позиции. Если совпадений не найдено, то сбрасывает `regexp.lastIndex` в ноль.
Это используют для поиска всех совпадений в цикле:
//+ run
var str = 'Многое по JavaScript можно найти на сайте http://javascript.ru';
var regexp = /javascript/ig;
alert( "Начальное значение lastIndex: " + regexp.lastIndex );
while (result = regexp.exec(str)) {
alert( 'Найдено: ' + result[0] + ' на позиции:' + result.index );
alert( 'Свойство lastIndex: ' + regexp.lastIndex );
}
alert( 'Конечное значение lastIndex: ' + regexp.lastIndex );
Здесь цикл продолжается до тех пор, пока regexp.exec
не вернёт null
, что означает "совпадений больше нет".
Найденные результаты последовательно помещаются в result
, причём находятся там в том же формате, что и match
-- с учётом скобок, со свойствами result.index
и result.input
.
[smart header="Поиск с нужной позиции"]
Можно заставить regexp.exec
искать сразу с нужной позиции, если поставить lastIndex
вручную:
//+ run
var str = 'Многое по JavaScript можно найти на сайте http://javascript.ru';
var regexp = /javascript/ig;
regexp.lastIndex = 40;
alert( regexp.exec(str).index ); // 49, поиск начат с 40й позиции
[/smart]
Итого, рецепты
Методы становятся гораздо понятнее, если разбить их использование по задачам, которые нужны в реальной жизни.
- Для поиска только одного совпадения:
-
- Найти позицию первого совпадения -- `str.search(reg)`.
- Найти само совпадение -- `str.match(reg)`.
- Проверить, есть ли хоть одно совпадение -- `regexp.test(str)` или `str.search(reg) != -1`.
- Найти совпадение с нужной позиции -- `regexp.exec(str)`, начальную позицию поиска задать в `regexp.lastIndex`.
- Для поиска всех совпадений:
-
- Найти массив совпадений -- `str.match(reg)`, с флагом `g`.
- Получить все совпадения, с подробной информацией о каждом -- `regexp.exec(str)` с флагом `g`, в цикле.
- Для поиска-и-замены:
-
- Замена на другую строку или функцией -- `str.replace(reg, str|func)`
- Для разбивки строки на части:
-
- `str.split(str|reg)`
Зная эти методы, мы уже можем использовать регулярные выражения.
Конечно, для этого желательно хорошо понимать их синтаксис и возможности, так что переходим к ним дальше.