Skip to content

Commit 817e556

Browse files
Merge pull request #234 from miniengineer/9-regular-expressions/14-regexp-lookahead-lookbehind/article.md-1
9-regular-expressions/14-regexp-lookahead-lookbehind
2 parents 562bbd9 + 83a0401 commit 817e556

File tree

1 file changed

+52
-52
lines changed
  • 9-regular-expressions/14-regexp-lookahead-lookbehind

1 file changed

+52
-52
lines changed
Lines changed: 52 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,109 +1,109 @@
1-
# Lookahead and lookbehind
1+
# Опережающие и ретроспективные проверки
22

3-
Sometimes we need to match a pattern only if followed by another pattern. For instance, we'd like to get the price from a string like `subject:1 turkey costs 30€`.
3+
В некоторых случаях нам нужно найти соответствие шаблону, за которым следует другой шаблон. Например, мы хотим получить цену одной индейки в строке: `subject:1 индейка стоит 30€`.
44

5-
We need a number (let's say a price has no decimal point) followed by `subject:€` sign.
5+
Нам нужно число (здесь предположим, что в цена - целое число, без десятичной точки), после которого следует знак валюты `subject:€`.
66

7-
That's what lookahead is for.
7+
Именно для таких задач и существует опережающая проверка.
88

9-
## Lookahead
9+
## Опережающая проверка
1010

11-
The syntax is: `pattern:x(?=y)`, it means "look for `pattern:x`, but match only if followed by `pattern:y`".
11+
Синтаксис: `pattern:x(?=y)`
12+
Пояснение: найди `pattern:х` при условии, что за ним следует `pattern:y`.
1213

13-
For an integer amount followed by `subject:€`, the regexp will be `pattern:\d+(?=€)`:
14+
Для целого числа, за которым идёт знак `subject:€`, шаблон регулярного выражения будет `pattern:\d+(?=€)`:
1415

1516
```js run
16-
let str = "1 turkey costs 30€";
17+
let str = "1 индейка стоит 30€";
1718

18-
alert( str.match(/\d+(?=€)/) ); // 30 (correctly skipped the sole number 1)
19+
alert( str.match(/\d+(?=€)/) ); // 30 (число 1 было проигнорировано, так как за ним НЕ следует `subject:€`)
1920
```
2021

21-
Let's say we want a quantity instead, that is a number, NOT followed by `subject:€`.
22+
Допустим, нам нужно узнать количество индеек, которое можно купить за 30€ - это число, за которым НЕ следует знак `subject:€`.
2223

23-
Here a negative lookahead can be applied.
24+
Для этой задачи мы можем применить негативную опережающую проверку.
2425

25-
The syntax is: `pattern:x(?!y)`, it means "search `pattern:x`, but only if not followed by `pattern:y`".
26+
Синтаксис: `pattern:x(?!y)`
27+
Пояснение: найди такой `pattern:х`, за которым НЕ следует `pattern:y`.
2628

2729
```js run
28-
let str = "2 turkeys cost 60€";
30+
let str = "2 индейки стоят 60€";
2931

30-
alert( str.match(/\d+(?!€)/) ); // 2 (correctly skipped the price)
32+
alert( str.match(/\d+(?!€)/) ); // 2 (в этот раз была проигнорирована цена)
3133
```
3234

33-
## Lookbehind
35+
## Ретроспективная проверка
3436

35-
Lookahead allows to add a condition for "what goes after".
37+
Опережающие проверки позволяют задавать условия на то, что "идёт после".
3638

37-
Lookbehind is similar, but it looks behind. That is, it allows to match a pattern only if there's something before.
39+
Ретроспективная проверка выполняет такую же функцию, но с просмотром назад. Другими словами, она находит соответствие шаблону, только если перед ним есть что-то заранее определённое.
3840

39-
The syntax is:
40-
- Positive lookbehind: `pattern:(?<=y)x`, matches `pattern:x`, but only if it follows after `pattern:y`.
41-
- Negative lookbehind: `pattern:(?<!y)x`, matches `pattern:x`, but only if there's no `pattern:y` before.
41+
Синтаксис:
42+
- Позитивная ретроспективная проверка: `pattern:(?<=y)x`, выдаёт совпадение на `pattern:x` при условии, что перед ним ЕСТЬ `pattern:y`.
43+
- Негативная ретроспективная проверка: `pattern:(?<!y)x`, выдаёт совпадение на `pattern:x` при условии, что перед ним НЕТ `pattern:y`.
4244

43-
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 preceeded by `subject:$`:
45+
Чтобы протестировать ретроспективную проверку, давайте поменяем валюту на доллары США. Знак доллара обычно ставится перед суммой денег, поэтому для того чтобы найти `$30`, мы используем `pattern:(?<=\$)\d+` - число, перед которым идёт `subject:$`:
4446

4547
```js run
46-
let str = "1 turkey costs $30";
48+
let str = "1 индейка стоит $30";
4749

48-
alert( str.match(/(?<=\$)\d+/) ); // 30 (skipped the sole number)
50+
alert( str.match(/(?<=\$)\d+/) ); // 30 (одинокое число игнорируется)
4951
```
5052

51-
And, to find the quantity -- a number, not preceeded by `subject:$`, we can use a negative lookbehind `pattern:(?<!\$)\d+`:
53+
Если нам необходимо найти количество индеек -- число, перед которым не идёт `subject:$`, мы можем использовать негативную ретроспективную проверку `pattern:(?<!\$)\d+`:
5254

5355
```js run
54-
let str = "2 turkeys cost $60";
56+
let str = "2 индейки стоят $60";
5557

56-
alert( str.match(/(?<!\$)\d+/) ); // 2 (skipped the price)
58+
alert( str.match(/(?<!\$)\d+/) ); // 2 (проигнорировалась цена)
5759
```
5860

59-
## Capture groups
61+
## Захват групп
6062

61-
Generally, what's inside the lookaround (a common name for both lookahead and lookbehind) parentheses does not become a part of the match.
63+
Как правило, то что находится внутри скобок, задающих опережающую и ретроспективную проверку, не включается в результат совпадения.
6264

63-
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:€`.
65+
Например, в шаблоне `pattern:\d+(?=€)` знак `pattern:€` не будет включён в результат. Это логично, ведь мы ищем число `pattern:\d+`, а `pattern:(?=€)` - это всего лишь проверка, что за ним идёт знак `subject:€`.
6466

65-
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 into additional parentheses.
67+
Но в некоторых ситуациях нам может быть интересно захватить и то, что в проверке. Для этого нужно обернуть это в дополнительные скобки.
6668

67-
For instance, here the currency `pattern:(€|kr)` is captured, along with the amount:
69+
В следующем примере знак валюты `pattern:(€|kr)` будет включён в результат вместе с суммой:
6870

6971
```js run
70-
let str = "1 turkey costs 30€";
71-
let reg = /\d+(?=(€|kr))/; // extra parentheses around €|kr
72+
let str = "1 индейка стоит 30€";
73+
let reg = /\d+(?=(€|kr))/; // добавлены дополнительные скобки вокруг €|kr
7274

7375
alert( str.match(reg) ); // 30, €
7476
```
7577

76-
And here's the same for lookbehind:
78+
Тоже самое можно применить к ретроспективной проверке:
7779

7880
```js run
79-
let str = "1 turkey costs $30";
81+
let str = "1 индейка стоит $30";
8082
let reg = /(?<=(\$|£))\d+/;
8183

8284
alert( str.match(reg) ); // 30, $
8385
```
8486

85-
Please note that for lookbehind the order stays be same, even though lookahead parentheses are before the main pattern.
87+
Обратите внимание, что порядок выдачи результата ретроспективной проверки остаётся прежним, хотя скобки из опережающей проверки расположены ПЕРЕД основным шаблоном.
8688

87-
Usually parentheses are numbered left-to-right, but lookbehind is an exception, it is always captured after the main pattern. So the match for `pattern:\d+` goes in the result first, and then for `pattern:(\$|£)`.
89+
Обычно совпадения с выражениями в скобках нумеруются по порядку -- слева направо. Однако, ретроспективная проверка является исключением, так как при ней совпадение с выражением в скобках всегда идёт после результата основного шаблона. Так, в нашем примере совпадение с основным шаблоном `pattern:\d+` будет идти первым, а результат для `pattern:(\$|£)` будет вторым.
8890

91+
## Итого
8992

90-
## Summary
93+
Опережающая и ретроспективная проверки удобны, когда мы хотим искать шаблон по дополнительному условию на контекст, в котором он находится.
9194

92-
Lookahead and lookbehind (commonly referred to as "lookaround") are useful when we'd like to take something into the match depending on the context before/after it.
95+
Для простых регулярных выражений мы можем сделать похожую вещь "вручную". То есть, найти все совпадения, независимо от контекста, а затем в цикле отфильтровать подходящие.
9396

94-
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.
97+
Как мы помним, что `str.matchAll` и `reg.exec` возвращают совпадения со свойством `.index`, поэтому мы знаем их точное расположение в тексте и можем посмотреть на контекст.
98+
Но обычно регулярные выражения удобнее.
9599

96-
Remember, `str.matchAll` and `reg.exec` return matches with `.index` property, so we know where exactly in the text it is, and can check the context.
100+
Виды проверок:
97101

98-
But generally regular expressions are more convenient.
102+
| Паттерн | Тип | Совпадение |
103+
|--------------------|----------------------------|---------------------------------|
104+
| `pattern:x(?=y)` | Позитивная опережающая | `x`, если за ним следует `y` |
105+
| `pattern:x(?!y)` | Негативная опережающая | `x`, если за ним НЕ следует `y` |
106+
| `pattern:(?<=y)x` | Позитивная ретроспективная | `x`, если следует за `y` |
107+
| `pattern:(?<!y)x` | Негативная ретроспективная | `x`, если НЕ следует за `y` |
99108

100-
Lookaround types:
101-
102-
| Pattern | type | matches |
103-
|--------------------|------------------|---------|
104-
| `pattern:x(?=y)` | Positive lookahead | `x` if followed by `y` |
105-
| `pattern:x(?!y)` | Negative lookahead | `x` if not followed by `y` |
106-
| `pattern:(?<=y)x` | Positive lookbehind | `x` if after `y` |
107-
| `pattern:(?<!y)x` | Negative lookbehind | `x` if not after `y` |
108-
109-
Lookahead can also used to disable backtracking. Why that may be needed and other details -- see in the next chapter.
109+
Опережающая проверка также может быть использована, чтобы отключить возврат при поиске. Для чего нам это может понадобиться и другие детали, вы узнаете в следующей главе.

0 commit comments

Comments
 (0)