Методы RegExp и String
Есть два набора методов для работы с регулярными выражениями.
- Объекты встроенного класса JavaScript RegExp, предоставляющего множество методов;
- Методы обычных строк.
Сначала мы рассмотрим методы, а потом – их применение на практических примерах.
str.search(reg)
Данный метод возвращает позицию первого совпадения или -1 , если ничего не найдено.
let str = "A drop of ink may make a million think";
alert( str.search( /a/i ) ); // 0 (первая позиция)
Важная информация: search всегда ищет первое совпадение.
Невозможно найти следующую позицию, используя search. Но есть другие методы, которые это умеют.
str.match(reg), без флага “g”
Поведение метода str.match меняется в зависимости от флага g. Сначала рассмотрим случай без него. Тогда str.match(reg) ищет только первое совпадение. Результат — массив с найденным совпадением и дополнительными свойствами:
- index – позиция совпадения в строке;
- input – рассматриваемая строка.
JavaScript RegExp пример:
let str = "Fame is the thirst of youth";
let result = str.match( /fame/i );
alert( result[0] ); // Fame ( совпадение)
alert( result.index ); // 0 (на позиции 0)
alert( result.input ); // "Fame is the thirst of youth" (строка)
В массиве может быть больше одного элемента.
Если часть шаблона выделена скобками (…), то она становится отдельным элементом массива.
Например:
let str = "JavaScript is a programming language";
let result = str.match( /JAVA(SCRIPT)/i );
alert( result[0] ); // JavaScript (все совпадение)
alert( result[1] ); // script (часть совпадения, которая отвечает скобкам)
alert( result.index ); // 0
alert( result.input ); // JavaScript is a programming language
Из-за флага i поиск не зависит от регистра, и находит JavaScript. Часть совпадения, которая отвечает строке SCRIPT, становится отдельным элементом.
str.match(reg) с флагом “g”
Когда есть флаг «g«, str.match возвращает массив всех совпадений. В этом массиве нет дополнительных свойств, а скобки не создают элементы.
Пример использования JavaScript RegExp match:
let str = "HO-Ho-ho!";
let result = str.match( /ho/ig );
alert( result ); // HO, Ho, ho (все совпадения, независимо от регистра)
Со скобками ничего не меняется, смотрите:
let str = "HO-Ho-ho!";
let result = str.match( /h(o)/ig );
alert( result ); // HO, Ho, ho
Итак, с флагом g результат – простой массив совпадений без дополнительных свойств.
Если нужно получить информацию о позициях совпадений и использовать скобки, то следует применить метод RegExp#exec, который мы рассмотрим ниже.
Если совпадений нет, вызов match вернет null.
Если не было совпадений, то результат — не пустой массив, аnull.
Держите это в голове, чтобы избежать подводных камней вроде этого:
let str = "Hey-hey-hey!";
alert( str.match(/ho/gi).length ); // ошибка! У null нет длины
str.split(regexp|substr, limit)
Разбивает строку, используя регулярное выражение (или подстроку) с помощью разделителя.
Мы уже использовали метод Java Script RegExp split со строками, например, так:
alert('12-34-56'.split('-')) // [12, 34, 56]
Но мы также можем передать регулярное выражение:
alert('12-34-56'.split(/-/)) // [12, 34, 56]
str.replace(str|reg, str|func)
Швейцарский нож для поиска и замены в строках. Самое простое применение – поиск и замена подстроки, например:
// заменить тире на двоеточие
alert('12-34-56'.replace("-", ":")) // 12:34-56
Когда первый аргумент replace — это строка, ищет только первое совпадение.
Чтобы найти все тире, нужно использовать не строку «-«, а регулярное выражение /-/g, с обязательным флагом g:
// заменить все тире на двоеточия
alert( '12-34-56'.replace( /-/g, ":" ) ) // 12:34:56
Второй аргумент — это строка-заменитель.
В JavaScript RegExp примерах можно использовать специальные символы:
Символ | Вставляет |
$$ | «$» |
$& | все совпадение |
$` | Часть строки перед совпадением |
$’ | Часть строки после совпадения |
$n | если n – число из одной или двух цифр. Это обозначает содержимое n-й скобки, слева направо |
Используем $&, чтобы заменить все вхождения «Джон»»John» на «г-н Джон» «Mr.John»:
let str = "John Doe, John Smith and John Bull.";
// для каждого John — заменить на Mr. А затем John
alert(str.replace(/John/g, 'Mr.$&'));
// "Mr.John Doe, Mr.John Smith and Mr.John Bull.";
Скобки часто используются вместе с $1, $2, например:
let str = "John Smith";
alert(str.replace(/(John) (Smith)/, '$2, $1')) // Smith, John
Для ситуаций, которые требуют «умных» замен, второй аргумент может быть функцией. Она будет вызываться для каждого совпадения, а ее результат будет вставлен как замена.
Например:
let i = 0;
// заменить каждое "ho" на результат функции
alert("HO-Ho-ho".replace(/ho/gi, function() {
return ++i;
})); // 1-2-3
В примере, приведенном выше, функция каждый раз возвращает следующее число, но обычно результат основан на совпадении.
Функция вызывается с аргументами func(str, p1, p2, …, pn, offset, s):
- str – совпадение;
- p1, p2, …, pn – содержимое скобок (если они есть);
- offset – позиция совпадения;
- s – исходная строка.
Если в JavaScript RegExp нет скобок, то у функции всегда есть три аргумента: func(str, offset, s). Используем ее, чтобы показать все совпадения:
// показать и заменить все совпадения
function replacer(str, offset, s) {
alert(`Найдено ${str} на позиции ${offset} в строке ${s}`);
return str.toLowerCase();
}
let result = "HO-Ho-ho".replace(/ho/gi, replacer);
alert( 'Результат: ' + result ); // Результат: ho-ho-ho
// Показать каждое совпадение:
// Найдено HO на позиции 0 в строке HO-Ho-ho
// Найдено Ho на позиции 3 в строке HO-Ho-ho
// Найдено ho на позиции 6 в строке HO-Ho-ho
В примере, приведенном ниже, есть две скобки, так что replacer («заменитель») вызывается с пятью аргументами: str — это полное совпадение, скобки, а затем offset (смещение) и s:
function replacer(str, name, surname, offset, s) {
// имя - это первые круглые скобки, фамилия – вторые
return surname + ", " + name;
}
let str = "John Smith";
alert(str.replace(/(John) (Smith)/, replacer)) // Smith, John
Функция получает всю информацию о совпадениях, имеет доступ к внешним переменным и может делать все.
regexp.test(str)
Перейдем к методам класса RegExp, которые вызываются для самих регулярных выражений.
Метод JavaScript RegExp test ищет любое совпадение и возвращает результат true/false. Так что это в принципе то же самое, что и str.search(reg) != -1, например:
let str = "I love JavaScript";
// эти две проверки делают одно и то же
alert( /love/i.test(str) ); // true
alert( str.search(/love/i) != -1 ); // true
Пример с отрицательным ответом:
str = "Bla-bla-bla";
alert( /love/i.test(str) ); // false
alert( str.search(/love/i) != -1 ); // false
regexp.exec(str)
Мы уже рассматривали эти методы:
- search – ищет позицию совпадения;
- match – если нет флага g, возвращает первое совпадение со скобками;
- match – если есть флаг g – возвращает все совпадения без отделяющих скобок.
Метод regexp.exec труднее использовать, но он позволяет искать все совпадения без скобок и позиций. Ведет себя по-разному, в зависимости от наличия флага g в регулярном выражении.
- Если нет флага g, то regexp.exec(str) возвращает первое совпадение, так же как str.match(reg);
- Если есть флаг g, то regexp.exec(str) возвращает первое совпадение и запоминает позицию за ним в свойстве regexp.lastIndex.
Следующий вызов начнет искать с позиции regexp.lastIndex и вернет следующее совпадение. Если совпадений больше нет, то regexp.exec возвращает null, а regexp.lastIndex устанавливается в 0.
Как видим, метод JavaScript RegExp exec не дает ничего нового, если мы используем его без флага g, так как str.match делает то же самое. Но флаг g позволяет получить все совпадения с их позициями и группами скобок.
Вот пример того, как последовательные вызовы regexp.exec возвращают совпадения одно за другим:
let str = "A lot about JavaScript at https://javascript.info";
let regexp = /JAVA(SCRIPT)/ig;
// Ищем первое совпадение
let matchOne = regexp.exec(str);
alert( matchOne[0] ); // JavaScript
alert( matchOne[1] ); // script
alert( matchOne.index ); // 12 (позиция совпадения)
alert( matchOne.input ); // то же, что и str
alert( regexp.lastIndex ); // 22 (позиция после совпадения)
// Ищем второе совпадение
let matchTwo = regexp.exec(str); // продолжим поиск с индекса regexp.lastIndex
alert( matchTwo[0] ); // javascript
alert( matchTwo[1] ); // script
alert( matchTwo.index ); // 34 (позиция совпадения)
alert( matchTwo.input ); // то же, что и str
alert( regexp.lastIndex ); // 44 (позиция после совпадения)
// Ищем третье совпадение
let matchThree = regexp.exec(str); // продолжим поиск с regexp.lastIndex
alert( matchThree ); // null (без совпадений)
alert( regexp.lastIndex ); // 0 (сбрасывание)
Как видим, каждый вызов JavaScript RegExp exec возвращает совпадение в «полном формате»: как массив со скобками, свойствами index и input.
Основное применение regexp.exec – находить все совпадения в цикле:
let str = 'A lot about JavaScript at https://javascript.info';
let regexp = /javascript/ig;
let result;
while (result = regexp.exec(str)) {
alert( `Найдено ${result[0]} на позиции ${result.index}` );
}
Цикл продолжается, пока regexp.exec не вернет null, что значит «совпадений больше нет».
Поиск от заданной позиции
Можно заставить regexp.exec начать поиск с данной позиции, установив вручную lastIndex:
let str = 'A lot about JavaScript at https://javascript.info';
let regexp = /javascript/ig;
regexp.lastIndex = 30;
alert( regexp.exec(str).index ); // 34, поиск начинается с 30-й позиции
Флаг «y»
Флаг y значит, что поиск должен найти совпадение на позиции, указанной в свойстве regexp.lastIndex, и только там.
Другими словами, обычно поиск идет по всей строке: /javascript/ ищет подстроку “javascript”. Но когда JavaScript RegExp содержит флаг y, оно ищет только совпадение на позиции, указанной в regexp.lastIndex(по умолчанию — 0).
Например:
let str = "I love JavaScript!";
let reg = /javascript/iy;
alert( reg.lastIndex ); // 0 (по умолчанию)
alert( str.match(reg) ); // null, не найдено на позиции 0
reg.lastIndex = 7;
alert( str.match(reg) ); // JavaScript (это слово начинается на позиции 7)
// для любого другого индекса reg.lastIndex результат - null
Регулярное выражение /javascript/iy может быть найдено, только если установить reg.lastIndex=7, так как из-за флага y программа пытается найти его только в одном месте внутри строки — на позиции reg.lastIndex.
Так в чем же смысл? В производительности.
Флаг y отлично работает для парсеров – программ, которым нужно «читать» текст и строить в памяти синтаксическую структуру или производить на ее основании действия. Для этого мы двигаемся по тексту и применяем регулярные выражения, чтобы увидеть, что находится дальше: строка, число или что-то еще.
Флаг y позволяет применять регулярное выражение (или несколько одно за другим) именно на заданной позиции. И когда мы поймем, что там, то сможем двигаться дальше, шаг за шагом исследуя текст.
Без флага y регулярное выражение всегда ищет до конца текста, что занимает время, особенно если текст большой. Тогда наш парсер будет медленным. Флаг y — это то, что нужно в подобном случае.
Выводы, рецепты
Будет намного проще понять методы JavaScript RegExp, если мы разделим их по использованию на практике.
Чтобы найти только первое совпадение:
- Найти позицию первого совпадения – str.search(reg);
- Найти полное совпадение – str.match(reg);
- Проверить, есть ли совпадение – regexp.test(str);
- Найти совпадение с заданной позиции – regexp.exec(str), установите regexp.lastIndex в номер позиции.
Чтобы найти все совпадения:
- Массив совпадений – str.match(reg), регулярное выражение с флагом g;
- Получить все совпадения с полной информацией о каждом из них – regexp.exec(str) с флагом g в цикле.
Чтобы найти и заменить:
- Заменить одну строку на другую или результат работы функции – str.replace(reg, str|func).
Чтобы разделить строку:
- str.split(str|reg).
Кроме этого мы изучили два флага JavaScript RegExp:
- Флаг g — чтобы найти все совпадения (глобальный поиск);
- Флаг y — чтобы искать на точно заданной позиции внутри текста.
Теперь мы знаем методы и можем использовать регулярные выражения.
Данная публикация представляет собой перевод статьи «Methods of RegExp and String» , подготовленной дружной командой проекта Интернет-технологии.ру