Skip to content

Commit 84fcae4

Browse files
committed
regexp
1 parent 508521d commit 84fcae4

3 files changed

Lines changed: 60 additions & 21 deletions

File tree

1-js/09-classes/02-class-inheritance/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ Here's the diagram of what happens:
520520
521521
```js run
522522
let animal = {
523-
eat: function() { // должен быть короткий синтаксис: eat() {...}
523+
eat: function() { // намеренно не пишем так, а не eat() { ...
524524
// ...
525525
}
526526
};

9-regular-expressions/11-regexp-groups/article.md

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,15 +200,16 @@ let results = '<h1> <h2>'.matchAll(/<(.*?)>/gi);
200200

201201
// results - не массив, а перебираемый объект
202202
alert(results); // [object RegExp String Iterator]
203-
alert(results[0]); // undefined
203+
204+
alert(results[0]); // undefined (*)
204205

205206
results = Array.from(results); // превращаем в массив
206207

207208
alert(results[0]); // <h1>,h1 (первый тег)
208209
alert(results[1]); // <h2>,h2 (второй тег)
209210
```
210211

211-
Как видите, первое отличие - очень важное. Мы не можем получить совпадение как `results[0]`, так как этот объект не является псевдомассивом. Его можно превратить в настоящий массив при помощи `Array.from`. Более подробно о псевдомассивах и перебираемых объектов мы говорили в главе <info:iterable>.
212+
Как видите, первое отличие - очень важное, это демонстрирует строка `(*)`. Мы не можем получить совпадение как `results[0]`, так как этот объект не является псевдомассивом. Его можно превратить в настоящий массив при помощи `Array.from`. Более подробно о псевдомассивах и перебираемых объектов мы говорили в главе <info:iterable>.
212213

213214
В явном преобразовании через `Array.from` нет необходимости, если мы перебираем результаты в цикле, вот так:
214215

@@ -228,6 +229,19 @@ for(let result of results) {
228229
let [tag1, tag2] = '<h1> <h2>'.matchAll(/<(.*?)>/gi);
229230
```
230231

232+
Каждое совпадение, возвращаемое `matchAll`, имеет тот же вид, что и при `match` без флага `pattern:g`: это массив с дополнительными свойствами `index` (позиция совпадения) и `input` (исходный текст):
233+
234+
```js run
235+
let results = '<h1> <h2>'.matchAll(/<(.*?)>/gi);
236+
237+
let [tag1, tag2] = results;
238+
239+
alert( tag1[0] ); // <h1>
240+
alert( tag1[1] ); // h1
241+
alert( tag1.index ); // 0
242+
alert( tag1.input ); // <h1> <h2>
243+
```
244+
231245
```smart header="Почему результат `matchAll` - перебираемый объект, а не обычный массив?"
232246
Зачем так сделано? Причина проста - для оптимизации.
233247

9-regular-expressions/16-regexp-sticky/article.md

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,76 @@
11
# Поиск на заданной позиции, флаг "y"
22

3-
Флаг `pattern:y` позволяет произвести поиск, начиная с определённой позиции в исходной строке.
3+
Флаг `pattern:y` позволяет произвести поиск на определённой позиции в исходной строке.
44

55
Чтобы разобрать флаг `pattern:y` и понять, чем же он хорош, рассмотрим практический пример.
66

7-
Одна из часто встречающихся задач регулярных выражений - лексический разбор: мы имеем текст на каком-то языке программирования и с помощью регулярных выражений (или иным способом) получаем его структурные элементы.
7+
Одна из часто встречающихся задач регулярных выражений - лексический разбор: мы имеем текст, например, на каком-то языке программирования и получаем его структурные элементы.
88

9-
Например, в браузеры встроен лексический анализатор HTML, а также для языка JavaScript.
9+
Например, в HTML есть теги и атрибуты, в JavaScript-коде - переменные и функции, и т.п.
1010

1111
Мы не будем погружаться глубоко в тему написания таких анализаторов (это специализированная область со своим набором инструментов и алгоритмов). Но в процессе их работы, вообще, в процессе анализа текста, очень часто возникает задача "прочитать что-то на заданной позиции".
1212

1313
Например, у нас есть строка кода `subject:let varName = "value"`, и нам надо прочитать из неё имя переменной, которое начинается с позиции `4`.
1414

1515
Имя переменной будем искать как слово `pattern:\w+`. Вообще, в языке JavaScript для имени переменной нужно чуть более сложное регулярное выражение, но здесь это не важно.
1616

17-
Обычный поиск, такой как `str.match` найдёт только первое слово в строке или все слова (с флагом `pattern:g`), а нам нужно одно слово именно на позиции `4`.
17+
Вызов `str.match(/\w+/)` найдёт только первое слово в строке или все слова (с флагом `pattern:g`), а нам нужно одно слово именно на позиции `4`.
1818

19-
Для использования флага `pattern:y` понадобится метод `regexp.exec(str)`.
19+
Для поиска, начиная с нужной позиции, можно использовать метод `regexp.exec(str)`.
2020

21-
Если у регулярного выражения нет флагов `pattern:g` или `pattern:y`, то он ищет первое совпадение с `regexp` в строке `str`, аналогично `str.match(regexp)`. Здесь нас вариант без флагов не интересует.
21+
Если у регулярного выражения `regexp` нет флагов `pattern:g` или `pattern:y`, то этот метод ищет первое совпадение в строке `str`, точно так же, как `str.match(regexp)`. Здесь нас этот простейший вариант без флагов не интересует.
2222

2323
Если флаг `pattern:g` есть, то он осуществляет поиск в строке `str`, начиная с позиции, заданной свойством `regexp.lastIndex`. И, когда находит, обновляет `regexp.lastIndex` на позицию после совпадения.
2424

2525
При создании регулярного выражения его свойство `lastIndex` равно `0`.
2626

27-
Например (пока без флага `y`):
27+
Так что повторные вызовы `regexp.exec` возвращают совпадения по очереди, одно за другим.
28+
29+
Например (с флагом `pattern:g`):
2830

2931
```js run
30-
let str = 'let varName = "value"';
32+
let str = 'let varName';
3133

3234
let regexp = /\w+/g;
3335
alert(regexp.lastIndex); // 0 (при создании lastIndex=0)
3436

3537
let word1 = regexp.exec(str);
36-
alert(word1); // let (первое слово)
38+
alert(word1[0]); // let (первое слово)
3739
alert(regexp.lastIndex); // 3 (позиция за первым совпадением)
3840

3941
let word2 = regexp.exec(str);
40-
alert(word2); // varName (второе слово)
42+
alert(word2[0]); // varName (второе слово)
4143
alert(regexp.lastIndex); // 11 (позиция за вторым совпадением)
44+
45+
let word3 = regexp.exec(str);
46+
alert(word3); // null (больше совпадений нет)
47+
alert(regexp.lastIndex); // 0 (сбрасывается по окончании поиска)
4248
```
4349

50+
Заметим, что каждое совпадение возвращается в виде массива, со всеми скобочными группами и дополнительными свойствами.
51+
52+
Можно перебрать все совпадения в цикле:
53+
54+
```js run
55+
let str = 'let varName';
56+
let regexp = /\w+/g;
57+
58+
let result;
59+
60+
while (result = regexp.exec(str)) {
61+
alert( `Найдено ${result[0]} на позиции ${result.index}` );
62+
// Найдено let на позиции 0, затем
63+
// Найдено varName на позиции 4
64+
}
65+
```
66+
67+
Такое использование `regexp.exec` представляет собой альтернативу методу `str.matchAll`.
68+
4469
Таким образом, последовательные вызовы `regexp.exec` могут найти все совпадения, представляя собой альтернативу методам `str.match/matchAll`.
4570

46-
Но, в отличие от этих методов, мы можем поставить самостоятельно `lastIndex`, начав тем самым поиск именно с нужной позиции.
71+
Но, в отличие от других методов, мы можем поставить самостоятельно `lastIndex`, начав тем самым поиск именно с нужной позиции.
4772

48-
Например, найдём слово на позиции `4`:
73+
Например, найдём слово, начиная с позиции `4`:
4974

5075
```js run
5176
let str = 'let varName = "value"';
@@ -68,12 +93,14 @@ alert(word); // varName
6893
let str = 'let varName = "value"';
6994

7095
let regexp = /\w+/g;
96+
7197
*!*
7298
regexp.lastIndex = 3;
7399
*/!*
74100

75101
let word = regexp.exec(str);
76-
alert(word); // varName
102+
alert(word[0]); // varName
103+
alert(word.index); // 4
77104
```
78105

79106
...То есть, при флаге `pattern:g` свойство `lastIndex` задаёт *стартовую позицию* поиска.
@@ -94,10 +121,8 @@ regexp.lastIndex = 4;
94121
alert( regexp.exec(str) ); // varName (слово на позиции 4)
95122
```
96123

97-
Как можно видеть, регулярное выражение `pattern:\w+` не найдено на позиции `3` (в отличие от флага `pattern:g`), но найдено на позиции `4`.
98-
99-
Флаг `pattern:y` позволяет проверять регулярное выражение именно на конкретной позиции. Это очень важно с точки зрения производительности.
124+
Как можно видеть, регулярное выражение `pattern:/\w+/y` не найдено на позиции `3` (в отличие от флага `pattern:g`), но найдено на позиции `4`.
100125

101-
При лексическом анализе больших текстов зачастую нужно применять несколько регэкспов на конкретной позиции текста, чтобы понять, что там находится (функция или переменная или число и т.п.)
126+
Представим себе, что у нас большой текст, и в нём нет ни одного совпадения. В таком случае регэксп с флагом `pattern:g` будет идти до самого конца текста, и это займёт гораздо больше времени, чем поиск с флагом `pattern:y`.
102127

103-
Без флага `pattern:y` регулярное выражение будет искать, начиная с `lastIndex` и далее по всему тексту, что будет занимать время, особенно, если текст большой. А по всему тексту дальше не надо. Флаг `pattern:y` для подобных задач -- именно то, что нужно.
128+
В задачах, подобных лексическому анализу, обычно много поисков на конкретной позиции. Использование флага `pattern:y` - ключ к хорошей производительности.

0 commit comments

Comments
 (0)