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