init
|
@ -0,0 +1,104 @@
|
|||
# Паттерны и флаги
|
||||
|
||||
Регулярные выражения –- мощное средство поиска и замены в строке.
|
||||
|
||||
В JavaScript регулярные выражения реализованы отдельным объектом `RegExp` и интегрированы в методы строк.
|
||||
[cut]
|
||||
|
||||
## Регэкспы
|
||||
|
||||
**Регулярное выражение (оно же "регэксп", "регулярка" или просто "рег"), состоит из *паттерна* (он же "шаблон") и необязательных *флагов*.**
|
||||
|
||||
Синтаксис создания регулярного выражения:
|
||||
|
||||
```js
|
||||
var regexp = new RegExp("шаблон", "флаги");
|
||||
```
|
||||
|
||||
Как правило, используют более короткую запись через слеши `/`:
|
||||
|
||||
```js
|
||||
var regexp = /шаблон/; // без флагов
|
||||
var regexp = /шаблон/gmi; // с флагами gmi (изучим их дальше)
|
||||
```
|
||||
|
||||
Слэши `'/'` говорят JavaScript о том, что это регулярное выражение. Они играют здесь ту же роль, что и кавычки для обозначения строк.
|
||||
|
||||
## Использование
|
||||
|
||||
Основа регулярного выражения -- паттерн. Это строка, которую можно расширить специальными символами, которые делают поиск намного мощнее.
|
||||
|
||||
Если флагов и специальных символов нет, то поиск по паттерну -- то же самое, что и обычный поиск подстроки:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "Я люблю JavaScript!"; // будем искать в этой строке
|
||||
|
||||
var regexp = /лю/;
|
||||
alert( str.search(regexp) ); // 2
|
||||
```
|
||||
|
||||
Сравните с обычным поиском:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "Я люблю JavaScript!";
|
||||
|
||||
var substr = "лю";
|
||||
alert( str.indexOf(substr) ); // 2
|
||||
```
|
||||
|
||||
Как видим, то же самое, разве что для регэкспа использован метод [:String#search(reg)], а для строки [:String#indexOf(substr)]. Но это соответствие лишь кажущееся. Очень скоро мы усложним регулярные выражения, и тогда появятся отличия.
|
||||
|
||||
[smart header="Цветовые обозначения"]
|
||||
Здесь и далее используется следующая цветовая схема:
|
||||
<ul>
|
||||
<li>регэксп (регулярное выражение) - <code class="pattern">красный</code></li>
|
||||
<li>строка - <code class="subject">синий</code></li>
|
||||
<li>результат - <code class="match">зеленый</code></li>
|
||||
</ul>
|
||||
[/smart]
|
||||
|
||||
## Флаги
|
||||
|
||||
Регулярные выражения могут иметь флаги, которые влияют на поиск.
|
||||
|
||||
В JavaScript их всего три:
|
||||
|
||||
<dl>
|
||||
<dt>`i`</dt>
|
||||
<dd>Если этот флаг есть, то регэксп ищет независимо от регистра, то есть не различает между `А` и `а`.</dd>
|
||||
<dt>`g`</dt>
|
||||
<dd>Если этот флаг есть, то регэксп ищет все совпадения, иначе -- только первое.</dd>
|
||||
<dt>`m`</dt>
|
||||
<dd>Многострочный режим.</dd>
|
||||
</dl>
|
||||
|
||||
Самый очевидный из этих флагов -- безусловно, `i`.
|
||||
|
||||
Пример его использования:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "Я люблю JavaScript!"; // будем искать в этой строке
|
||||
|
||||
alert( str.search( *!*/ЛЮ/*/!* ) ); // -1
|
||||
alert( str.search( *!*/ЛЮ/i*/!* ) ); // 2
|
||||
```
|
||||
|
||||
<ol>
|
||||
<li>С регом `/ЛЮ/` вызов вернул `-1`, что означает "не найдено" (то же соглашение, что и `indexOf`),</li>
|
||||
<li>С регом `/ЛЮ/i` вызов нашёл совпадение на позиции 2, так как стоит флаг `i`, а значит "лю" тоже подходит.</li>
|
||||
</ol>
|
||||
|
||||
Следующий, пожалуй, самый важный флаг -- это `g`.
|
||||
|
||||
Мы рассмотрим его в следующей секции, вместе с основными методами поиска по регулярным выражениям в JavaScript.
|
||||
|
||||
## Итого
|
||||
|
||||
<ul>
|
||||
<li>Регулярное выражение состоит из шаблона и необязательных флагов `g`, `i` и `m`.</li>
|
||||
<li>Поиск по регулярному выражению <code class="pattern">/регэксп/</code> без флагов и спец. символов, которые мы изучим далее -- это то же самое, что и обычный поиск подстроки в строке.</li>
|
||||
<li>Метод строки `str.search(regexp)` возвращает индекс, на котором найдено совпадение.</li>
|
||||
</ul>
|
|
@ -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>$`</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>
|
||||
|
||||
Далее мы перейдём к более подробному изучению синтаксиса регулярных выражений.
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
# Символьные классы
|
||||
|
||||
Рассмотрим задачу -- есть телефонный номер `"+7(903)-123-45-67"`, и нам нужно найти в этой строке цифры, а остальные символы нас не интересуют.
|
||||
|
||||
**Для поиска символов определённого вида, в регулярных выражениях предусмотрены "классы символов".**
|
||||
[cut]
|
||||
Класс символов -- это, в первую очередь, специальное обозначение.
|
||||
|
||||
Например, в данном случае нам нужен класс "произвольная цифра", он обозначается `\d`.
|
||||
|
||||
Это обозначение вставляется в паттерн наравне с остальными символами. При поиске под него подходит любая цифра.
|
||||
|
||||
Пример ниже ищет все цифры в строке:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "+7(903)-123-45-67";
|
||||
|
||||
var reg = /\d/g
|
||||
|
||||
alert( str.match(reg) ); // 7,9,0,3,1,2,3,4,5,6,7
|
||||
```
|
||||
|
||||
Есть и другие классы. Самые полезные:
|
||||
<dl>
|
||||
<dt>`\d` (от английского "digit" - "цифра")</dt>
|
||||
<dd>Цифра, символ от `0` до `9`.</dd>
|
||||
<dt>`\s` (от английского "space" - "пробел")</dt>
|
||||
<dd>Пробельный символ, включая табы, переводы строки и т.п.</dd>
|
||||
<dt>`\w` (от английского "word" -- "слово") </dt>
|
||||
<dd>Символ латинского алфавита или цифра или подчёркивание `'_'`</dd>
|
||||
</dl>
|
||||
|
||||
**Регулярное выражение обычно содержит одновременно и обычные символы и классы**:
|
||||
|
||||
Например, найдём строку `CSS` с любой цифровой версией:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "Стандарт CSS4 - наше будущее";
|
||||
var reg = /CSS\d/
|
||||
|
||||
alert( str.match(reg) );
|
||||
```
|
||||
|
||||
Несколько классов в одном регэкспе:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
showMatch( "Я люблю HTML5!", /\s\w\w\w\w\d/ ); // 'HTML5'
|
||||
```
|
||||
|
||||
Совпадение (каждому классу в регэкспе соответствует один символ результата):
|
||||
|
||||
<img src="love_html5.png">
|
||||
|
||||
**Также существуют обратные символьные классы:**
|
||||
|
||||
<dl>
|
||||
<dt>`\D`</dt>
|
||||
<dd>Не-цифра, любой символ кроме `\d`</dd>
|
||||
<dt>`\S`</dt>
|
||||
<dd>Не-пробел, любой символ кроме `\s`.</dd>
|
||||
<dt>`\W`</dt>
|
||||
<dd>Символ, не принадлежащий латиннице, а также не буква и не подчёркивание, алфавиту, т.е. любой кроме `\w`</dd>
|
||||
</dl>
|
||||
|
||||
Например, мы хотим получить из телефона <code class="subject">+7(903)-123-45-67</code> только цифры.
|
||||
|
||||
Есть два способа сделать это.
|
||||
|
||||
<ol>
|
||||
<li>Первый -- найти все цифры и объединить их:
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "+7(903)-123-45-67";
|
||||
|
||||
var digits = str.match( /\d/g ).join("");
|
||||
alert(digits); // 79031234567
|
||||
```
|
||||
|
||||
</li>
|
||||
<li>Второй -- найти все НЕцифры и удалить их из строки:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "+7(903)-123-45-67";
|
||||
|
||||
alert( str.replace(/\D/g, "") ); // 79031234567
|
||||
```
|
||||
|
||||
Второй способ короче, не правда ли?
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
**Регулярное выражение может также содержать стандартные спецсимволы строк, такие как `\n, \t` и другие.**
|
||||
|
||||
Они являются обычными символами. Отличить их от классов очень просто -- для классов зарезервированы другие буквы.
|
||||
|
||||
[warn header="Пробелы важны!"]
|
||||
Обычно мы не обращаем внимание на пробелы. Для нашего взгляда строки <code class="subject">1-5</code> и <code class="subject">1 - 5</code> почти идентичны.
|
||||
|
||||
Но в регулярных выражениях **пробел - такой же символ, как и другие**.
|
||||
|
||||
Поиск ниже не сработает, т.к. не учитывает пробелы вокруг дефиса:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "1 - 5".match (/\d-\d/) ); // null, нет совпадений!
|
||||
```
|
||||
|
||||
Поправим это, добавив в паттерн пробелы:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "1 - 5".match (/\d - \d/) ); // работает, пробелы вокруг дефиса
|
||||
```
|
||||
|
||||
В регулярные выражения также не надо вставлять лишние пробелы. Все они имеют значение:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "1-5".match( /\d - \d/ ) ); // null, так как в строке 1-5 нет пробелов
|
||||
```
|
||||
|
||||
[/warn]
|
||||
|
||||
Особым классом символов является точка `"."`.
|
||||
|
||||
**В регулярном выражении, точка <code class="pattern">"."</code> обозначает *любой символ*, кроме перевода строки**:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var re = /CS.4/;
|
||||
|
||||
alert( "Стандарт CSS4".match(re) ); // найдено "CSS4"
|
||||
alert( "Сталь CS-4".match(re) ); // найдено "CS-4"
|
||||
alert( "CS 4".match(re) ); // найдено "CS 4", пробел тоже символ
|
||||
```
|
||||
|
||||
Обратим внимание -- точка означает именно "произвольный символ".
|
||||
|
||||
То есть какой-то символ на этом месте в строке должен быть:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
|
||||
alert( "CS4".match (/CS.4/) ); // нет совпадений, так как для точки нет символа
|
||||
```
|
||||
|
After Width: | Height: | Size: 5.6 KiB |
|
@ -0,0 +1,46 @@
|
|||
# Экранирование специальных символов
|
||||
|
||||
В регулярных выражениях есть и другие символы, имеющие особый смысл.
|
||||
|
||||
Они используются, чтобы расширить возможности поиска.
|
||||
|
||||
Вот их полный список: <code class="pattern">[ \ ^ $ . | ? * + ( )</code>.
|
||||
|
||||
Не пытайтесь запомнить его -- когда мы разберёмся с каждым из них по отдельности, он запомнится сам собой.
|
||||
|
||||
**Чтобы использовать специальный символ в качестве обычного, он должен быть *экранирован*.**
|
||||
|
||||
Или, другими словами, перед символом должен быть обратный слэш `'\'`.
|
||||
|
||||
Например, нам нужно найти точку <code class="pattern">'.'</code>. В регулярном выражении она означает "любой символ, кроме новой строки", поэтому чтобы найти именно сам символ "точка" -- её нужно экранировать: <code class="pattern">\.</code>.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "Глава 5.1".match( /\d\.\d/ ) ); // 5.1
|
||||
```
|
||||
|
||||
Круглые скобки также являются специальными символами, так что для поиска именно скобки нужно использовать `\(`. Пример ниже ищет строку `"g()"`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "function g()".match( /g\(\)/ ) ); // "g()"
|
||||
```
|
||||
|
||||
**Слэш `'/'`, хотя и не является специальными символом, но открывает-закрывает регэксп в синтаксисе <code class="pattern">/...pattern.../</code>. Поэтому его тоже нужно экранировать: <code><code>'\/'</code></code>**.
|
||||
|
||||
Так выглядит поиск слэша `'/'`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "/".match( /\// ) ); // '/'
|
||||
```
|
||||
|
||||
Ну и, наконец, если нам нужно найти сам обратный слэш `\`, то его нужно просто задублировать.
|
||||
|
||||
Так выглядит поиск обратного слэша `"\"`:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "1\2".match( /\\/ ) ); // '\'
|
||||
```
|
||||
|
|
@ -0,0 +1,149 @@
|
|||
# Наборы и диапазоны [...]
|
||||
|
||||
Если в регулярном выражении нескольки символов или символьных классов заключены в квадратные скобки `[…]`, то это означает "искать любой символ из указанных в `[…]`".
|
||||
|
||||
Например, <code class="pattern">[еао]</code> означает любой символ из этих трёх: `'а'`, `'е'`, или `'о'`.
|
||||
[cut]
|
||||
Можно использовать квадратные скобки вместе с обычными символами.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
// найти [г или т], а затем "оп"
|
||||
alert( "Гоп-стоп".match( /[гт]оп/gi ) ); // "Гоп", "топ"
|
||||
```
|
||||
|
||||
Несмотря на то, что в квадратных скобках несколько символов, в совпадении должен присутствовать *ровно один* из них.
|
||||
|
||||
Поэтому в примере ниже нет результатов:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
// найти "В", затем [у или а], затем "ля"
|
||||
alert( "Вуаля".match( /В[уа]ля/ ) ); // совпадений нет
|
||||
```
|
||||
|
||||
Совпадение было бы, если бы после `"Ву"` шло сразу `"ля"`, например <code class="subject">Вуля</code> или если бы `"Ва"` сопровождалось `"ля"`, то есть <code class="subject">Валя</code>.
|
||||
|
||||
**Квадратные скобки могут также содержать *диапазоны символов*.**
|
||||
|
||||
Например, <code class="pattern">[a-z]</code> -- произвольный символ от `a` до `z`, <code class="pattern">[0-5]</code> -- цифра от `0` до `5`.
|
||||
|
||||
В примере ниже мы будем искать `"x"`, после которого идёт два раза `[0-9A-F]` -- цифра или буква от A до F:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
// найдёт "xAF"
|
||||
alert( "Exception 0xAF".match(/x[0-9A-F][0-9A-F]/g) );
|
||||
```
|
||||
|
||||
Обратим внимание, в слове <code class="subject">Exception</code> есть сочетание <code class="subject">xce</code>, но оно не подошло, потому что буквы в нём маленькие, а в диапазоне -- большие.
|
||||
|
||||
Если хочется искать и его тоже, можно добавить в скобки диапазон `a-f`: <code class="pattern">[0-9A-Fa-f]</code>. Или же просто указать у всего регулярного выражения флаг `i`.
|
||||
|
||||
**Символьные классы -- всего лишь более короткие записи для диапазонов, в частности:**
|
||||
|
||||
<ul>
|
||||
<li>**\d** -- то же самое, что <code class="pattern">[0-9]</code>,</li>
|
||||
<li>**\w** -- то же самое, что <code class="pattern">[a-zA-Z0-9_]</code>,</li>
|
||||
<li>**\s** -- то же самое, что <code class="pattern">[\t\n\v\f\r ]</code> плюс несколько юникодных пробельных символов.</li>
|
||||
</ul>
|
||||
|
||||
**В квадратных скобках можно использовать и диапазоны и символьные классы -- вместе.**
|
||||
|
||||
Например, нам нужно найти слова в тексте. Если оно на английском -- это достаточно просто:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "The sun is rising!";
|
||||
|
||||
alert( str.match( /\w+/g ) ); // The, sun, is, rising*!*
|
||||
```
|
||||
|
||||
А если есть слова и на русском?
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "Солнце встаёт!";
|
||||
|
||||
alert( str.match( /\w+/g ) ); // null*!*
|
||||
```
|
||||
|
||||
Ничего не найдено! Это можно понять, ведь <code class="pattern">\w</code> -- это именно английская букво-цифра, как можно видеть из аналога <code class="pattern">[a-zA-Z0-9_]</code>.
|
||||
|
||||
Чтобы находило слово на русском -- нужно использовать диапазон, например <code class="pattern">/[а-я]/</code>. А чтобы на обоих языках -- и то и другое вместе:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "Солнце (the sun) встаёт!";
|
||||
|
||||
alert( str.match( /[\wа-я]+/gi ) ); // Солнце, the, sun, вста, т*!*
|
||||
```
|
||||
|
||||
...Присмотритесь внимательно к предыдущему примеру! Вы видите странность? Оно не находит букву <code class="match">ё</code>, более того -- считает её разрывом в слове. Причина -- в кодировке юникод, она подробно раскрыта в главе [](/string). Буква `ё` лежит в стороне от основной кириллицы и её следует добавить в диапазон дополнительно, вот так:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "Солнце (the sun) встаёт!";
|
||||
|
||||
alert( str.match( /[\wа-яё]+/gi ) ); // Солнце, the, sun, встаёт*!*
|
||||
```
|
||||
|
||||
Теперь всё в порядке.
|
||||
|
||||
**Кроме обычных, существуют также *исключающие* диапазоны: <code class="pattern">[^…]</code>.**
|
||||
|
||||
Квадратные скобки, начинающиеся со знака каретки: <code class="pattern">[^…]</code> находят любой символ, *кроме указанных*.
|
||||
|
||||
Например:
|
||||
|
||||
<ul>
|
||||
<li><code class="pattern">[^аеуо]</code> -- любой символ, кроме `'a'`, `'e'`, `'y'`, `'o'`</li>
|
||||
<li><code class="pattern">[^0-9]</code> -- любая не-цифра, то же что `\D`</li>
|
||||
<li><code class="pattern">[^\s]</code> -- любой не-пробельный символ, как и `\S`</li>
|
||||
</ul>
|
||||
|
||||
Пример ниже ищет любые символы, кроме букв, цифр и пробелов:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "alice15@gmail.com".match( /[^\d\sA-Z]/gi ) ); // "@", "."
|
||||
```
|
||||
|
||||
**В квадратных скобках большинство специальных символов можно использовать без экранирования.**
|
||||
|
||||
Обычно, если мы хотим искать именно точку, а не любой символ, или именно `"\d"`, а не произвольную цифру, то мы используем экранирование: указываем `\.` или `\\d`.
|
||||
|
||||
Но квадратные скобки подразумевают поиск одного символа из нескольких или из диапазона. Использование некоторых специальных символов внутри них не имело бы смысла, и они воспринимаются как обычные символы.
|
||||
|
||||
Это касается символов:
|
||||
<ul>
|
||||
<li>точка <code class="pattern">'.'</code></li>
|
||||
<li>плюс <code class="pattern">'+'</code></li>
|
||||
<li>круглые скобки <code class="pattern">'( )'</code></li>
|
||||
<li>дефис <code class="pattern">'-'</code>, если он находится в начале или конце квадратных скобок, то есть не выделяет диапазон.</li>
|
||||
<li>символ каретки <code class="pattern">'^'</code>, если не находится в начале квадратных скобок, то есть кроме <code class="pattern">[\^..]</code>.</li>
|
||||
<li>а также открывающая квадратная скобка <code class="pattern">'['</code></li>
|
||||
</ul>
|
||||
|
||||
То есть, точка `"."` в квадратных скобках означает не "любой символ", а обычную точку.
|
||||
|
||||
Например, регэксп <code class="pattern">[-().^+]</code> ищет один из символов `-().^`. В данном контексте эти символы не являются специальными.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var re = /[-().^+]/g;
|
||||
|
||||
alert( "1 + 2 - 3".match(re) ); // найдёт +, -
|
||||
```
|
||||
|
||||
...Впрочем, даже если вы решите "на всякий случай" заэкранировать эти символы, поставив перед ними обратный слэш `\` -- вреда не будет:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var re = /[\-\(\)\.\^\+]/g;
|
||||
|
||||
alert( "1 + 2 - 3".match(re) ); // тоже работает: +, -
|
||||
```
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
Символ `.` является специальным, значит его надо экранировать.
|
||||
Регулярное выражение:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var reg = /\.{3,}/g;
|
||||
alert("Привет!... Как дела?.....".match(reg)); // ..., .....
|
||||
```
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
# Как найти многоточие... ?
|
||||
|
||||
[importance 5]
|
||||
|
||||
Напишите регулярное выражения для поиска многоточий: трёх или более точек подряд.
|
||||
|
||||
|
||||
Проверьте его:
|
||||
|
||||
```js
|
||||
var reg = /ваше выражение/g;
|
||||
alert("Привет!... Как дела?.....".match(reg)); // ..., .....
|
||||
```
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
# Цифровые квантификаторы {n}
|
||||
|
||||
Рассмотрим задачу -- взять телефон вида `+7(903)-123-45-67` и найти все числа в нём. То есть, нас интересует результат вида `7, 903, 123, 45, 67`.
|
||||
|
||||
Нечто похожее мы уже делали ранее -- мы искали цифры. Для этого было достаточно класса `\d`. Но здесь нужно искать *числа* -- последовательности из 1 или более цифр.
|
||||
|
||||
**Количество повторений символа можно указать с помощью числа в фигурных скобках: `{n}`.**
|
||||
|
||||
<dl>
|
||||
<dt>Точное количество: `{5}`</dt>
|
||||
<dd>Паттерн <code class="pattern">\d{5}</code> обозначает 5 цифр, как и <code class="pattern>\d\d\d\d\d</code>.
|
||||
|
||||
Следующий пример находит пятизначное число.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "Мне 12345 лет".match (/\d{5}/) ); // "12345"
|
||||
```
|
||||
|
||||
</dd>
|
||||
<dt>Количество от-до: `{3,5}`</dt>
|
||||
<dd>Для того, чтобы найти, например, числа размером от трёх до пяти знаков, нужно указать границы в фигурных скобках: `\d{3,5}`
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "Мне не 12, а 1234 года".match( /\d{3,5}/ ) ); // "1234"
|
||||
```
|
||||
|
||||
Последнее значение можно и не указывать. Тогда выражение `\d{3,}` найдет числа, длиной от трех знаков:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "Мне не 12, а 345678 лет".match( /\d{3,5}/ ) ); // "345678"
|
||||
```
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
В случае с телефоном нам нужны числа - одна или более цифр подряд. Этой задаче соответствует регулярное выражение <code class="pattern">\d{1,}</code>:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "+7(903)-123-45-67";
|
||||
|
||||
alert( str.match( /\d{1,}/g ) ); // 7,903,123,45,67
|
||||
```
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
# Квантификаторы +, * и ?
|
||||
|
||||
Для самые часто востребованных квантификаторов есть специальные короткие обозначения.
|
||||
|
||||
[cut]
|
||||
<dl>
|
||||
<dt>`+`</dt>
|
||||
<dd>Означает "один или более", то же что `{1,}`.
|
||||
|
||||
Например, <code class="pattern">\d+</code> находит числа -- последовательности из 1 или более цифр:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "+7(903)-123-45-67";
|
||||
|
||||
alert( str.match( /\d+/g ) ); // 7,903,123,45,67
|
||||
```
|
||||
|
||||
</dd>
|
||||
<dt>`?`</dt>
|
||||
<dd>Означает "ноль или один", то же что и `{0,1}`. По сути, делает символ необязательным.
|
||||
|
||||
Например, <code class="pattern">ou?r</code> найдёт <code class="match">or</code> в слове <code class="subject">color</code> и <code class="match">our</code> в его британском варианте написания <code class="subject">colour</code>.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "Можно писать color или colour";
|
||||
|
||||
alert( str.match( /colou?r/g ) ); // color, colour
|
||||
```
|
||||
|
||||
</dd>
|
||||
<dt>`*`</dt>
|
||||
<dd>Означает "ноль или более", то же что `{0,}`. То есть, символ может повторяться много раз или вообще отсутствовать.
|
||||
|
||||
Пример ниже находит цифру, после которой идёт один или более нулей:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "100 10 1".match( /\d0*/g ) ); // 100, 10, 1
|
||||
```
|
||||
|
||||
Сравните это с `'+'` (один или более):
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "100 10 1".match( /\d0+/g ) ); // 100, 10
|
||||
```
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
Эти квантификаторы -- одни из важнейших "строительных блоков" для сложных регулярных выражений, поэтому мы рассмотрим ещё примеры.
|
||||
|
||||
<dl>
|
||||
<dt>Десятичная дробь (число с точкой внутри): <code class="pattern">\d+\.\d+</code></dt>
|
||||
<dd>
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "0 1 12.345 7890".match( /\d+\.\d+/g ) ); // 123.45
|
||||
```
|
||||
|
||||
</dd>
|
||||
<dt>Открывающий HTML-тег без атрибутов, такой как `<span>` или `<p>`: <code class="pattern">/<[a-z]+>/i</code></dt>
|
||||
<dd>Пример:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "<BODY> ... </BODY>".match ( /<[a-z]+>/gi ) ); // <BODY>
|
||||
```
|
||||
|
||||
Это регулярное выражение ищет символ <code class="pattern">'<'</code>, за которым идут одна или более букв английского алфавита, и затем <code class="pattern">'>'</code>.
|
||||
</dd>
|
||||
<dt>Открывающий HTML-тег без атрибутов (лучше): <code class="pattern">/<[a-z][a-z0-9]*>/i</code></dt>
|
||||
<dd>
|
||||
Здесь регулярное выражение расширено: в соответствие со стандартом, HTML-тег может иметь символ на любой позиции, кроме первой, например `<h1>`.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "<h1>Привет!</h1>".match( /<[a-z][a-z0-9]*>/gi ) ); // <h1>
|
||||
```
|
||||
|
||||
</dd>
|
||||
<dt>Открывающий или закрывающий HTML-тег без атрибутов: <code class="pattern">/<\/?[a-z][a-z0-9]*>/i</code></dt>
|
||||
<dd>В предыдущий паттерн добавили необязательный слэш <code class="pattern">/?</code> перед тегом. Его понадобилось заэкранировать, чтобы JavaScript не принял его за конец шаблона.
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "<h1>Привет!</h1>".match( /<\/?[a-z][a-z0-9]*>/gi ) ); // <h1>, </h1>
|
||||
```
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
[smart header="Точнее -- значит сложнее"]
|
||||
Здесь мы видим классическую ситуацию, которая повторяется из раза в раз.
|
||||
|
||||
Чем точнее регулярное выражение, тем оно длиннее и сложнее.
|
||||
|
||||
Например, для HTML-тегов, скорее всего, подошло бы и более короткое регулярное выражение <code class="pattern"><\w+></code>.
|
||||
|
||||
Так как класс `\w` означает "любая цифра или английская буква или _`, то под такой шаблон подойдут и не теги, например <code class="match"><_></code>, однако он гораздо проще, чем <code class="pattern"><[a-z][a-z0-9]*></code>.
|
||||
|
||||
Подойдёт ли нам <code class="pattern"><\w+></code> или нужно использовать именно <code class="pattern"><[a-z][a-z0-9]*></code>?
|
||||
|
||||
В реальной жизни допустимы оба варианта. Ответ на подобные вопросы зависит от того, насколько реально важна точность и насколько сложно потом будет отфильтровать лишние совпадения (если появятся).
|
||||
[/smart]
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
# Жадный и ленивый режимы
|
||||
|
||||
Теперь время залезть "под капот" регулярных выражений и посмотреть, как происходит поиск.
|
||||
|
||||
Это понимание необходимо для того, чтобы искать что-либо сложнее чем <code class="pattern">/\d+/</code>.
|
||||
[cut]
|
||||
|
||||
Для примера рассмотрим задачу, которая часто возникает в типографике -- заменить кавычки вида `"..."` на "кавычки-лапки": `«...»`.
|
||||
|
||||
Шаблон, который мы будем использовать для поиска подстрок в кавычках, будет выглядеть так:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var reg = /".+"/g;
|
||||
|
||||
var str = 'a "witch" and her "broom" is one';
|
||||
|
||||
alert( str.match(reg) );
|
||||
```
|
||||
|
||||
Запустите этот пример...
|
||||
|
||||
Упс! Он не работает! Вместо того, чтобы найти два совпадения <code class="match">"witch"</code> и <code class="match">"broom"</code>, он находит одно: <code class="match">"witch" and her "broom"</code>.
|
||||
|
||||
Это как раз тот случай, когда *жадность* -- причина всех зол.
|
||||
|
||||
## Алгоритм поиска
|
||||
|
||||
Движок регулярных выражений пытается проверить строку на соответствие шаблону, начиная с самой первой, нулевой позиции. Если не получается, он идёт вперёд и пытается найти с 1й позиции и так далее.
|
||||
|
||||
Чтобы сделать происходящее максимально наглядным и понятным, проследим, что именно он делает для паттерна <code class="pattern">".+"</code>.
|
||||
|
||||
<ol>
|
||||
<li>Первый символ шаблона -- это кавычка <code class="pattern">"</code>.
|
||||
|
||||
Движок регулярных выражений пытается найти её на 0й позиции, но там совсем другой символ, поэтому на 0й позиции соответствия явно нет.
|
||||
|
||||
Далее он переходит 1ю, 2ю позицию в исходной строке и, наконец, обнаруживает кавычку на 3й позиции:
|
||||
<img src="witch_greedy1.png">
|
||||
</li>
|
||||
<li>Кавычка найдена, далее движок проверяет, есть ли соответствие для остальной части паттерна.
|
||||
|
||||
В данном случае следующий символ паттерна -- `.` (точка). Она обозначает "любой символ", так что следующая буква строки <code class="match">'w'</code> вполне подходит:
|
||||
<img src="witch_greedy2.png">
|
||||
</li>
|
||||
<li>Далее "любой символ" повторяется, так как стоит квантификатор <code class="pattern">.+</code>. Движок регулярных выражений берёт один символ за другим, до тех пор, пока у него это получается.
|
||||
|
||||
В данном случае это означает "до конца строки":
|
||||
<img src="witch_greedy3.png">
|
||||
</li>
|
||||
<li>Итак, текст закончился, движок регулярных выражений больше не может найти "любой символ", он закончил строить соответствие для <code class="pattern">.+</code> и очень рад по этому поводу.
|
||||
|
||||
Следующий символ шаблона -- это кавычка. Её тоже необходимо найти, чтобы соответствие было полным. А тут -- беда, ведь поисковый текст завершился!
|
||||
|
||||
Движок регулярных выражений понимает, что, наверное, взял многовато <code class="pattern">.+</code> и начинает отступать обратно ("фаза бэктрекинга" -- backtracking на англ.).
|
||||
|
||||
Иными словами, он сокращает текущее совпадение на один символ:
|
||||
|
||||
<img src="witch_greedy4.png">
|
||||
|
||||
После этого он ещё раз пытается подобрать соответствие для остатка паттерна. Но кавычка <code class="pattern">'"'</code> не совпадает с <code class="subject">'e'</code>.</li>
|
||||
<li>...Так что движок уменьшает число повторений <code class="pattern">.+</code> ещё раз:
|
||||
|
||||
<img src="witch_greedy5.png">
|
||||
|
||||
Кавычка <code class="pattern">'"'</code> не совпадает с <code class="subject">'n'</code>. Опять неудача.</li>
|
||||
<li>Движок продолжает отступать, он уменьшает количество повторений точки <code class="pattern">'.'</code> до тех пор, пока остаток паттерна не совпадёт:
|
||||
|
||||
<img src="witch_greedy6.png">
|
||||
</li>
|
||||
<li>Мы получили результат. Так как у регэкспа есть флаг `g`, то поиск продолжится, однако это произойдёт после первого совпадения и не даст новых результатов.</li>
|
||||
</ol>
|
||||
|
||||
**В жадном режиме (по умолчанию) регэксп повторяет квантификатор настолько много раз, насколько это возможно, чтобы найти соответствие.**
|
||||
|
||||
Возможно, это не совсем то, что мы хотели, но так это работает.
|
||||
|
||||
## Ленивый режим
|
||||
|
||||
Ленивый режим работы квантификаторов -- противоположность жадному, он означает "повторять минимальное количество раз".
|
||||
|
||||
Его можно включить, если поставить знак вопроса <code class="pattern">'?'</code> после квантификатора, так что он станет таким: <code class="pattern">*?</code> или <code class="pattern">+?</code> или даже <code class="pattern">??</code> для <code class="pattern">'?'</code>.
|
||||
|
||||
Чтобы не возникло путаницы -- важно понимать: обычно `?` сам является квантификатором (ноль или один). Но если он стоит *после другого квантификатора (или даже после себя)*, то обретает другой смысл -- в этом случае он меняет режим его работы на ленивый.
|
||||
|
||||
Регэксп <code class="pattern">/".+?"/g</code> работает, как задумано -- находит отдельно <code class="match">witch</code> и <code class="match">broom</code>:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var reg = /".+?"/g;
|
||||
|
||||
var str = 'a "witch" and her "broom" is one';
|
||||
|
||||
alert( str.match(reg) ); // witch, broom
|
||||
```
|
||||
|
||||
Чтобы в точности понять, что происходим, разберём в деталях, как ищется <code class="pattern">".+?"</code>.
|
||||
|
||||
<ol>
|
||||
<li>Первый шаг -- тот же, кавычка <code class="pattern">'"'</code> найдена на 3й позиции:
|
||||
<img src="witch_greedy1.png">
|
||||
</li>
|
||||
|
||||
<li>Второй шаг -- тот же, находим произвольный символ <code class="pattern">'.'</code>:
|
||||
<img src="witch_greedy2.png">
|
||||
</li>
|
||||
|
||||
<li>А вот дальше -- так как стоит ленивый режим работы `+`, то движок пытается повторять точку (произвольный символ) *минимальное количество раз*.
|
||||
|
||||
Так что он тут же пытается проверить, достаточно ли повторить 1 раз -- и для этого пытается найти соответствие остальной части шаблона, то есть <code class="pattern">'"'</code>:
|
||||
<img src="witch_lazy3.png">
|
||||
|
||||
Нет, один раз повторить недостаточно. В данном случае, символ `'i' != '"'`, но если бы оставшаяся часть паттерна была бы более сложной -- алгоритм остался бы тем же. Если остаток шаблона не находится -- увеличиваем количество повторений.
|
||||
</li>
|
||||
<li>Движок регулярных выражений увиличивает количество повторений точки на одно и пытается найти соответствие остатку шаблона ещё раз:
|
||||
|
||||
<img src="witch_lazy4.png">
|
||||
Опять неудача. Тогда поисковой движок увеличивает количество повторений ещё и ещё...
|
||||
</li>
|
||||
<li>Только на 5м шаге поисковой движок наконец находит соответствие для остатка паттерна:
|
||||
|
||||
<img src="witch_lazy5.png">
|
||||
</li>
|
||||
<li>Так как поиск происходит с флагом `g`, то он продолжается с конца текущего совпадения, давая ещё один результат:
|
||||
|
||||
<img src="witch_lazy6.png">
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
В примере выше продемонстрирована работа ленивого режима для <code class="pattern">+?</code>. Квантификаторы <code class="pattern">+?</code> и <code class="pattern">??</code> ведут себя аналогично -- "ленивый" движок увеличивает количество повторений только в том случае, если для остальной части шаблона на данной позиции нет соответствия, в то время как жадный сначала берёт столько повторений, сколько возможно, а потом отступает назад.
|
||||
|
||||
**Ленивость распространяется только на тот квантификатор, после которого стоит `?`.**
|
||||
|
||||
Прочие квантификаторы остаются жадными.
|
||||
|
||||
Например:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( "123 456".match ( /\d+ \d+?/g) ); // 123 4
|
||||
```
|
||||
|
||||
<ol>
|
||||
<li>Подпаттерн <code class="pattern">\d+</code> пытается найти столько символов, сколько возможно (работает жадно), так что он находит <code class="match">123</code> и останавливается, поскольку символ пробела <code class="pattern">' '</code> не подходит под <code class="pattern">\d</code>.</li>
|
||||
<li>Далее идёт пробел, и в игру вступает <code class="pattern">\d+?</code>.
|
||||
|
||||
Он находит один символ <code class="match">'4'</code> и пытатся проверить, есть ли совпадение с остатком шаблона (после <code class="pattern">\d+?</code>).
|
||||
|
||||
Здесь мы ещё раз заметим -- ленивый режим без необходимости ничего не возьмёт.
|
||||
|
||||
Так как шаблон закончился, то поиск завершается и <code class="match">123 4</code> становится результатом.</li>
|
||||
<li>Следующий поиск продолжится с `5`, но ничего не найдёт.</li>
|
||||
</ol>
|
||||
|
||||
[smart header="Конечные автоматы и не только"]
|
||||
Современные движки регулярных выражений могут иметь более хитрую реализацию внутренних алгоритмов, чтобы искать быстрее.
|
||||
|
||||
Однако, чтобы понять, как работает регулярное выражение, и строить регулярные выражения самому, знание этих хитрых алгоритмов ни к чему. Они служат лишь внутренней оптимизации способа поиска, описанного выше.
|
||||
|
||||
Кроме того, сложные регулярные выражения плохо поддаются всяким оптимизациям, так что поиск вполне может работать и в точности как здесь описано.
|
||||
[/smart]
|
||||
|
||||
## Альтернативный подход
|
||||
|
||||
В данном конкретном случае, возможно искать строки в кавычках, оставаясь в жадном режиме, с использованием регулярного выражения <code class="pattern">"[^"]+"</code>:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var reg = /"[^"]+"/g;
|
||||
|
||||
var str = 'a "witch" and her "broom" is one';
|
||||
|
||||
alert( str.match(reg) ); // witch, broom
|
||||
```
|
||||
|
||||
Регэксп <code class="pattern">"[^"]+"</code> даст правильные результаты, поскольку ищет кавычку <code class="pattern">'"'</code>, за которой идут столько не-кавычек (исключающие квадратные скобки), сколько возможно. Так что вторая кавычка автоматически прекращает повторения <code class="pattern">[^"]+</code> и позволяет найти остаток шаблона <code class="pattern">"</code>.
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,126 @@
|
|||
# Группы
|
||||
|
||||
Часть шаблона может быть заключена в скобки <code class="pattern">(...)</code>. Такие выделенные части шаблона называют "скобочными выражениями" или "скобочными группами".
|
||||
|
||||
У такого выделения есть два эффекта:
|
||||
<ol>
|
||||
<li>Он позволяет выделить часть совпадения в отдельный элемент массива при поиске через [:String#match] или [:RegExp#exec].</li>
|
||||
<li>Если поставить квантификатор после скобки, то он применится *ко всей скобке*, а не всего лишь к одному символу.</li>
|
||||
</ol>
|
||||
|
||||
[cut]
|
||||
В примере ниже, шаблон <code class="pattern">(go)+</code> находит один или более повторяющихся <code class="pattern">'go'</code>:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( 'Gogogo now!'.match( /(go)+/i ); // "Gogogo"
|
||||
```
|
||||
|
||||
Без скобок, шаблон <code class="pattern">/go+/</code> означал бы <code class="subject">g</code>, после которого идёт одна или более <code class="subject">o</code>, например: <code class="match">goooo</code>.
|
||||
|
||||
|
||||
**Скобки нумеруются слева направо. Поисковой движок запоминает содержимое каждой скобки и позволяет обращаться к нему, в том числе -- в шаблоне и строке замены.**
|
||||
|
||||
Например, найти HTML-тег можно шаблоном <code class="pattern"><.*?></code>. Скорее всего, после поиска мы захотим что-то сделать с результатом, и нас будет интересовать содержимое `<...>`.
|
||||
|
||||
Для удобства заключим его в скобки: <code class="pattern"><(.*?)></code>. Тогда содержимое скобок можно будет получить отдельно.
|
||||
|
||||
Используем метод [:String#match]. В результирующем массиве будет сначала всё совпадение, а далее -- скобочные группы, в данном случае -- только одна:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = '<h1>Привет, мир!</h1>'
|
||||
var reg = /<(.*?)>/
|
||||
|
||||
alert( str.match(reg) ) // массив: <h1>, h1
|
||||
```
|
||||
|
||||
Для поиска всех совпадений, как мы обсуждали ранее, используется метод [:RegExp#exec].
|
||||
|
||||
**Скобки могут быть и вложенными. В этом случае нумерация также идёт слева направо.**
|
||||
|
||||
Например, в строке <code class="subject"><span class="my"></code> нас может интересовать отдельно тег `span` и, для примера, его первая буква.
|
||||
|
||||
Добавим скобки в регулярное выражение:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = '<span class="my">';
|
||||
|
||||
reg = /<(([a-z])[a-z0-9]*).*?>/;
|
||||
|
||||
alert( str.match(reg) ); // <span class="my">, span, s
|
||||
```
|
||||
|
||||
Вот так выглядят скобочные группы:
|
||||
<img src="groups.png">
|
||||
|
||||
На нулевом месте -- всегда совпадение полностью, далее -- группы. Их вложенность означает всего лишь, что группа 1 содержит группу 2. Нумерация всегда идёт слева направо, по открывающей скобке.
|
||||
|
||||
**Даже если скобочная группа необязательна и не входит в совпадение, соответствующий элемент массива существует (и равен `undefined`).**
|
||||
|
||||
Например, рассмотрим регэксп <code class="pattern">a(z)?(c)?</code>. Он ищет `"a"`, за которой не обязательно идёт буква `"z"`, за которой необязательно идёт буква `"c"`.
|
||||
|
||||
Если напустить его на строку из одной буквы `"a"`, то результат будет таков:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
match = 'a'.match( /a(z)?(c)?/ )
|
||||
|
||||
alert(match.length); // 3
|
||||
alert(match[0]); // a
|
||||
alert(match[1]); // undefined
|
||||
alert(match[2]); // undefined
|
||||
```
|
||||
|
||||
Массив получился длины `3`, но все скобочные группы -- `undefined`.
|
||||
|
||||
А теперь более сложная ситуация, строка <code class="subject">ack</code>:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
match = 'ack'.match( /a(z)?(c)?/ )
|
||||
|
||||
alert(match.length); // 3
|
||||
alert(match[0]); // ac, всё совпадение
|
||||
alert(match[1]); // undefined, для (z)? ничего нет
|
||||
alert(match[2]); // c
|
||||
```
|
||||
|
||||
Длина массива результатов по-прежнему `3`. Она постоянна. А вот для скобочной группы <code class="pattern">(z)?</code> в ней ничего нет.
|
||||
|
||||
**Скобочную группу можно исключить из запоминаемых и нумеруемых, добавив в её начало <code class="pattern">?:</code>**
|
||||
|
||||
Бывает так, что скобки нужны, чтобы квантификатор правильно применился, а вот запоминать её в массиве не нужно. Тогда мы просто ставим сразу после открывающей скобки `?:`
|
||||
|
||||
В примере ниже есть скобочная группа <code class="pattern">(go-?)</code>, которая сама по себе не интересна, но входит в результаты:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "Go-go John!";
|
||||
*!*
|
||||
var reg = /(go-?)* (\w+)/i;
|
||||
*/!*
|
||||
|
||||
var result = str.match(reg);
|
||||
|
||||
alert( result[0] ); // Go-go John
|
||||
alert( result[1] ); // go
|
||||
alert( result[2] ); // John
|
||||
```
|
||||
|
||||
Исключим её из запоминаемых:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = "Go-go John!";
|
||||
*!*
|
||||
var reg = /(?:go-?)* (\w+)/i;
|
||||
*/!*
|
||||
|
||||
var result = str.match(reg);
|
||||
|
||||
alert( result[0] ); // Go-go John
|
||||
alert( result[1] ); // John
|
||||
```
|
||||
|
BIN
03-more/08-regular-expressions-javascript/09-regexp-groups/groups.png
Executable file
After Width: | Height: | Size: 6.9 KiB |
|
@ -0,0 +1,52 @@
|
|||
# Обратные ссылки \\n и $n
|
||||
|
||||
На скобочные группы можно ссылаться как в самом паттерне, так и в строке замены.
|
||||
[cut]
|
||||
|
||||
Ссылки в строке замены мы уже видели: они имеют вид `$n`, где `n` -- это номер скобочной группы. Вместо `$n` подставляется содержимое соответствующей скобки:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var name = "Александр Пушкин";
|
||||
|
||||
name = name.replace(/([а-яё]+) ([а-яё]+)/i, "$2, $1");
|
||||
alert(name); // Пушкин, Александр
|
||||
```
|
||||
|
||||
К скобочной группе можно также обратиться в самом шаблоне.
|
||||
|
||||
Рассмотрим это в реальном примере -- необходимо найти строку в кавычках. Эта строка может быть в одинарных кавычках <code class="subject">'...'</code> или в двойных <code class="subject">"..."</code> -- не важно, в каких именно, но открывающая и закрывающая кавычки должны быть одинаковыми.
|
||||
|
||||
Как такие строки искать? Регэксп <code class="pattern">`['"](.*?)['"]`</code> позволяет использовать разные кавычки, но он даст неверный ответ в случае, если одна кавычка ненароком оказалась внутри другой, как например в строке <code class="subject">"She's the one"</code>:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
str = "He said:\"She's the one\"."
|
||||
|
||||
reg = /['"](.*?)['"]/g
|
||||
|
||||
alert( str.match(reg) ) // "She'
|
||||
```
|
||||
|
||||
Как видно, регэксп нашёл открывающую кавычку <code class="match">"</code>, затем текст, вплоть до новой кавычки <code class="match">'</code>, которая закрывает соответствие.
|
||||
|
||||
Для того, чтобы попросить регэксп искать закрывающую кавычку -- такую же, как открывающую, мы обернём её в скобочную группу и используем обратную ссылку на неё:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
str = "He said:\"She's the one\"."
|
||||
|
||||
reg = /(['"])(.*?)\1/g
|
||||
|
||||
alert( str.match(reg) ) // "She's the one"
|
||||
```
|
||||
|
||||
Теперь работает верно!
|
||||
|
||||
Обратим внимание на два нюанса:
|
||||
|
||||
<ul>
|
||||
<li>В строке замены ссылка на первую скобочную группу выглядит как `$1`, а в шаблоне нужно использовать `\1`.</li>
|
||||
<li>Чтобы обращаться к скобочной группе -- не важно откуда, она не должна быть исключена из запоминаемых при помощи `?:`, то есть `(?:['"])` не подошло бы.</li>
|
||||
</ul>
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
# Чёрная дыра бэктрекинга
|
||||
|
||||
Некоторые регулярные выражения, с виду являясь простыми, могут выполняться оооочень долго, и даже подвешивать браузер.
|
||||
|
||||
[cut]
|
||||
Например, попробуйте пример ниже в Chrome или IE (осторожно, подвесит браузер!):
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( '123456789012345678901234567890z'.match(/(\d+)*$/) );
|
||||
```
|
||||
|
||||
Некоторые движки регулярных выражений (Firefox) справляются с таким регэкспом, а некоторые (IE, Chrome) -- нет.
|
||||
|
||||
В чём же дело, что не так с регэкспом?
|
||||
|
||||
Да с регэкспом-то всё так, синтаксис вполне допустимый. Проблема в том, как выполняется поиск по нему.
|
||||
|
||||
Для краткости рассмотрим более короткую строку: <code class="subject">1234567890z</code>:
|
||||
|
||||
<ol>
|
||||
<li>Первым делом, движок регэкспов пытается найти <code class="pattern">\d+</code>. Плюс <code class="pattern">+</code> является жадным по умолчанию, так что он хватает все цифры, какие может.
|
||||
|
||||
Затем движок пытается применить звёздочку вокруг скобок <code class="pattern">(\d+)*</code>, но больше цифр нет, так что звёздочка не даёт повторений.
|
||||
|
||||
После этого в паттерне остаётся <code class="pattern">$</code>, а в тексте -- символ <code class="subject">z</code>.
|
||||
|
||||
<img src="bad_backtrack_greedy1.png">
|
||||
|
||||
Так как соответствия нет, то жадный плюс <code class="pattern">+</code> отступает на один символ (бэктрекинг, зелёная стрелка на рисунке выше).
|
||||
</li>
|
||||
<li>После бэктрекинга, <code class="pattern">\d+</code> содержит всё число, кроме последней цифры. Затем движок снова пытается найти совпадение, уже с новой позиции (`9`).
|
||||
|
||||
Звёздочка <code class="pattern">(\d+)*</code> теперь может быть применена -- она даёт ещё одно число <code class="match">9</code>:
|
||||
|
||||
<img src="bad_backtrack_greedy11.png">
|
||||
|
||||
Движок пытается найти `$`, но это ему не удаётся -- на его пути опять `z`:
|
||||
|
||||
<img src="bad_backtrack_greedy2.png">
|
||||
|
||||
Так как совпадения нет, то поисковой движок отступает назад ещё раз.
|
||||
</li>
|
||||
<li>Теперь первое число <code class="pattern">\d+</code> будет содержать 8 цифр, а остаток строки <code class="subject">90</code> становится вторым <code class="pattern">\d+</code>:
|
||||
|
||||
<img src="bad_backtrack_greedy3.png">
|
||||
|
||||
Увы, всё ещё нет соответствия для <code class="pattern">$</code>.
|
||||
|
||||
Поисковой движок снова должен отступить назад. При этом последний жадный квантификатор отпускает символ. В данном случае это означает, что укорачивается второй <code class="pattern">\d+</code>, до одного символа <code class="subject">9</code>.
|
||||
</li>
|
||||
<li>Теперь движок регулярных выражений снова может применить звёздочку и находит третье число <code class="pattern">\d+</code>:
|
||||
|
||||
<img src="bad_backtrack_greedy4.png">
|
||||
|
||||
...И снова неудача. Второе и третье <code class="pattern">\d+</code> отступили по-максимуму, так что сокращается снова первое число.
|
||||
</li>
|
||||
<li>Теперь есть 7 цифр в первом <code class="pattern">\d+</code>. Поисковой движок видит место для второго <code class="pattern">\d+</code>, теперь уже с позиции 8:
|
||||
|
||||
<img src="bad_backtrack_greedy5.png">
|
||||
|
||||
Так как совпадения нет, второй <code class="pattern">\d+</code> отступает назад....
|
||||
</li>
|
||||
<li>...И так далее, легко видеть, что поисковой движок будет перебирать *все возможные комбинации* <code class="pattern">\d+</code> в числе. А их много.</li>
|
||||
</ol>
|
||||
|
||||
На этом месте умный читатель может воскликнуть: "Бэктрекинг? Давайте включим ленивый режим -- и не будет никакого бэктрекинга!"
|
||||
|
||||
Что ж, заменим <code class="pattern">\d+</code> на <code class="pattern">\d+?</code> и посмотрим (аккуратно, может подвесить браузер):
|
||||
|
||||
```js
|
||||
//+ run
|
||||
alert( '123456789012345678901234567890z'.match(/(\d+?)*$/) );
|
||||
```
|
||||
|
||||
Не помогло!
|
||||
|
||||
**Ленивые регулярные выражения делают то же самое, но в обратном порядке.**
|
||||
|
||||
Просто подумайте о том, как будет в этом случае работать поисковой движок.
|
||||
|
||||
Некоторые движки регулярных выражений, например Firefox, содержат хитрые проверки, в дополнение к алгоритму выше, которые позволяют избежать бесконечного перебора или кардинально ускорить его, но все движки и не всегда.
|
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 6.3 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 6.4 KiB |
|
@ -0,0 +1,26 @@
|
|||
# Альтернация (или) |
|
||||
|
||||
Альтернация -- термин в регулярных выражениях, которому в русском языке соответствует слово "ИЛИ". Она обозначается символом вертикальной черты <code class="pattern">|</code> и позволяет выбирать между вариантами.
|
||||
|
||||
[cut]
|
||||
|
||||
Например, нам нужно найти языки программирования: HTML, PHP, Java и JavaScript.
|
||||
|
||||
Соответствующее регулярное выражение: <code class="pattern">/html|php/java(script)?/</code>:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var reg = /html|php|css|java(script)?/gi
|
||||
|
||||
var str = "Сначала появился HTML, затем CSS, потом JavaScript"
|
||||
|
||||
alert( str.match(reg) ) // 'HTML', 'CSS', 'JavaScript'
|
||||
```
|
||||
|
||||
**Альтернация имеет очень низкий приоритет.**
|
||||
|
||||
Чтобы регэксп находил одновременно <code class="match">gr<b>a</b>y</code> и <code class="match">gr<b>e</b>y</code>, можно использовать <code class="pattern">gr(a|e)y</code> или <code class="pattern">gr[ae]y</code>, но не <code class="pattern">gra|ey</code>. Последний регэксп применит альтернацию к подвыражениям: <code class="pattern">gra</code> (ИЛИ) <code class="pattern">ey</code>.
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
# Начало ^ и конец $ строки, многострочный режим
|
||||
|
||||
Знак каретки <code class="pattern">'^'</code> и доллара <code class="pattern">'$'</code> имеют в регулярном выражении особый смысл. Их называют "якорями" (anchor - англ.).
|
||||
[cut]
|
||||
|
||||
Каретка <code class="pattern">^</code> совпадает в начале текста, а доллар <code class="pattern">$</code> -- в конце.
|
||||
|
||||
**Якоря являются не символами, а проверками. Они совпадают на "позиции".**
|
||||
|
||||
Это очень важное отличие по сравнению с символьными классами. Если движок регулярных выражений видит <code class="pattern">^</code> -- он *проверяет* начало текста, <code class="pattern">$</code> -- проверяет конец текста, при этом *никаких символов к совпадению не добавляется*.
|
||||
|
||||
Каретку <code class="pattern">^</code> обычно используют, чтобы указать, что регулярное выражение необходимо проверить именно с начала текста.
|
||||
|
||||
Например, без каретки:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = '100500 попугаев съели 500100 бананов!';
|
||||
alert( str.match( /\d+/ig ) // 100500, 500100 (все числа)
|
||||
```
|
||||
|
||||
А с кареткой:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = '100500 попугаев съели 500100 бананов!';
|
||||
alert( str.match( /^\d+/ig ) // 100500 (только в начале строки)*!*
|
||||
```
|
||||
|
||||
Знак доллара <code class="pattern">$</code> используют, чтобы указать, что паттерн должен заканчиваться в конце текста.
|
||||
|
||||
Тот же пример с долларом:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = '100500 попугаев съели 500100 бананов!';
|
||||
alert( str.match( /\d+$/ig ) // null (в начале строки чисел нет)*!*
|
||||
```
|
||||
|
||||
Якоря используют одновременно, чтобы указать, что паттерн должен охватывать текст с начала и до конца. Обычно это требуется при валидации.
|
||||
|
||||
Например, мы хотим проверить, что в переменной `num` хранится именно десятичная дробь.
|
||||
|
||||
Ей соответствует регэксп <code class="pattern">\d+\.\d+</code>. Но простая проверка найдёт дробь в любом тексте:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var num = "ля-ля 12.34";
|
||||
alert( num.match( /\d+\.\d+/ig) ); // 12.34
|
||||
```
|
||||
|
||||
Если мы хотим проверить, что `num` *целиком* соответствует паттерну <code class="pattern">\d+\.\d+</code>, то укажем якоря по обе стороны от него:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var num = "ля-ля 12.34";
|
||||
alert( num.match( /^\d+\.\d+$/ig) ); // null, не дробь
|
||||
|
||||
var num = "12.34";
|
||||
alert( num.match( /^\d+\.\d+$/ig) ); // 12.34, дробь!
|
||||
```
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
# Многострочный режим, флаг "m"
|
||||
|
||||
Многострочный режим включается, если у регэкспа есть флаг <code class="pattern">/m</code>.
|
||||
[cut]
|
||||
|
||||
В этом случае изменяется поведение <code class="pattern">^</code> и <code class="pattern">$</code>.
|
||||
|
||||
**В многострочном режиме якоря означают не только начало/конец текста, но и начало/конец строки.**
|
||||
|
||||
В примере ниже текст состоит из нескольких строк. Паттерн <code class="pattern">/^\d+/gm</code> берёт число с начала каждой строки:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = '1е место: Винни-пух\n' +
|
||||
'2е место: Пятачок\n' +
|
||||
'33е место: Слонопотам';
|
||||
|
||||
alert( str.match(/^\d+/gm ) ); // 1, 2, 33*!*
|
||||
```
|
||||
|
||||
Обратим внимание -- без флага <code class="pattern">/m</code> было бы только первое число:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
var str = '1е место: Винни-пух\n' +
|
||||
'2е место: Пятачок\n' +
|
||||
'33е место: Слонопотам';
|
||||
|
||||
alert( str.match(/^\d+/g ) ); // 1
|
||||
```
|
||||
|
||||
Это потому что в обычном режиме каретка <code class="pattern">^</code> -- это только начало текста.
|
||||
|
||||
Символ доллара <code class="pattern">$</code> ведёт себя точно так же.
|
||||
|
||||
Следующий пример находит последнее слово в строке:
|
||||
|
||||
TODO: указать на коренное отличие $ от \n: доллар не матчит символ, а \n матчит!!!!
|
||||
|
||||
```js
|
||||
//+ run
|
||||
showMatch(
|
||||
'1st: *!*John*!*\n' +
|
||||
'2nd: *!*Mary*/!*\n' +
|
||||
'33rd: *!*Peter*/!*', /\w+$/gm ) // John, Mary, Peter
|
||||
```
|
||||
|
||||
Please note that <code class="pattern">$</code> as well as <code class="pattern">^</code> doesn't add <code class="match">\n</code> to the match. They only check that the position is right.
|
||||
|
||||
|
||||
[summary]
|
||||
<ul>
|
||||
<li>The caret <code class="pattern">'^'</code> matches the position at the the text start. *In multiline mode* it also matches after the newline symbol.</li>
|
||||
<li>The dollar <code class="pattern">'$'</code> matches the position at the text end. *In multiline mode* it also matches before the newline symbol.</li>
|
||||
</ul>
|
||||
|
||||
**For both anchors, the regexp engine only checks the position, and doesn't match a character.**
|
||||
[/summary]
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
# Word boundary
|
||||
|
||||
Another position check is a *word boundary* <code class="pattern">\b</code>. It doesn't match a character, but matches in situations when a wordly character follows a non-wordly or vice versa. A "non-wordly" may also be text start or end.
|
||||
[cut]
|
||||
For example, <code class=pattern">\bdog\b</code> matches a standalone <code class="subject">dog</code>, not <code class="subject">doggy</code> or <code class="subject">catdog</code>:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
showMatch( "doggy catdog dog", /\bdog\b/ ) // "dog"
|
||||
```
|
||||
|
||||
Here, <code class="match">dog</code> matches, because the previous char is a space (non-wordly), and the next position is text end.
|
||||
|
||||
Normally, <code class="pattern">\w{4}</code> matches 4 consequent word characters.
|
||||
If the word is long enough, it may match multiple times:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
showMatch( "Boombaroom", /\w{4}/g) // 'Boom', 'baro'
|
||||
```
|
||||
|
||||
Appending <code class="pattern">\b</code> causes <code class="pattern">\w{4}\b</code> to match only at word end:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
showMatch( "Because life is awesome", /\w{4}\b/g) // 'ause', 'life', 'some'
|
||||
```
|
||||
|
||||
**The word boundary <code class="pattern">\b</code> like <code class="pattern">^</code> and <code class="pattern">$</code> doesn't match a char. It only performs the check.**
|
||||
|
||||
Let's add the check from another side, <code class="pattern">\b\w{4}\b</code>:
|
||||
|
||||
```js
|
||||
//+ run
|
||||
showMatch( "Because life is awesome", /\b\w{4}\b/g) // 'life'
|
||||
```
|
||||
|
||||
Now there is only one result <code class="match">life</code>.
|
||||
|
||||
<ol>
|
||||
<li>The regexp engine matches first word boundary <code class="pattern">\b</code> at zero position:
|
||||
<img src="boundary1.png">
|
||||
</li>
|
||||
<li>Then it successfully matches <code class="pattern">\w{4}</code>, but fails to match finishing <code class="pattern">\b</code>.
|
||||
|
||||
<img src="boundary2.png">
|
||||
|
||||
So, the match at position zero fails.
|
||||
</li>
|
||||
<li>The search continues from position 1, and the closest <code class="pattern">\b</code> is right after <code class="subject">Because</code> (position 9):
|
||||
|
||||
<img src="boundary3.png">
|
||||
|
||||
Now <code class="pattern">\w{4}</code> doesn't match, because the next character is a space.
|
||||
</li>
|
||||
<li>The search continues, and the closest <code class="pattern">\b</code> is right before <code class="subject">life</code> at position 11.
|
||||
|
||||
<img src="boundary4.png">
|
||||
|
||||
Finally, <code class="pattern">\w{4}</code> matches and the position check <code class="pattern">\b</code> after it is positive. We've got the result.
|
||||
</li>
|
||||
<li>The search continues after the match, but doesn't yield new results.</li>
|
||||
</ol>
|
||||
|
||||
**The word boundary check <code class="pattern">/\b/</code> works only for words in latin alphabet,** because it is based on <code class="pattern">\w</code> as "wordly" chars. Sometimes that's acceptable, but limits the application range of the feature.
|
||||
|
||||
And, for completeness..
|
||||
**There is also an inverse check <code class="pattern">\B</code>, meaning a position other than <code class="pattern">\b</code>.** It is extremely rarely used.
|
||||
|
BIN
03-more/08-regular-expressions-javascript/15-regexp-word-boundary/boundary1.png
Executable file
After Width: | Height: | Size: 4.6 KiB |
BIN
03-more/08-regular-expressions-javascript/15-regexp-word-boundary/boundary2.png
Executable file
After Width: | Height: | Size: 6.3 KiB |
BIN
03-more/08-regular-expressions-javascript/15-regexp-word-boundary/boundary3.png
Executable file
After Width: | Height: | Size: 6.2 KiB |
BIN
03-more/08-regular-expressions-javascript/15-regexp-word-boundary/boundary4.png
Executable file
After Width: | Height: | Size: 6.3 KiB |
|
@ -0,0 +1,7 @@
|
|||
# Practice
|
||||
|
||||
Here you found tasks which help in understanding regexp construction principles.
|
||||
[cut]
|
||||
|
||||
|
||||
The tutorial is not finished. Till now, we have it up to this part...
|
|
@ -0,0 +1,5 @@
|
|||
# Задачи-сироты по регекспам
|
||||
|
||||
|
||||
|
||||
|
9
03-more/08-regular-expressions-javascript/index.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
# Регулярные выражения [в работе]
|
||||
|
||||
Регулярные выражения -- мощный способ поиска и замены строк, который используется в самых разных языках, включая, конечно, JavaScript.
|
||||
|
||||
У меня здесь для вас две новости.
|
||||
<ol>
|
||||
<li>Первая плохая -- в JavaScript они на редкость убогие, хуже большинства существующих языков.</li>
|
||||
<li>Вторая хорошая -- зато нам будет проще их изучить.</li>
|
||||
</ol>
|