From 18fb1b0973bef5bdefc1a5959690a9ab3e54c132 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Sun, 19 Mar 2017 21:51:15 +0300 Subject: [PATCH] up --- .../1-find-webcolor-3-or-6/solution.md | 36 +++--- .../1-find-webcolor-3-or-6/task.md | 14 +-- .../09-regexp-groups/article.md | 116 +++++++++--------- 3 files changed, 85 insertions(+), 81 deletions(-) diff --git a/10-regular-expressions-javascript/09-regexp-groups/1-find-webcolor-3-or-6/solution.md b/10-regular-expressions-javascript/09-regexp-groups/1-find-webcolor-3-or-6/solution.md index e6af9289..443fdd85 100644 --- a/10-regular-expressions-javascript/09-regexp-groups/1-find-webcolor-3-or-6/solution.md +++ b/10-regular-expressions-javascript/09-regexp-groups/1-find-webcolor-3-or-6/solution.md @@ -1,29 +1,29 @@ -Регулярное выражение для поиска 3-значного цвета вида `#abc`: `pattern:/#[a-f0-9]{3}/i`. +A regexp to search 3-digit color `#abc`: `pattern:/#[a-f0-9]{3}/i`. -Нужно добавить ещё три символа, причём нужны именно три, четыре или семь символов не нужны. Эти три символа либо есть, либо нет. +We can add exactly 3 more optional hex digits. We don't need more or less. Either we have them or we don't. -Самый простой способ добавить -- просто дописать в конец регэкспа: `pattern:/#[a-f0-9]{3}([a-f0-9]{3})?/i` +The simplest way to add them -- is to append to the regexp: `pattern:/#[a-f0-9]{3}([a-f0-9]{3})?/i` -Можно поступить и хитрее: `pattern:/#([a-f0-9]{3}){1,2}/i`. +We can do it in a smarter way though: `pattern:/#([a-f0-9]{3}){1,2}/i`. -Здесь регэксп `pattern:[a-f0-9]{3}` заключён в скобки, чтобы квантификатор `pattern:{1,2}` применялся целиком ко всей этой структуре. +Here the regexp `pattern:[a-f0-9]{3}` is in parentheses to apply the quantifier `pattern:{1,2}` to it as a whole. -В действии: -```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`: +In action: ```js run -var re = /#([a-f0-9]{3}){1,2}\b/gi; +let reg = /#([a-f0-9]{3}){1,2}/gi; -var str = "color: #3f3; background-color: #AA00ef; and: #abcd"; +let str = "color: #3f3; background-color: #AA00ef; and: #abcd"; -alert( str.match(re) ); // #3f3 #AA0ef +alert( str.match(reg) ); // #3f3 #AA0ef #abc ``` +There's minor problem here: the pattern found `match:#abc` in `subject:#abcd`. To prevent that we can add `pattern:\b` to the end: + +```js run +let reg = /#([a-f0-9]{3}){1,2}\b/gi; + +let str = "color: #3f3; background-color: #AA00ef; and: #abcd"; + +alert( str.match(reg) ); // #3f3 #AA0ef +``` diff --git a/10-regular-expressions-javascript/09-regexp-groups/1-find-webcolor-3-or-6/task.md b/10-regular-expressions-javascript/09-regexp-groups/1-find-webcolor-3-or-6/task.md index 419c0476..c23097c0 100644 --- a/10-regular-expressions-javascript/09-regexp-groups/1-find-webcolor-3-or-6/task.md +++ b/10-regular-expressions-javascript/09-regexp-groups/1-find-webcolor-3-or-6/task.md @@ -1,14 +1,14 @@ -# Найдите цвет в формате #abc или #abcdef +# Find color in the format #abc or #abcdef -Напишите регулярное выражение, которое находит цвет в формате `#abc` или `#abcdef`. То есть, символ `#`, после которого идут 3 или 6 шестнадцатиричных символа. +Write a regexp that matches colors in the format `#abc` or `#abcdef`. That is: `#` followed by 3 or 6 hexadimal digits. -Пример использования: +Usage example: ```js -var re = /* ваш регэксп */ +let reg = /your regexp/g; -var str = "color: #3f3; background-color: #AA00ef; and: #abcd"; +let str = "color: #3f3; background-color: #AA00ef; and: #abcd"; -alert( str.match(re) ); // #3f3 #AA0ef +alert( str.match(reg) ); // #3f3 #AA0ef ``` -P.S. Значения из любого другого количества букв, кроме 3 и 6, такие как `#abcd`, не должны подходить под регэксп. \ No newline at end of file +P.S. Should be exactly 3 or 6 hex digits: values like `#abcd` should not match. diff --git a/10-regular-expressions-javascript/09-regexp-groups/article.md b/10-regular-expressions-javascript/09-regexp-groups/article.md index 685b4172..7ea81e74 100644 --- a/10-regular-expressions-javascript/09-regexp-groups/article.md +++ b/10-regular-expressions-javascript/09-regexp-groups/article.md @@ -1,6 +1,6 @@ # Bracket groups -A part of the pattern can be enclosed in parentheses `pattern:(...)`. That's called a "bracket expression" or a "bracket group". +A part of the pattern can be enclosed in parentheses `pattern:(...)`. That's called a "capturing group". That has two effects: @@ -11,129 +11,133 @@ That has two effects: ## Example -В примере ниже, шаблон `pattern:(go)+` находит один или более повторяющихся `pattern:'go'`: +In the example below the pattern `pattern:(go)+` finds one or more `match:'go'`: ```js run alert( 'Gogogo now!'.match(/(go)+/i) ); // "Gogogo" ``` -Без скобок, шаблон `pattern:/go+/` означал бы `subject:g`, после которого идёт одна или более `subject:o`, например: `match:goooo`. А скобки "группируют" `pattern:(go)` вместе. +Without parentheses, the pattern `pattern:/go+/` means `subject:g`, followed by `subject:o` repeated one or more times. For instance, `match:goooo` or `match:gooooooooo`. -## Содержимое группы +Parentheses group the word `pattern:(go)` together. -Скобки нумеруются слева направо. Поисковой движок запоминает содержимое каждой скобки и позволяет обращаться к нему -- в шаблоне и строке замены и, конечно же, в результатах. -Например, найти HTML-тег можно шаблоном `pattern:<.*?>`. +## Contents of parentheses -После поиска мы захотим что-то сделать с результатом. Для удобства заключим содержимое `<...>` в скобки: `pattern:<(.*?)>`. Тогда оно будет доступно отдельно. +Parentheses are numbered from left to right. The search engine remembers the content of each and allows to reference it in the pattern or in the replacement string. -При поиске методом [String#match](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/match) в результирующем массиве будет сначала всё совпадение, а далее -- скобочные группы. В шаблоне `pattern:<(.*?)>` скобочная группа только одна: +For instance, we can find an HTML-tag using a (simplified) pattern `pattern:<.*?>`. Usually we'd want to do something with the result after it. + +If we enclose the inner contents of `<...>` into parentheses, then we can access it like this: ```js run -var str = '

Привет, мир!

'; -var reg = /<(.*?)>/; +let str = '

Hello, world!

'; +let reg = /<(.*?)>/; -alert( str.match(reg) ); // массив:

, h1 +alert( str.match(reg) ); // Array: ["

", "h1"] ``` -Заметим, что метод [String#match](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/String/match) выдаёт скобочные группы только при поиске без флага `/.../g`. В примере выше он нашёл только первое совпадение `match:

`, а закрывающий `match:

` не нашёл, поскольку без флага `/.../g` ищется только первое совпадение. +The call to [String#match](mdn:js/String/match) returns groups only if the regexp has no `pattern:/.../g` flag. -Для того, чтобы искать и с флагом `/.../g` и со скобочными группами, используется метод [RegExp#exec](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec): +If we need all matches with their groups then we can use [RegExp#exec](mdn:js/RegExp/exec) method as described in : ```js run -var str = '

Привет, мир!

'; -var reg = /<(.*?)>/g; +let str = '

Hello, world!

'; -var match; +// two matches: opening

and closing

tags +let reg = /<(.*?)>/g; -while ((match = reg.exec(str)) !== null) { - // сначала выведет первое совпадение:

,h1 - // затем выведет второе совпадение:

,/h1 +let match; + +while (match = reg.exec(str)) { + // first shows the match:

,h1 + // then shows the match:

,/h1 alert(match); } ``` -Теперь найдено оба совпадения `pattern:<(.*?)>`, каждое -- массив из полного совпадения и скобочных групп (одна в данном случае). +Here we have two matches for `pattern:<(.*?)>`, each of them is an array with the full match and groups. -## Вложенные группы -Скобки могут быть и вложенными. В этом случае нумерация также идёт слева направо. +## Nested groups -Например, при поиске тега в `subject:` нас может интересовать: +Parentheses can be nested. In this case the numbering also goes from left to right. -1. Содержимое тега целиком: `span class="my"`. -2. В отдельную переменную для удобства хотелось бы поместить тег: `span`. -3. Также может быть удобно отдельно выделить атрибуты `class="my"`. +For instance, when searching a tag in `subject:` we may be interested in: -Добавим скобки в регулярное выражение: +1. The tag content as a whole: `match:span class="my"`. +2. The tag name: `match:span`. +3. The tag attributes: `match:class="my"`. + +Let's add parentheses for them: ```js run -var str = ''; +let str = ''; -var reg = /<(([a-z]+)\s*([^>]*))>/; +let reg = /<(([a-z]+)\s*([^>]*))>/; -alert( str.match(reg) ); // , span class="my", span, class="my" +let result = str.match(reg); +alert(result); // , span class="my", span, class="my" ``` -Вот так выглядят скобочные группы: +Here's how groups look: ![](regexp-nested-groups.png) -На нулевом месте -- всегда совпадение полностью, далее -- группы. Нумерация всегда идёт слева направо, по открывающей скобке. +At the zero index of the `result` is always the full match. -В данном случае получилось, что группа 1 включает в себя содержимое групп 2 и 3. Это совершенно нормальная ситуация, которая возникает, когда нужно выделить что-то отдельное внутри большей группы. +Then groups, numbered from left to right. Whichever opens first gives the first group `result[1]`. Here it encloses the whole tag content. -**Даже если скобочная группа необязательна и не входит в совпадение, соответствующий элемент массива существует (и равен `undefined`).** +Then in `result[2]` goes the group from the second opening `pattern:(` till the corresponding `pattern:)` -- tag name, then we don't group spaces, but group attributes for `result[3]`. -Например, рассмотрим регэксп `pattern:a(z)?(c)?`. Он ищет `"a"`, за которой не обязательно идёт буква `"z"`, за которой необязательно идёт буква `"c"`. +**If a group is optional and doesn't exist in the match, the corresponding `result` index is present (and equals `undefined`).** -Если напустить его на строку из одной буквы `"a"`, то результат будет таков: +For instance, let's consider the regexp `pattern:a(z)?(c)?`. It looks for `"a"` optionally followed by `"z"` optionally followed by `"c"`. + +If we run it on the string with a single letter `subject:a`, then the result is: ```js run -var match = 'a'.match(/a(z)?(c)?/) +let match = 'a'.match(/a(z)?(c)?/); alert( match.length ); // 3 -alert( match[0] ); // a +alert( match[0] ); // a (whole match) alert( match[1] ); // undefined alert( match[2] ); // undefined ``` -Массив получился длины `3`, но все скобочные группы -- `undefined`. +The array has the length of `3`, but all groups are empty. -А теперь более сложная ситуация, строка `subject:ack`: +And here's a more complex match for the string `subject:ack`: ```js run -var match = 'ack'.match(/a(z)?(c)?/) +let match = 'ack'.match(/a(z)?(c)?/) alert( match.length ); // 3 -alert( match[0] ); // ac, всё совпадение -alert( match[1] ); // undefined, для (z)? ничего нет +alert( match[0] ); // ac (whole match) +alert( match[1] ); // undefined, because there's nothing for (z)? alert( match[2] ); // c ``` -Длина массива результатов по-прежнему `3`. Она постоянна. А вот для скобочной группы `pattern:(z)?` в ней ничего нет, поэтому результат: `["ac", undefined, "c"]`. +The array length is permanent: `3`. But there's nothing for the group `pattern:(z)?`, so the result is `["ac", undefined, "c"]`. -## Исключение из запоминания через ?: +## Non-capturing groups with ?: -Бывает так, что скобки нужны, чтобы квантификатор правильно применился, а вот запоминать их содержимое в массиве не нужно. +Sometimes we need parentheses to correctly apply a quantifier, but we don't want their contents in the array. -Скобочную группу можно исключить из запоминаемых и нумеруемых, добавив в её начало `pattern:?:`. +A group may be excluded by adding `pattern:?:` in the beginning. -Например, мы хотим найти `pattern:(go)+`, но содержимое скобок (`go`) в отдельный элемент массива выделять не хотим. +For instance, if we want to find `pattern:(go)+`, but don't want to put remember the contents (`go`) in a separate array item, we can write: `pattern:(?:go)+`. -Для этого нужно сразу после открывающей скобки поставить `?:`, то есть: `pattern:(?:go)+`. - -Например: +In the example below we only get the name "John" as a separate member of the `results` array: ```js run -var str = "Gogo John!"; +let str = "Gogo John!"; *!* -var reg = /(?:go)+ (\w+)/i; +// exclude Gogo from capturing +let reg = /(?:go)+ (\w+)/i; */!* -var result = str.match(reg); +let result = str.match(reg); alert( result.length ); // 2 alert( result[1] ); // John ``` - -В примере выше массив результатов имеет длину `2` и содержит только полное совпадение и результат `pattern:(\w+)`. Это удобно в тех случаях, когда содержимое скобок нас не интересует.