You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
results =Array.from(results); // превращаем в массив
206
207
207
208
alert(results[0]); // <h1>,h1 (первый тег)
208
209
alert(results[1]); // <h2>,h2 (второй тег)
209
210
```
210
211
211
-
Как видите, первое отличие - очень важное. Мы не можем получить совпадение как `results[0]`, так как этот объект не является псевдомассивом. Его можно превратить в настоящий массив при помощи `Array.from`. Более подробно о псевдомассивах и перебираемых объектов мы говорили в главе <info:iterable>.
212
+
Как видите, первое отличие - очень важное, это демонстрирует строка `(*)`. Мы не можем получить совпадение как `results[0]`, так как этот объект не является псевдомассивом. Его можно превратить в настоящий массив при помощи `Array.from`. Более подробно о псевдомассивах и перебираемых объектов мы говорили в главе <info:iterable>.
212
213
213
214
В явном преобразовании через `Array.from` нет необходимости, если мы перебираем результаты в цикле, вот так:
214
215
@@ -228,6 +229,19 @@ for(let result of results) {
228
229
let [tag1, tag2] ='<h1> <h2>'.matchAll(/<(.*?)>/gi);
229
230
```
230
231
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
+
231
245
```smart header="Почему результат `matchAll` - перебираемый объект, а не обычный массив?"
232
246
Зачем так сделано? Причина проста - для оптимизации.
Copy file name to clipboardExpand all lines: 9-regular-expressions/16-regexp-sticky/article.md
+43-18Lines changed: 43 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,51 +1,76 @@
1
1
# Поиск на заданной позиции, флаг "y"
2
2
3
-
Флаг `pattern:y` позволяет произвести поиск, начиная с определённой позиции в исходной строке.
3
+
Флаг `pattern:y` позволяет произвести поиск на определённой позиции в исходной строке.
4
4
5
5
Чтобы разобрать флаг `pattern:y` и понять, чем же он хорош, рассмотрим практический пример.
6
6
7
-
Одна из часто встречающихся задач регулярных выражений - лексический разбор: мы имеем текстна каком-то языке программирования и с помощью регулярных выражений (или иным способом) получаем его структурные элементы.
7
+
Одна из часто встречающихся задач регулярных выражений - лексический разбор: мы имеем текст, например, на каком-то языке программирования и получаем его структурные элементы.
8
8
9
-
Например, в браузеры встроен лексический анализатор HTML, а также для языка JavaScript.
9
+
Например, в HTML есть теги и атрибуты, в JavaScript-коде - переменные и функции, и т.п.
10
10
11
11
Мы не будем погружаться глубоко в тему написания таких анализаторов (это специализированная область со своим набором инструментов и алгоритмов). Но в процессе их работы, вообще, в процессе анализа текста, очень часто возникает задача "прочитать что-то на заданной позиции".
12
12
13
13
Например, у нас есть строка кода `subject:let varName = "value"`, и нам надо прочитать из неё имя переменной, которое начинается с позиции `4`.
14
14
15
15
Имя переменной будем искать как слово `pattern:\w+`. Вообще, в языке JavaScript для имени переменной нужно чуть более сложное регулярное выражение, но здесь это не важно.
16
16
17
-
Обычный поиск, такой как `str.match` найдёт только первое слово в строке или все слова (с флагом `pattern:g`), а нам нужно одно слово именно на позиции `4`.
17
+
Вызов `str.match(/\w+/)` найдёт только первое слово в строке или все слова (с флагом `pattern:g`), а нам нужно одно слово именно на позиции `4`.
18
18
19
-
Для использования флага `pattern:y` понадобится метод `regexp.exec(str)`.
19
+
Для поиска, начиная с нужной позиции, можно использовать метод `regexp.exec(str)`.
20
20
21
-
Если у регулярного выражения нет флагов `pattern:g` или `pattern:y`, то он ищет первое совпадение с `regexp`в строке `str`, аналогично `str.match(regexp)`. Здесь нас вариант без флагов не интересует.
21
+
Если у регулярного выражения `regexp`нет флагов `pattern:g` или `pattern:y`, то этот метод ищет первое совпадение в строке `str`, точно так же, как `str.match(regexp)`. Здесь нас этот простейший вариант без флагов не интересует.
22
22
23
23
Если флаг `pattern:g` есть, то он осуществляет поиск в строке `str`, начиная с позиции, заданной свойством `regexp.lastIndex`. И, когда находит, обновляет `regexp.lastIndex` на позицию после совпадения.
24
24
25
25
При создании регулярного выражения его свойство `lastIndex` равно `0`.
26
26
27
-
Например (пока без флага `y`):
27
+
Так что повторные вызовы `regexp.exec` возвращают совпадения по очереди, одно за другим.
28
+
29
+
Например (с флагом `pattern:g`):
28
30
29
31
```js run
30
-
let str ='let varName = "value"';
32
+
let str ='let varName';
31
33
32
34
let regexp =/\w+/g;
33
35
alert(regexp.lastIndex); // 0 (при создании lastIndex=0)
34
36
35
37
let word1 =regexp.exec(str);
36
-
alert(word1); // let (первое слово)
38
+
alert(word1[0]); // let (первое слово)
37
39
alert(regexp.lastIndex); // 3 (позиция за первым совпадением)
38
40
39
41
let word2 =regexp.exec(str);
40
-
alert(word2); // varName (второе слово)
42
+
alert(word2[0]); // varName (второе слово)
41
43
alert(regexp.lastIndex); // 11 (позиция за вторым совпадением)
44
+
45
+
let word3 =regexp.exec(str);
46
+
alert(word3); // null (больше совпадений нет)
47
+
alert(regexp.lastIndex); // 0 (сбрасывается по окончании поиска)
42
48
```
43
49
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
+
44
69
Таким образом, последовательные вызовы `regexp.exec` могут найти все совпадения, представляя собой альтернативу методам `str.match/matchAll`.
45
70
46
-
Но, в отличие от этих методов, мы можем поставить самостоятельно `lastIndex`, начав тем самым поиск именно с нужной позиции.
71
+
Но, в отличие от других методов, мы можем поставить самостоятельно `lastIndex`, начав тем самым поиск именно с нужной позиции.
47
72
48
-
Например, найдём слово на позиции `4`:
73
+
Например, найдём слово, начиная с позиции `4`:
49
74
50
75
```js run
51
76
let str ='let varName = "value"';
@@ -68,12 +93,14 @@ alert(word); // varName
68
93
let str ='let varName = "value"';
69
94
70
95
let regexp =/\w+/g;
96
+
71
97
*!*
72
98
regexp.lastIndex=3;
73
99
*/!*
74
100
75
101
let word =regexp.exec(str);
76
-
alert(word); // varName
102
+
alert(word[0]); // varName
103
+
alert(word.index); // 4
77
104
```
78
105
79
106
...То есть, при флаге `pattern:g` свойство `lastIndex` задаёт *стартовую позицию* поиска.
@@ -94,10 +121,8 @@ regexp.lastIndex = 4;
94
121
alert( regexp.exec(str) ); // varName (слово на позиции 4)
95
122
```
96
123
97
-
Как можно видеть, регулярное выражение `pattern:\w+` не найдено на позиции `3` (в отличие от флага `pattern:g`), но найдено на позиции `4`.
98
-
99
-
Флаг `pattern:y` позволяет проверять регулярное выражение именно на конкретной позиции. Это очень важно с точки зрения производительности.
124
+
Как можно видеть, регулярное выражение `pattern:/\w+/y` не найдено на позиции `3` (в отличие от флага `pattern:g`), но найдено на позиции `4`.
100
125
101
-
При лексическом анализе больших текстов зачастую нужно применять несколько регэкспов на конкретной позиции текста, чтобы понять, что там находится (функция или переменная или число и т.п.)
126
+
Представим себе, что у нас большой текст, и в нём нет ни одного совпадения. В таком случае регэксп с флагом `pattern:g` будет идти до самого конца текста, и это займёт гораздо больше времени, чем поиск с флагом `pattern:y`.
102
127
103
-
Без флага `pattern:y` регулярное выражение будет искать, начиная с `lastIndex` и далее по всему тексту, что будет занимать время, особенно, если текст большой. А по всему тексту дальше не надо. Флаг `pattern:y`для подобных задач -- именно то, что нужно.
128
+
В задачах, подобных лексическому анализу, обычно много поисков на конкретной позиции. Использование флага `pattern:y`- ключ к хорошей производительности.
0 commit comments