This commit is contained in:
Ilya Kantor 2014-10-26 22:10:13 +03:00
parent 06f61d8ce8
commit f301cb744d
2271 changed files with 103162 additions and 0 deletions

View file

@ -0,0 +1,376 @@
# Методы RegExp и String
В JavaScript методы для работы с регулярными выражениями есть в классе `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 (часть совпадения, соответствующая скобкам)
// также есть свойства result.index, result.input
```
Благодаря флагу `i` поиск не обращает внимание на регистр буквы, поэтому находит <code class="match">javascript</code>. При этом часть строки, соответствующая <code class="pattern">SCRIPT</code>, выделена в отдельный элемент массива. Позже мы используем это для поиска с заменой.
### 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], которые будет рассмотрен далее.
[warn header="В случае, если совпадений не было, `match` возвращает `null`"]
Обратите внимание, это важно -- если `match` не нашёл совпадений, он возвращает не пустой массив, а именно `null`.
Это важно иметь в виду, чтобы не попасть в такую ловушку:
```js
//+ run
var str = "Ой-йой-йой";
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, newSubStr|function)
Швейцарский нож для работы со строками, поиска и замены любого уровня сложности.
Его простейшее применение -- поиск и замена подстроки в строке, вот так:
```js
//+ run
// заменить дефис на двоеточие
alert( '12-34-56'.replace("-", ":" ) ) // 12:34-56
```
При вызове со строкой замены `replace` всегда заменяет только первое совпадение.
**Чтобы заменить *все* совпадения, нужно использовать для поиска не строку `"-"`, а регулярное выражение <code class="pattern">/-/g</code>, причём обязательно с флагом `g`:**
```js
//+ run
// заменить дефис на двоеточие
alert( '12-34-56'.replace( *!*/-/g*/!*, ":" ) ) // 12:34:56
```
В строке для замены можно использовать специальные символы:
<table>
<tr>
<th>Спецсимволы</th>
<th>Действие в строке замены</th>
</tr>
<tr>
<td>`$$`</td>
<td>Вставляет `"$"`.</td>
</tr>
<tr>
<td>`$&`</td>
<td>Вставляет всё найденное совпадение.</td>
</tr>
<tr>
<td><code>$&#096;</code></td>
<td>Вставляет часть строки до совпадения.</td>
</tr>
<tr>
<td>
<code>$'</code>
</td>
<td>Вставляет часть строки после совпадения.</td>
</tr>
<tr>
<td>
<code>$*n*</code>
</td>
<td>где `n` -- цифра или двузначное число, обозначает `n-ю` по счёту скобку, если считать слева-направо.</td>
</tr>
</table>
Пример использования скобок и `$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
```
Эта функция также получает аргументы:
<ol>
<li>`str` -- найденное совпадение,</li>
<li>`p1, p2, ..., pn` -- содержимое скобок</li>
<li>`offset` -- позиция, на которой найдено совпадение</li>
<li>`s` -- исходная строка</li>
</ol>
Если скобок в регулярном выражении нет, то у функции всегда будет ровно 3 аргумента.
Используем это, чтобы вывести полную информацию о совпадениях:
```js
//+ run
// вывести и заменить все совпадения
function replacer(str, offset, s) {
alert("Найдено: " + str + " на позиции: " + offset + " в строке: " + s);
return ":"
}
var result = "ОЙ-Ой-ой".replace( /ой/gi, replacer);
alert('Результат: ' + result);
```
Со скобочными выражениями:
```js
//+ run
function replacer(str, name, surname, offset, s) {
return surname +", " + name;
}
alert( str.replace( /(Василий) (Пупкин)/ , replacer) ) // Пупкин, Василий
```
Функция для замены -- это самое мощное средство, какое только может быть. Она владеет всей информацией о совпадении и имеет доступ к замыканию, поэтому может всё.
## Методы объектов RegExp
Регэкспы являются объектами класса [:RegExp].
У них есть два основных метода.
### regexp.test(str)
Проверяет, есть ли хоть одно совпадение в строке `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)
Этот метод -- самое мощное, что только есть для поиска с использованием регулярных выражений.
Он ведёт себя по-разному, в зависимости от того, есть ли у регэкспа флаг `g`.
<ul>
<li>Если флага `g` нет, то `regexp.exec(str)` ищет и возвращает первое совпадение, является полным аналогом вызова `str.match(regexp)`.</li>
<li>Если флаг `g` есть, то вызов `regexp.exec` возвращает первое совпадение и *запоминает* его позицию в свойстве `regexp.lastIndex`. Последующий поиск он начнёт уже с этой позиции. Если совпадений не найдено, то сбрасывает `regexp.lastIndex` в ноль.</li>
</ul>
Второй вариант запуска обычно используют в цикле:
```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]
## Итого, рецепты
Методы становятся гораздо понятнее, если разбить их использование по задачам, которые нужны в реальной жизни.
<ul>
<li>Для поиска только одного совпадения:
<dl>
<dt>Найти позицию первого совпадения</dt>
<dd>`str.search(regexp)`</dd>
<dt>Найти само совпадение</dt>
<dd>`str.match(regexp)`</dd>
<dt>Проверить, есть ли хоть одно совпадение</dt>
<dd>`regexp.test(str)`, также можно использовать `str.search(regexp) != -1`</dd>
<dt>Найти совпадение с нужной позиции</dt>
<dd>`regexp.exec(str)`, начальную позицию поиска задать в `regexp.lastIndex`</dd>
</dl>
</li>
<li>Для поиска всех совпадений:
<dl>
<dt>Найти массив совпадений</dt>
<dd>`str.match(regexp)`, с флагом `g`</dd>
<dt>Получить все совпадения, с подробной информацией о каждом</dt>
<dd>`regexp.exec(str)` с флагом `g`, в цикле</dd>
</dl>
</li>
<li>Дополнительно:
<dl>
<dt>Для поиска-и-замены</dt>
<dd>`str.replace(regexp, str|func)`</dd>
<dt>Для разбивки строки на части</dt>
<dd>`str.split(regexp)`</dd>
</dl>
</li>
</ul>
Далее мы перейдём к более подробному изучению синтаксиса регулярных выражений.