This commit is contained in:
Ilya Kantor 2017-03-19 20:40:47 +03:00
parent 75e30539ef
commit 62c507c78f
92 changed files with 583 additions and 574 deletions

View file

@ -0,0 +1,29 @@
Регулярное выражение для поиска 3-значного цвета вида `#abc`: `pattern:/#[a-f0-9]{3}/i`.
Нужно добавить ещё три символа, причём нужны именно три, четыре или семь символов не нужны. Эти три символа либо есть, либо нет.
Самый простой способ добавить -- просто дописать в конец регэкспа: `pattern:/#[a-f0-9]{3}([a-f0-9]{3})?/i`
Можно поступить и хитрее: `pattern:/#([a-f0-9]{3}){1,2}/i`.
Здесь регэксп `pattern:[a-f0-9]{3}` заключён в скобки, чтобы квантификатор `pattern:{1,2}` применялся целиком ко всей этой структуре.
В действии:
```js run
var re = /#([a-f0-9]{3}){1,2}/gi;
var str = "color: #3f3; background-color: #AA00ef; and: #abcd";
alert( str.match(re) ); // #3f3 #AA0ef #abc
```
В последнем выражении `subject:#abcd` было найдено совпадение `match:#abc`. Чтобы этого не происходило, добавим в конец `pattern:\b`:
```js run
var re = /#([a-f0-9]{3}){1,2}\b/gi;
var str = "color: #3f3; background-color: #AA00ef; and: #abcd";
alert( str.match(re) ); // #3f3 #AA0ef
```

View file

@ -0,0 +1,14 @@
# Найдите цвет в формате #abc или #abcdef
Напишите регулярное выражение, которое находит цвет в формате `#abc` или `#abcdef`. То есть, символ `#`, после которого идут 3 или 6 шестнадцатиричных символа.
Пример использования:
```js
var re = /* ваш регэксп */
var str = "color: #3f3; background-color: #AA00ef; and: #abcd";
alert( str.match(re) ); // #3f3 #AA0ef
```
P.S. Значения из любого другого количества букв, кроме 3 и 6, такие как `#abcd`, не должны подходить под регэксп.

View file

@ -0,0 +1,49 @@
Регулярное выражение для числа, возможно, дробного и отрицательного: `pattern:-?\d+(\.\d+)?`. Мы уже разбирали его в предыдущих задачах.
Оператор -- это `pattern:[-+*/]`. Заметим, что дефис `pattern:-` идёт в списке первым, так как на любой позиции, кроме первой и последней, он имеет специальный смысл внутри `pattern:[...]`, и его понадобилось бы экранировать.
Кроме того, когда мы оформим это в JavaScript-синтаксис `pattern:/.../` -- понадобится заэкранировать слэш `pattern:/`.
Нам нужно число, затем оператор, затем число, и необязательные пробелы между ними.
Полное регулярное выражение будет таким: `pattern:-?\d+(\.\d+)?\s*[-+*/]\s*-?\d+(\.\d+)?`.
Чтобы получить результат в виде массива, добавим скобки вокруг тех данных, которые нам интересны, то есть -- вокруг чисел и оператора: `pattern:(-?\d+(\.\d+)?)\s*([-+*/])\s*(-?\d+(\.\d+)?)`.
Посмотрим в действии:
```js run
var re = /(-?\d+(\.\d+)?)\s*([-+*\/])\s*(-?\d+(\.\d+)?)/;
alert( "1.2 + 12".match(re) );
```
Итоговый массив будет включать в себя компоненты:
- `result[0] == "1.2 + 12"` (вначале всегда полное совпадение)
- `result[1] == "1"` (первая скобка)
- `result[2] == "2"` (вторая скобка -- дробная часть `(\.\d+)?`)
- `result[3] == "+"` (...)
- `result[4] == "12"` (...)
- `result[5] == undefined` (последняя скобка, но у второго числа дробная часть отсутствует)
Нам из этого массива нужны только числа и оператор. А, скажем, дробная часть сама по себе -- не нужна.
Уберём её из запоминания, добавив в начало скобки `pattern:?:`, то есть: `pattern:(?:\.\d+)?`.
Итого, решение:
```js run
function parse(expr) {
var re = /(-?\d+(?:\.\d+)?)\s*([-+*\/])\s*(-?\d+(?:\.\d+)?)/;
var result = expr.match(re);
if (!result) return;
result.shift();
return result;
}
alert( parse("-1.23 * 3.45") ); // -1.23, *, 3.45
```

View file

@ -0,0 +1,19 @@
# Разобрать выражение
Арифметическое выражение состоит из двух чисел и операции между ними, например:
- `1 + 2`
- `1.2 * 3.4`
- `-3 / -6`
- `-2 - 2`
Список операций: `"+"`, `"-"`, `"*"` и `"/"`.
Также могут присутствовать пробелы вокруг оператора и чисел.
Напишите функцию, которая будет получать выражение и возвращать массив из трёх аргументов:
1. Первое число.
2. Оператор.
3. Второе число.

View file

@ -0,0 +1,16 @@
An integer number is `pattern:\d+`.
A decimal part is: `pattern:\.\d+`.
Because the decimal part is optional, let's put it in parentheses with quantifier `pattern:'?'`.
Finally we have the regexp: `pattern:\d+(\.\d+)?`:
```js run
let reg = /\d+(\.\d+)?/g;
let str = "1.5 0 12. 123.4.";
alert( str.match(re) ); // 1.5, 0, 12, 123.4
```

View file

@ -0,0 +1,12 @@
# Find positive numbers
Create a regexp that looks for positive numbers, including those without a decimal point.
An example of use:
```js
let reg = /your regexp/g;
let str = "1.5 0 12. 123.4.";
alert( str.match(reg) ); // 1.5, 0, 12, 123.4
```

View file

@ -0,0 +1,11 @@
A positive number with an optional decimal part is (per previous task): `pattern:\d+(\.\d+)?`.
Let's add an optional `-` in the beginning:
```js run
let reg = /-?\d+(\.\d+)?/g;
let str = "-1.5 0 2 -123.4.";
alert( str.match(reg) ); // -1.5, 0, 2, -123.4
```

View file

@ -0,0 +1,13 @@
# Find all numbers
Write a regexp that looks for all decimal numbers including integer ones, with the floating point and negative ones.
An example of use:
```js
let reg = /your regexp/g;
let str = "-1.5 0 2 -123.4.";
alert( str.match(re) ); // -1.5, 0, 2, -123.4
```

View file

@ -0,0 +1,139 @@
# Bracket groups
A part of the pattern can be enclosed in parentheses `pattern:(...)`. That's called a "bracket expression" or a "bracket group".
That has two effects:
1. It allows to place a part of the match into a separate array item when using [String#match](mdn:js/String/match) or [RegExp#exec](mdn:/RegExp/exec) methods.
2. If we put a quantifier after the parentheses, it applies to the parentheses as a whole, not the last character.
[cut]
## Example
В примере ниже, шаблон `pattern:(go)+` находит один или более повторяющихся `pattern:'go'`:
```js run
alert( 'Gogogo now!'.match(/(go)+/i) ); // "Gogogo"
```
Без скобок, шаблон `pattern:/go+/` означал бы `subject:g`, после которого идёт одна или более `subject:o`, например: `match:goooo`. А скобки "группируют" `pattern:(go)` вместе.
## Содержимое группы
Скобки нумеруются слева направо. Поисковой движок запоминает содержимое каждой скобки и позволяет обращаться к нему -- в шаблоне и строке замены и, конечно же, в результатах.
Например, найти HTML-тег можно шаблоном `pattern:<.*?>`.
После поиска мы захотим что-то сделать с результатом. Для удобства заключим содержимое `<...>` в скобки: `pattern:<(.*?)>`. Тогда оно будет доступно отдельно.
При поиске методом [String#match](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/match) в результирующем массиве будет сначала всё совпадение, а далее -- скобочные группы. В шаблоне `pattern:<(.*?)>` скобочная группа только одна:
```js run
var str = '<h1>Привет, мир!</h1>';
var reg = /<(.*?)>/;
alert( str.match(reg) ); // массив: <h1>, h1
```
Заметим, что метод [String#match](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/match) выдаёт скобочные группы только при поиске без флага `/.../g`. В примере выше он нашёл только первое совпадение `match:<h1>`, а закрывающий `match:</h1>` не нашёл, поскольку без флага `/.../g` ищется только первое совпадение.
Для того, чтобы искать и с флагом `/.../g` и со скобочными группами, используется метод [RegExp#exec](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec):
```js run
var str = '<h1>Привет, мир!</h1>';
var reg = /<(.*?)>/g;
var match;
while ((match = reg.exec(str)) !== null) {
// сначала выведет первое совпадение: <h1>,h1
// затем выведет второе совпадение: </h1>,/h1
alert(match);
}
```
Теперь найдено оба совпадения `pattern:<(.*?)>`, каждое -- массив из полного совпадения и скобочных групп (одна в данном случае).
## Вложенные группы
Скобки могут быть и вложенными. В этом случае нумерация также идёт слева направо.
Например, при поиске тега в `subject:<span class="my">` нас может интересовать:
1. Содержимое тега целиком: `span class="my"`.
2. В отдельную переменную для удобства хотелось бы поместить тег: `span`.
3. Также может быть удобно отдельно выделить атрибуты `class="my"`.
Добавим скобки в регулярное выражение:
```js run
var str = '<span class="my">';
var reg = /<(([a-z]+)\s*([^>]*))>/;
alert( str.match(reg) ); // <span class="my">, span class="my", span, class="my"
```
Вот так выглядят скобочные группы:
![](regexp-nested-groups.png)
На нулевом месте -- всегда совпадение полностью, далее -- группы. Нумерация всегда идёт слева направо, по открывающей скобке.
В данном случае получилось, что группа 1 включает в себя содержимое групп 2 и 3. Это совершенно нормальная ситуация, которая возникает, когда нужно выделить что-то отдельное внутри большей группы.
**Даже если скобочная группа необязательна и не входит в совпадение, соответствующий элемент массива существует (и равен `undefined`).**
Например, рассмотрим регэксп `pattern:a(z)?(c)?`. Он ищет `"a"`, за которой не обязательно идёт буква `"z"`, за которой необязательно идёт буква `"c"`.
Если напустить его на строку из одной буквы `"a"`, то результат будет таков:
```js run
var match = 'a'.match(/a(z)?(c)?/)
alert( match.length ); // 3
alert( match[0] ); // a
alert( match[1] ); // undefined
alert( match[2] ); // undefined
```
Массив получился длины `3`, но все скобочные группы -- `undefined`.
А теперь более сложная ситуация, строка `subject:ack`:
```js run
var match = 'ack'.match(/a(z)?(c)?/)
alert( match.length ); // 3
alert( match[0] ); // ac, всё совпадение
alert( match[1] ); // undefined, для (z)? ничего нет
alert( match[2] ); // c
```
Длина массива результатов по-прежнему `3`. Она постоянна. А вот для скобочной группы `pattern:(z)?` в ней ничего нет, поэтому результат: `["ac", undefined, "c"]`.
## Исключение из запоминания через ?:
Бывает так, что скобки нужны, чтобы квантификатор правильно применился, а вот запоминать их содержимое в массиве не нужно.
Скобочную группу можно исключить из запоминаемых и нумеруемых, добавив в её начало `pattern:?:`.
Например, мы хотим найти `pattern:(go)+`, но содержимое скобок (`go`) в отдельный элемент массива выделять не хотим.
Для этого нужно сразу после открывающей скобки поставить `?:`, то есть: `pattern:(?:go)+`.
Например:
```js run
var str = "Gogo John!";
*!*
var reg = /(?:go)+ (\w+)/i;
*/!*
var result = str.match(reg);
alert( result.length ); // 2
alert( result[1] ); // John
```
В примере выше массив результатов имеет длину `2` и содержит только полное совпадение и результат `pattern:(\w+)`. Это удобно в тех случаях, когда содержимое скобок нас не интересует.

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB