|
1 | | -# Lookahead and lookbehind |
| 1 | +# Перегляд уперед та назад |
2 | 2 |
|
3 | | -Sometimes we need to find only those matches for a pattern that are followed or preceded by another pattern. |
| 3 | +Іноді, нам потрібно знайти тільки такі співпадіння з шаблоном, за якими слідує, або яким передує інший шаблон. |
4 | 4 |
|
5 | | -There's a special syntax for that, called "lookahead" and "lookbehind", together referred to as "lookaround". |
| 5 | +Для цього існують спеціальні синтаксичні конструкції, котрі називається "перегляд уперед" та "перегляд назад". |
6 | 6 |
|
7 | | -For the start, let's find the price from the string like `subject:1 turkey costs 30€`. That is: a number, followed by `subject:€` sign. |
| 7 | +Для початку, давайте знайдемо ціну у рядку `subject:1 індичка коштує 30€`. Маємо: число, за яким йде символ `subject:€`. |
8 | 8 |
|
9 | | -## Lookahead |
| 9 | +## Перегляд уперед |
10 | 10 |
|
11 | | -The syntax is: `pattern:X(?=Y)`, it means "look for `pattern:X`, but match only if followed by `pattern:Y`". There may be any pattern instead of `pattern:X` and `pattern:Y`. |
| 11 | +Синтаксис виглядає наступнм чином: `pattern:X(?=Y)`, це означає "шукай `pattern:X`, але вважай його співпадінням, тільки якщо за ним слідує `pattern:Y`". Замість `pattern:X` та `pattern:Y` можуть бути будь-які інші шаблони. |
12 | 12 |
|
13 | | -For an integer number followed by `subject:€`, the regexp will be `pattern:\d+(?=€)`: |
| 13 | +Для цілого числа, за яким слідує `subject:€`, регулярний вираз виглядатиме наступним чином `pattern:\d+(?=€)`: |
14 | 14 |
|
15 | 15 | ```js run |
16 | | -let str = "1 turkey costs 30€"; |
| 16 | +let str = "1 індичка коштує 30€"; |
17 | 17 |
|
18 | | -alert( str.match(/\d+(?=€)/) ); // 30, the number 1 is ignored, as it's not followed by € |
| 18 | +alert( str.match(/\d+(?=€)/) ); // 30, число 1 ігнорується, оскільки після нього не стоїть символ € |
19 | 19 | ``` |
20 | 20 |
|
21 | | -Please note: the lookahead is merely a test, the contents of the parentheses `pattern:(?=...)` is not included in the result `match:30`. |
| 21 | +Зверніть увагу: Перегляд уперед це свого роду тест, вміст в дужках `pattern:(?=...)` не входить до відображуваного регулярним виразом співпадіння `match:30`. |
22 | 22 |
|
23 | | -When we look for `pattern:X(?=Y)`, the regular expression engine finds `pattern:X` and then checks if there's `pattern:Y` immediately after it. If it's not so, then the potential match is skipped, and the search continues. |
| 23 | +Коли ми шукаємо `pattern:X(?=Y)`, регулярний вираз знаходить `pattern:X` і далі перевіряє наявність `pattern:Y` одразу після нього. Якщо це не так, тоді потенційне співпадіння пропускається і регулярний вираз продовжує пошук. |
24 | 24 |
|
25 | | -More complex tests are possible, e.g. `pattern:X(?=Y)(?=Z)` means: |
| 25 | +Можливі і більш складні тести, наприклад `pattern:X(?=Y)(?=Z)` означає: |
26 | 26 |
|
27 | | -1. Find `pattern:X`. |
28 | | -2. Check if `pattern:Y` is immediately after `pattern:X` (skip if isn't). |
29 | | -3. Check if `pattern:Z` is also immediately after `pattern:X` (skip if isn't). |
30 | | -4. If both tests passed, then the `pattern:X` is a match, otherwise continue searching. |
| 27 | +1. Знайди `pattern:X`. |
| 28 | +2. Перевір, чи `pattern:Y` йде одразу після `pattern:X` (пропускай, якщо це не так). |
| 29 | +3. Перевір, чи `pattern:Z` також йде одразу пысля `pattern:X` (пропускай, якщо це не так). |
| 30 | +4. Якщо обидва тести пройдено, тоді `pattern:X` відповідає умовам пошуку, в інщому випадку - продовжуй пошук. |
31 | 31 |
|
32 | | -In other words, such pattern means that we're looking for `pattern:X` followed by `pattern:Y` and `pattern:Z` at the same time. |
| 32 | +Інакше кажучи, такий шаблон означає, що ми шукаємо на `pattern:X` за яким одночасно слідують `pattern:Y` та `pattern:Z`. |
33 | 33 |
|
34 | | -That's only possible if patterns `pattern:Y` and `pattern:Z` aren't mutually exclusive. |
| 34 | +Це можливо тільки за умови, якщо шаблон `pattern:Y` та `pattern:Z` не взаємовиключні. |
35 | 35 |
|
36 | | -For example, `pattern:\d+(?=\s)(?=.*30)` looks for `pattern:\d+` that is followed by a space `pattern:(?=\s)`, and there's `30` somewhere after it `pattern:(?=.*30)`: |
| 36 | +До прикладу, `pattern:\d+(?=\s)(?=.*30)` шукає на `pattern:\d+` за яким йде пробільний символ `pattern:(?=\s)`, а також `30` десь після нього `pattern:(?=.*30)`: |
37 | 37 |
|
38 | 38 | ```js run |
39 | | -let str = "1 turkey costs 30€"; |
| 39 | +let str = "1 індичка коштує 30€"; |
40 | 40 |
|
41 | 41 | alert( str.match(/\d+(?=\s)(?=.*30)/) ); // 1 |
42 | 42 | ``` |
43 | 43 |
|
44 | | -In our string that exactly matches the number `1`. |
| 44 | +В нашому рядку цим параметрам повністю відповідає число `1`. |
45 | 45 |
|
46 | | -## Negative lookahead |
| 46 | +## Негативний перегляд уперед |
47 | 47 |
|
48 | | -Let's say that we want a quantity instead, not a price from the same string. That's a number `pattern:\d+`, NOT followed by `subject:€`. |
| 48 | +Скажімо, ми хочем знайти кількість, а не ціну в тому самому рядку. Тобто, шукаємо число `pattern:\d+`, за якийм НЕ слідує `subject:€`. |
49 | 49 |
|
50 | | -For that, a negative lookahead can be applied. |
| 50 | +В такому випадку, доречним буде використання негативного перегляду уперед. |
51 | 51 |
|
52 | | -The syntax is: `pattern:X(?!Y)`, it means "search `pattern:X`, but only if not followed by `pattern:Y`". |
| 52 | +Синатксис виглядає наступним чином: `pattern:X(?!Y)`, і означає "шукай `pattern:X`, але за умови, що після нього не йде `pattern:Y`". |
53 | 53 |
|
54 | 54 | ```js run |
55 | | -let str = "2 turkeys cost 60€"; |
| 55 | +let str = "2 індички коштують 60€"; |
56 | 56 |
|
57 | | -alert( str.match(/\d+\b(?!€)/g) ); // 2 (the price is not matched) |
| 57 | +alert( str.match(/\d+\b(?!€)/g) ); // 2 (ціна не відповідає вимогам шаблону і не відображається в результаті) |
58 | 58 | ``` |
59 | 59 |
|
60 | | -## Lookbehind |
| 60 | +## Перегляд назад |
61 | 61 |
|
62 | | -```warn header="Lookbehind browser compatibility" |
63 | | -Please Note: Lookbehind is not supported in non-V8 browsers, such as Safari, Internet Explorer. |
| 62 | +```warn header="Сумісність браузерів з переглядом назад" |
| 63 | +Зверніть увагу: Перегляд назад не підтримується в браузерах з відміннимим від V8 двигунами, зокрема Safari, Internet Explorer. |
64 | 64 | ``` |
65 | 65 |
|
66 | | -Lookahead allows to add a condition for "what follows". |
| 66 | +Перегляд уперед дозволяє додати умову на кшталт "те, що слідує після". |
67 | 67 |
|
68 | | -Lookbehind is similar, but it looks behind. That is, it allows to match a pattern only if there's something before it. |
| 68 | +Перегляд назад подібний, але дивиться у зворотньому напрямку. Таким чином, він виводить результат, тільки якщо співпадає і шаблон і те, що йде до нього. |
69 | 69 |
|
70 | | -The syntax is: |
71 | | -- Positive lookbehind: `pattern:(?<=Y)X`, matches `pattern:X`, but only if there's `pattern:Y` before it. |
72 | | -- Negative lookbehind: `pattern:(?<!Y)X`, matches `pattern:X`, but only if there's no `pattern:Y` before it. |
| 70 | +Синтаксис наступний: |
| 71 | +- Позитивний перегляд назад: `pattern:(?<=Y)X`, співпадає з `pattern:X`, тільки за умови, якщо перед ним є `pattern:Y`. |
| 72 | +- Негативний перегляд назад: `pattern:(?<!Y)X`, співпадає `pattern:X`, тільки за умови, якщо перед ним немає `pattern:Y`. |
73 | 73 |
|
74 | | -For example, let's change the price to US dollars. The dollar sign is usually before the number, so to look for `$30` we'll use `pattern:(?<=\$)\d+` -- an amount preceded by `subject:$`: |
| 74 | +Наприклад, змінимо ціну з евро на американські долари. Знак долару зазвичай стоїть перед числом, тому, для пошуку `$30` ми використовуватимемо `pattern:(?<=\$)\d+` -- сума, перед якою є символ `subject:$`: |
75 | 75 |
|
76 | 76 | ```js run |
77 | | -let str = "1 turkey costs $30"; |
| 77 | +let str = "1 індичка коштує $30"; |
78 | 78 |
|
79 | | -// the dollar sign is escaped \$ |
80 | | -alert( str.match(/(?<=\$)\d+/) ); // 30 (skipped the sole number) |
| 79 | +// знак долара екрановано \$ |
| 80 | +alert( str.match(/(?<=\$)\d+/) ); // 30 (число 1 пропущено через відсутність знаку долару перед ним) |
81 | 81 | ``` |
82 | 82 |
|
83 | | -And, if we need the quantity -- a number, not preceded by `subject:$`, then we can use a negative lookbehind `pattern:(?<!\$)\d+`: |
| 83 | +Також, якщо нам потрібна кількість -- число, якому не передує `subject:$`, в такому випадку ми можемо використати негативний перегляд назад `pattern:(?<!\$)\d+`: |
84 | 84 |
|
85 | 85 | ```js run |
86 | | -let str = "2 turkeys cost $60"; |
| 86 | +let str = "2 індички коштують $60"; |
87 | 87 |
|
88 | | -alert( str.match(/(?<!\$)\b\d+/g) ); // 2 (the price is not matched) |
| 88 | +alert( str.match(/(?<!\$)\b\d+/g) ); // 2 (ціна не спвіпадає з умовами пошуку) |
89 | 89 | ``` |
90 | 90 |
|
91 | | -## Capturing groups |
| 91 | +## Дужкові групи |
92 | 92 |
|
93 | | -Generally, the contents inside lookaround parentheses does not become a part of the result. |
| 93 | +Зазвичай, вміст в дужках перегляду вперед та назад не є частиною співпадіння. |
94 | 94 |
|
95 | | -E.g. in the pattern `pattern:\d+(?=€)`, the `pattern:€` sign doesn't get captured as a part of the match. That's natural: we look for a number `pattern:\d+`, while `pattern:(?=€)` is just a test that it should be followed by `subject:€`. |
| 95 | +Наприклад, у шаблоні `pattern:\d+(?=€)`, символ `pattern:€` не відображається при виведенні співпадінь. Це нормально: ми шукаємо на число `pattern:\d+`, тоді як `pattern:(?=€)` це лише перевірка на те, чи дійсно за ним йде символ `subject:€`. |
96 | 96 |
|
97 | | -But in some situations we might want to capture the lookaround expression as well, or a part of it. That's possible. Just wrap that part into additional parentheses. |
| 97 | +Але в деяких ситуаціях ми можемо поребувати виведення вмісту шаблону перегляду вперед та назад, або його частини. Це можливо. Просто огорніть потрібну частину в додаткові круглі дужки. |
98 | 98 |
|
99 | | -In the example below the currency sign `pattern:(€|kr)` is captured, along with the amount: |
| 99 | +В нижченаведеному прикладі знак валюти `pattern:(€|kr)` теж відображено у результаті, разом із сумою: |
100 | 100 |
|
101 | 101 | ```js run |
102 | | -let str = "1 turkey costs 30€"; |
103 | | -let regexp = /\d+(?=(€|kr))/; // extra parentheses around €|kr |
| 102 | +let str = "1 індичка коштує 30€"; |
| 103 | +let regexp = /\d+(?=(€|kr))/; // додаткові круглі дужки навколо €|kr |
104 | 104 |
|
105 | 105 | alert( str.match(regexp) ); // 30, € |
106 | 106 | ``` |
107 | 107 |
|
108 | | -And here's the same for lookbehind: |
| 108 | +І так само для перегляду назад: |
109 | 109 |
|
110 | 110 | ```js run |
111 | | -let str = "1 turkey costs $30"; |
| 111 | +let str = "1 індичка коштує $30"; |
112 | 112 | let regexp = /(?<=(\$|£))\d+/; |
113 | 113 |
|
114 | 114 | alert( str.match(regexp) ); // 30, $ |
115 | 115 | ``` |
116 | 116 |
|
117 | | -## Summary |
| 117 | +## Підсумок |
118 | 118 |
|
119 | | -Lookahead and lookbehind (commonly referred to as "lookaround") are useful when we'd like to match something depending on the context before/after it. |
| 119 | +Перегляд вперед на назад корисні, коли нам потрібно знайти щось, залежно від контексту до чи після потрібного шаблону. |
120 | 120 |
|
121 | | -For simple regexps we can do the similar thing manually. That is: match everything, in any context, and then filter by context in the loop. |
| 121 | +Для простих регулярних виразів ми можемо виконати подібну задачу вручну. Тобто: відшукати всі спвіпадіння, у будь-якому контексті, а потім відфільтрувати їх за контекстом за допомогою циклу. |
122 | 122 |
|
123 | | -Remember, `str.match` (without flag `pattern:g`) and `str.matchAll` (always) return matches as arrays with `index` property, so we know where exactly in the text it is, and can check the context. |
| 123 | +Пам'ятайте, `str.match` (без флажку `pattern:g`) і `str.matchAll` (всі) повертає співпадіння у вигляді масиву з властивістю `індекс`, тож ми точно знаємо де саме в тексті вони знаходяться і можемо перевірити контекст. |
124 | 124 |
|
125 | | -But generally lookaround is more convenient. |
| 125 | +Але загалом перегляд уперед і назад більш підходящі. |
126 | 126 |
|
127 | | -Lookaround types: |
| 127 | +Типи переглядів: |
128 | 128 |
|
129 | | -| Pattern | type | matches | |
| 129 | +| Шаблон | Тип | Співпадіння | |
130 | 130 | |--------------------|------------------|---------| |
131 | | -| `X(?=Y)` | Positive lookahead | `pattern:X` if followed by `pattern:Y` | |
132 | | -| `X(?!Y)` | Negative lookahead | `pattern:X` if not followed by `pattern:Y` | |
133 | | -| `(?<=Y)X` | Positive lookbehind | `pattern:X` if after `pattern:Y` | |
134 | | -| `(?<!Y)X` | Negative lookbehind | `pattern:X` if not after `pattern:Y` | |
| 131 | +| `X(?=Y)` | Позитивний перегляд уперед | `pattern:X` якщо за ним йде `pattern:Y` | |
| 132 | +| `X(?!Y)` | Негативний перегляд уперед | `pattern:X` якщо за ним не йде `pattern:Y` | |
| 133 | +| `(?<=Y)X` | Позитивний перегляд назад | `pattern:X` якщо він йде після `pattern:Y` | |
| 134 | +| `(?<!Y)X` | Негативний перегляд назад | `pattern:X` якщо тільки він не йде після `pattern:Y` | |
0 commit comments