From a9429d8fac4977eb3fc73a88aa3c466ce2fc65ff Mon Sep 17 00:00:00 2001 From: LuiGee3471 Date: Mon, 15 Jun 2020 11:15:53 +0900 Subject: [PATCH 01/34] Fixed Wikipedia link and list markdown --- 03-regexp-unicode/article.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/03-regexp-unicode/article.md b/03-regexp-unicode/article.md index fb8fed470..defbe15ab 100644 --- a/03-regexp-unicode/article.md +++ b/03-regexp-unicode/article.md @@ -93,7 +93,7 @@ Here's the main character categories and their subcategories: - control `Cc`, - format `Cf`, - not assigned `Cn`, - -- private use `Co`, + - private use `Co`, - surrogate `Cs`. @@ -127,7 +127,7 @@ alert("number: xAF".match(regexp)); // xAF Let's look for Chinese hieroglyphs. -There's a unicode property `Script` (a writing system), that may have a value: `Cyrillic`, `Greek`, `Arabic`, `Han` (Chinese) and so on, [here's the full list]("https://en.wikipedia.org/wiki/Script_(Unicode)"). +There's a unicode property `Script` (a writing system), that may have a value: `Cyrillic`, `Greek`, `Arabic`, `Han` (Chinese) and so on, [here's the full list](https://en.wikipedia.org/wiki/Script_(Unicode)). To look for characters in a given writing system we should use `pattern:Script=`, e.g. for Cyrillic letters: `pattern:\p{sc=Cyrillic}`, for Chinese hieroglyphs: `pattern:\p{sc=Han}`, and so on: From f4d4f7274221158bd47581cc0de7d36e42d03802 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Sun, 5 Jul 2020 12:45:21 +0300 Subject: [PATCH 02/34] minor fixes --- 06-regexp-boundary/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/06-regexp-boundary/article.md b/06-regexp-boundary/article.md index aad65877f..7c9f442fe 100644 --- a/06-regexp-boundary/article.md +++ b/06-regexp-boundary/article.md @@ -25,7 +25,7 @@ So, it matches the pattern `pattern:\bHello\b`, because: 1. At the beginning of the string matches the first test `pattern:\b`. 2. Then matches the word `pattern:Hello`. -3. Then the test `pattern:\b` matches again, as we're between `subject:o` and a space. +3. Then the test `pattern:\b` matches again, as we're between `subject:o` and a comma. The pattern `pattern:\bHello\b` would also match. But not `pattern:\bHell\b` (because there's no word boundary after `l`) and not `Java!\b` (because the exclamation sign is not a wordly character `pattern:\w`, so there's no word boundary after it). From 4928b9c41153145764b673dcc5b974c1f10a8ce2 Mon Sep 17 00:00:00 2001 From: josephrocca <1167575+josephrocca@users.noreply.github.com> Date: Fri, 21 Aug 2020 11:23:42 +1000 Subject: [PATCH 03/34] Remove Edge and Firefox warning Supported in Edge 79 and Firefox 78 --- 03-regexp-unicode/article.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/03-regexp-unicode/article.md b/03-regexp-unicode/article.md index defbe15ab..60d85ff13 100644 --- a/03-regexp-unicode/article.md +++ b/03-regexp-unicode/article.md @@ -33,12 +33,6 @@ Unlike strings, regular expressions have flag `pattern:u` that fixes such proble ## Unicode properties \p{...} -```warn header="Not supported in Firefox and Edge" -Despite being a part of the standard since 2018, unicode properties are not supported in Firefox ([bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1361876)) and Edge ([bug](https://github.com/Microsoft/ChakraCore/issues/2969)). - -There's [XRegExp](http://xregexp.com) library that provides "extended" regular expressions with cross-browser support for unicode properties. -``` - Every character in Unicode has a lot of properties. They describe what "category" the character belongs to, contain miscellaneous information about it. For instance, if a character has `Letter` property, it means that the character belongs to an alphabet (of any language). And `Number` property means that it's a digit: maybe Arabic or Chinese, and so on. From bcbe28b49ba2eff754116026f987e3905de029ba Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Tue, 6 Oct 2020 00:47:35 +0300 Subject: [PATCH 04/34] minor fixes --- .../article.md | 129 ++++++++++-------- 1 file changed, 73 insertions(+), 56 deletions(-) diff --git a/15-regexp-catastrophic-backtracking/article.md b/15-regexp-catastrophic-backtracking/article.md index 59467f938..c66a1a219 100644 --- a/15-regexp-catastrophic-backtracking/article.md +++ b/15-regexp-catastrophic-backtracking/article.md @@ -1,20 +1,20 @@ # Catastrophic backtracking -Some regular expressions are looking simple, but can execute veeeeeery long time, and even "hang" the JavaScript engine. +Some regular expressions are looking simple, but can execute a veeeeeery long time, and even "hang" the JavaScript engine. -Sooner or later most developers occasionally face such behavior, because it's quite easy to create such a regexp. - -The typical symptom -- a regular expression works fine sometimes, but for certain strings it "hangs", consuming 100% of CPU. +Sooner or later most developers occasionally face such behavior. The typical symptom -- a regular expression works fine sometimes, but for certain strings it "hangs", consuming 100% of CPU. In such case a web-browser suggests to kill the script and reload the page. Not a good thing for sure. -For server-side JavaScript it may become a vulnerability if regular expressions process user data. +For server-side JavaScript such a regexp may hang the server process, that's even worse. So we definitely should take a look at it. ## Example -Let's say we have a string, and we'd like to check if it consists of words `pattern:\w+` with an optional space `pattern:\s?` after each. +Let's say we have a string, and we'd like to check if it consists of words `pattern:\w+` with an optional space `pattern:\s?` after each. + +An obvious way to construct a regexp would be to take a word followed by an optional space `pattern:\w+\s?` and then repeat it with `*`. -We'll use a regexp `pattern:^(\w+\s?)*$`, it specifies 0 or more such words. +That leads us to the regexp `pattern:^(\w+\s?)*$`, it specifies zero or more such words, that start at the beginning `pattern:^` and finish at the end `pattern:$` of the line. In action: @@ -25,9 +25,9 @@ alert( regexp.test("A good string") ); // true alert( regexp.test("Bad characters: $@#") ); // false ``` -It seems to work. The result is correct. Although, on certain strings it takes a lot of time. So long that JavaScript engine "hangs" with 100% CPU consumption. +The regexp seems to work. The result is correct. Although, on certain strings it takes a lot of time. So long that JavaScript engine "hangs" with 100% CPU consumption. -If you run the example below, you probably won't see anything, as JavaScript will just "hang". A web-browser will stop reacting on events, the UI will stop working. After some time it will suggest to reloaad the page. So be careful with this: +If you run the example below, you probably won't see anything, as JavaScript will just "hang". A web-browser will stop reacting on events, the UI will stop working (most browsers allow only scrolling). After some time it will suggest to reload the page. So be careful with this: ```js run let regexp = /^(\w+\s?)*$/; @@ -37,24 +37,22 @@ let str = "An input string that takes a long time or even makes this regexp to h alert( regexp.test(str) ); ``` -Some regular expression engines can handle such search, but most of them can't. +To be fair, let's note that some regular expression engines can handle such a search effectively. But most of them can't. Browser engines usually hang. ## Simplified example -What's the matter? Why the regular expression "hangs"? +What's the matter? Why the regular expression hangs? To understand that, let's simplify the example: remove spaces `pattern:\s?`. Then it becomes `pattern:^(\w+)*$`. And, to make things more obvious, let's replace `pattern:\w` with `pattern:\d`. The resulting regular expression still hangs, for instance: - - ```js run let regexp = /^(\d+)*$/; -let str = "012345678901234567890123456789!"; +let str = "012345678901234567890123456789z"; -// will take a very long time +// will take a very long time (careful!) alert( regexp.test(str) ); ``` @@ -62,45 +60,49 @@ So what's wrong with the regexp? First, one may notice that the regexp `pattern:(\d+)*` is a little bit strange. The quantifier `pattern:*` looks extraneous. If we want a number, we can use `pattern:\d+`. -Indeed, the regexp is artificial. But the reason why it is slow is the same as those we saw above. So let's understand it, and then the previous example will become obvious. +Indeed, the regexp is artificial, we got it by simplifying the previous example. But the reason why it is slow is the same. So let's understand it, and then the previous example will become obvious. -What happens during the search of `pattern:^(\d+)*$` in the line `subject:123456789!` (shortened a bit for clarity), why does it take so long? +What happens during the search of `pattern:^(\d+)*$` in the line `subject:123456789z` (shortened a bit for clarity, please note a non-digit character `subject:z` at the end, it's important), why does it take so long? -1. First, the regexp engine tries to find a number `pattern:\d+`. The plus `pattern:+` is greedy by default, so it consumes all digits: +Here's what the regexp engine does: + +1. First, the regexp engine tries to find the content of the parentheses: the number `pattern:\d+`. The plus `pattern:+` is greedy by default, so it consumes all digits: ``` \d+....... (123456789)z ``` - Then it tries to apply the star quantifier, but there are no more digits, so it the star doesn't give anything. + After all digits are consumed, `pattern:\d+` is considered found (as `match:123456789`). + + Then the star quantifier `pattern:(\d+)*` applies. But there are no more digits in the text, so the star doesn't give anything. - The next in the pattern is the string end `pattern:$`, but in the text we have `subject:!`, so there's no match: + The next character in the pattern is the string end `pattern:$`. But in the text we have `subject:z` instead, so there's no match: ``` X \d+........$ - (123456789)! + (123456789)z ``` 2. As there's no match, the greedy quantifier `pattern:+` decreases the count of repetitions, backtracks one character back. - Now `pattern:\d+` takes all digits except the last one: + Now `pattern:\d+` takes all digits except the last one (`match:12345678`): ``` \d+....... - (12345678)9! + (12345678)9z ``` -3. Then the engine tries to continue the search from the new position (`9`). +3. Then the engine tries to continue the search from the next position (right after `match:12345678`). - The star `pattern:(\d+)*` can be applied -- it gives the number `match:9`: + The star `pattern:(\d+)*` can be applied -- it gives one more match of `pattern:\d+`, the number `match:9`: ``` \d+.......\d+ - (12345678)(9)! + (12345678)(9)z ``` - The engine tries to match `pattern:$` again, but fails, because meets `subject:!`: + The engine tries to match `pattern:$` again, but fails, because it meets `subject:z` instead: ``` X @@ -118,7 +120,7 @@ What happens during the search of `pattern:^(\d+)*$` in the line `subject:123456 ``` X \d+......\d+ - (1234567)(89)! + (1234567)(89)z ``` The first number has 7 digits, and then two numbers of 1 digit each: @@ -126,7 +128,7 @@ What happens during the search of `pattern:^(\d+)*$` in the line `subject:123456 ``` X \d+......\d+\d+ - (1234567)(8)(9)! + (1234567)(8)(9)z ``` The first number has 6 digits, and then a number of 3 digits: @@ -134,7 +136,7 @@ What happens during the search of `pattern:^(\d+)*$` in the line `subject:123456 ``` X \d+.......\d+ - (123456)(789)! + (123456)(789)z ``` The first number has 6 digits, and then 2 numbers: @@ -142,23 +144,19 @@ What happens during the search of `pattern:^(\d+)*$` in the line `subject:123456 ``` X \d+.....\d+ \d+ - (123456)(78)(9)! + (123456)(78)(9)z ``` ...And so on. -There are many ways to split a set of digits `123456789` into numbers. To be precise, there are 2n-1, where `n` is the length of the set. +There are many ways to split a sequence of digits `123456789` into numbers. To be precise, there are 2n-1, where `n` is the length of the sequence. -For `n=20` there are about 1 million combinations, for `n=30` - a thousand times more. Trying each of them is exactly the reason why the search takes so long. +- For `123456789` we have `n=9`, that gives 511 combinations. +- For a longer sequence with `n=20` there are about one million (1048575) combinations. +- For `n=30` - a thousand times more (1073741823 combinations). -What to do? - -Should we turn on the lazy mode? - -Unfortunately, that won't help: if we replace `pattern:\d+` with `pattern:\d+?`, the regexp will still hang. The order of combinations will change, but not their total count. - -Some regular expression engines have tricky tests and finite automations that allow to avoid going through all combinations or make it much faster, but not all engines, and not in all cases. +Trying each of them is exactly the reason why the search takes so long. ## Back to words and strings @@ -176,7 +174,15 @@ The reason is that a word can be represented as one `pattern:\w+` or many: For a human, it's obvious that there may be no match, because the string ends with an exclamation sign `!`, but the regular expression expects a wordly character `pattern:\w` or a space `pattern:\s` at the end. But the engine doesn't know that. -It tries all combinations of how the regexp `pattern:(\w+\s?)*` can "consume" the string, including variants with spaces `pattern:(\w+\s)*` and without them `pattern:(\w+)*` (because spaces `pattern:\s?` are optional). As there are many such combinations, the search takes a lot of time. +It tries all combinations of how the regexp `pattern:(\w+\s?)*` can "consume" the string, including variants with spaces `pattern:(\w+\s)*` and without them `pattern:(\w+)*` (because spaces `pattern:\s?` are optional). As there are many such combinations (we've seen it with digits), the search takes a lot of time. + +What to do? + +Should we turn on the lazy mode? + +Unfortunately, that won't help: if we replace `pattern:\w+` with `pattern:\w+?`, the regexp will still hang. The order of combinations will change, but not their total count. + +Some regular expression engines have tricky tests and finite automations that allow to avoid going through all combinations or make it much faster, but most engines don't, and it doesn't always help. ## How to fix? @@ -184,7 +190,7 @@ There are two main approaches to fixing the problem. The first is to lower the number of possible combinations. -Let's rewrite the regular expression as `pattern:^(\w+\s)*\w*` - we'll look for any number of words followed by a space `pattern:(\w+\s)*`, and then (optionally) a word `pattern:\w*`. +Let's make the space non-optional by rewriting the regular expression as `pattern:^(\w+\s)*\w*$` - we'll look for any number of words followed by a space `pattern:(\w+\s)*`, and then (optionally) a final word `pattern:\w*`. This regexp is equivalent to the previous one (matches the same) and works well: @@ -197,26 +203,30 @@ alert( regexp.test(str) ); // false Why did the problem disappear? -Now the star `pattern:*` goes after `pattern:\w+\s` instead of `pattern:\w+\s?`. It became impossible to represent one word of the string with multiple successive `pattern:\w+`. The time needed to try such combinations is now saved. +That's because now the space is mandatory. -For example, the previous pattern `pattern:(\w+\s?)*` could match the word `subject:string` as two `pattern:\w+`: +The previous regexp, if we omit the space, becomes `pattern:(\w+)*`, leading to many combinations of `\w+` within a single word + +So `subject:input` could be matched as two repetitions of `pattern:\w+`, like this: -```js run -\w+\w+ -string +``` +\w+ \w+ +(inp)(ut) ``` -The previous pattern, due to the optional `pattern:\s` allowed variants `pattern:\w+`, `pattern:\w+\s`, `pattern:\w+\w+` and so on. +The new pattern is different: `pattern:(\w+\s)*` specifies repetitions of words followed by a space! The `subject:input` string can't be matched as two repetitions of `pattern:\w+\s`, because the space is mandatory. -With the rewritten pattern `pattern:(\w+\s)*`, that's impossible: there may be `pattern:\w+\s` or `pattern:\w+\s\w+\s`, but not `pattern:\w+\w+`. So the overall combinations count is greatly decreased. +The time needed to try a lot of (actually most of) combinations is now saved. ## Preventing backtracking -It's not always convenient to rewrite a regexp. And it's not always obvious how to do it. +It's not always convenient to rewrite a regexp though. In the example above it was easy, but it's not always obvious how to do it. + +Besides, a rewritten regexp is usually more complex, and that's not good. Regexps are complex enough without extra efforts. -The alternative approach is to forbid backtracking for the quantifier. +Luckily, there's an alternative approach. We can forbid backtracking for the quantifier. -The regular expressions engine tries many combinations that are obviously wrong for a human. +The root of the problem is that the regexp engine tries many combinations that are obviously wrong for a human. E.g. in the regexp `pattern:(\d+)*$` it's obvious for a human, that `pattern:+` shouldn't backtrack. If we replace one `pattern:\d+` with two separate `pattern:\d+\d+`, nothing changes: @@ -230,19 +240,26 @@ E.g. in the regexp `pattern:(\d+)*$` it's obvious for a human, that `pattern:+` And in the original example `pattern:^(\w+\s?)*$` we may want to forbid backtracking in `pattern:\w+`. That is: `pattern:\w+` should match a whole word, with the maximal possible length. There's no need to lower the repetitions count in `pattern:\w+`, try to split it into two words `pattern:\w+\w+` and so on. -Modern regular expression engines support possessive quantifiers for that. They are like greedy ones, but don't backtrack (so they are actually simpler than regular quantifiers). +Modern regular expression engines support possessive quantifiers for that. Regular quantifiers become possessive if we add `pattern:+` after them. That is, we use `pattern:\d++` instead of `pattern:\d+` to stop `pattern:+` from backtracking. + +Possessive quantifiers are in fact simpler than "regular" ones. They just match as many as they can, without any backtracking. The search process without bracktracking is simpler. There are also so-called "atomic capturing groups" - a way to disable backtracking inside parentheses. -Unfortunately, in JavaScript they are not supported. But there's another way. +...But the bad news is that, unfortunately, in JavaScript they are not supported. + +We can emulate them though using a "lookahead transform". ### Lookahead to the rescue! -We can prevent backtracking using lookahead. +So we've come to real advanced topics. We'd like a quantifier, such as `pattern:+` not to backtrack, because sometimes backtracking makes no sense. + +The pattern to take as much repetitions of `pattern:\w` as possible without backtracking is: `pattern:(?=(\w+))\1`. Of course, we could take another pattern instead of `pattern:\w`. -The pattern to take as much repetitions of `pattern:\w` as possible without backtracking is: `pattern:(?=(\w+))\1`. +That may seem odd, but it's actually a very simple transform. Let's decipher it: + - Lookahead `pattern:?=` looks forward for the longest word `pattern:\w+` starting at the current position. - The contents of parentheses with `pattern:?=...` isn't memorized by the engine, so wrap `pattern:\w+` into parentheses. Then the engine will memorize their contents - ...And allow us to reference it in the pattern as `pattern:\1`. From cd9314dbfb72af4a116a832ec71b17a5e9df6c53 Mon Sep 17 00:00:00 2001 From: joaquinelio Date: Thu, 15 Oct 2020 19:48:47 -0300 Subject: [PATCH 05/34] redundant "use case" --- 16-regexp-sticky/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/16-regexp-sticky/article.md b/16-regexp-sticky/article.md index f3650c916..31c87fc0d 100644 --- a/16-regexp-sticky/article.md +++ b/16-regexp-sticky/article.md @@ -3,7 +3,7 @@ The flag `pattern:y` allows to perform the search at the given position in the source string. -To grasp the use case of `pattern:y` flag, and see how great it is, let's explore a practical use case. +To grasp the use case of `pattern:y` flag, and see how great it is, let's explore a practical example. One of common tasks for regexps is "lexical analysis": we get a text, e.g. in a programming language, and analyze it for structural elements. From e62c44cdc506f7d5ed326b4f55dc8ffaf5e7ab64 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Fri, 16 Oct 2020 10:07:28 +0300 Subject: [PATCH 06/34] minor fixes --- 16-regexp-sticky/article.md | 57 ++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/16-regexp-sticky/article.md b/16-regexp-sticky/article.md index 31c87fc0d..161ce4dd8 100644 --- a/16-regexp-sticky/article.md +++ b/16-regexp-sticky/article.md @@ -3,11 +3,9 @@ The flag `pattern:y` allows to perform the search at the given position in the source string. -To grasp the use case of `pattern:y` flag, and see how great it is, let's explore a practical example. +To grasp the use case of `pattern:y` flag, and better understand the ways of regexps, let's explore a practical example. -One of common tasks for regexps is "lexical analysis": we get a text, e.g. in a programming language, and analyze it for structural elements. - -For instance, HTML has tags and attributes, JavaScript code has functions, variables, and so on. +One of common tasks for regexps is "lexical analysis": we get a text, e.g. in a programming language, and need to find its structural elements. For instance, HTML has tags and attributes, JavaScript code has functions, variables, and so on. Writing lexical analyzers is a special area, with its own tools and algorithms, so we don't go deep in there, but there's a common task: to read something at the given position. @@ -15,24 +13,27 @@ E.g. we have a code string `subject:let varName = "value"`, and we need to read We'll look for variable name using regexp `pattern:\w+`. Actually, JavaScript variable names need a bit more complex regexp for accurate matching, but here it doesn't matter. -A call to `str.match(/\w+/)` will find only the first word in the line. Or all words with the flag `pattern:g`. But we need only one word at position `4`. +- A call to `str.match(/\w+/)` will find only the first word in the line (`var`). That's not it. +- We can add the flag `pattern:g`. But then the call `str.match(/\w+/g)` will look for all words in the text, while we need one word at position `4`. Again, not what we need. + +**So, how to search for a regexp exactly at the given position?** -To search from the given position, we can use method `regexp.exec(str)`. +Let's try using method `regexp.exec(str)`. -If the `regexp` doesn't have flags `pattern:g` or `pattern:y`, then this method looks for the first match in the string `str`, exactly like `str.match(regexp)`. Such simple no-flags case doesn't interest us here. +For a `regexp` without flags `pattern:g` and `pattern:y`, this method looks only for the first match, it works exactly like `str.match(regexp)`. -If there's flag `pattern:g`, then it performs the search in the string `str`, starting from position stored in its `regexp.lastIndex` property. And, if it finds a match, then sets `regexp.lastIndex` to the index immediately after the match. +...But if there's flag `pattern:g`, then it performs the search in `str`, starting from position stored in the `regexp.lastIndex` property. And, if it finds a match, then sets `regexp.lastIndex` to the index immediately after the match. -When a regexp is created, its `lastIndex` is `0`. +In other words, `regexp.lastIndex` serves as a starting point for the search, that each `regexp.exec(str)` call resets to the new value ("after the last match"). That's only if there's `pattern:g` flag, of course. So, successive calls to `regexp.exec(str)` return matches one after another. -An example (with flag `pattern:g`): +Here's an example of such calls: ```js run -let str = 'let varName'; - +let str = 'let varName'; // Let's find all words in this string let regexp = /\w+/g; + alert(regexp.lastIndex); // 0 (initially lastIndex=0) let word1 = regexp.exec(str); @@ -48,8 +49,6 @@ alert(word3); // null (no more matches) alert(regexp.lastIndex); // 0 (resets at search end) ``` -Every match is returned as an array with groups and additional properties. - We can get all matches in the loop: ```js run @@ -65,11 +64,13 @@ while (result = regexp.exec(str)) { } ``` -Such use of `regexp.exec` is an alternative to method `str.matchAll`. +Such use of `regexp.exec` is an alternative to method `str.matchAll`, with a bit more control over the process. -Unlike other methods, we can set our own `lastIndex`, to start the search from the given position. +Let's go back to our task. -For instance, let's find a word, starting from position `4`: +We can manually set `lastIndex` to `4`, to start the search from the given position! + +Like this: ```js run let str = 'let varName = "value"'; @@ -84,9 +85,15 @@ let word = regexp.exec(str); alert(word); // varName ``` +Hooray! Problem solved! + We performed a search of `pattern:\w+`, starting from position `regexp.lastIndex = 4`. -Please note: the search starts at position `lastIndex` and then goes further. If there's no word at position `lastIndex`, but it's somewhere after it, then it will be found: +The result is correct. + +...But wait, not so fast. + +Please note: the `regexp.exec` call start searching at position `lastIndex` and then goes further. If there's no word at position `lastIndex`, but it's somewhere after it, then it will be found: ```js run let str = 'let varName = "value"'; @@ -94,17 +101,19 @@ let str = 'let varName = "value"'; let regexp = /\w+/g; *!* +// start the search from position 3 regexp.lastIndex = 3; */!* -let word = regexp.exec(str); +let word = regexp.exec(str); +// found the match at position 4 alert(word[0]); // varName alert(word.index); // 4 ``` -...So, with flag `pattern:g` property `lastIndex` sets the starting position for the search. +For some tasks, including the lexical analysis, that's just wrong. We need to find a match exactly at the given position at the text, not somewhere after it. And that's what the flag `y` is for. -**Flag `pattern:y` makes `regexp.exec` to look exactly at position `lastIndex`, not before, not after it.** +**The flag `pattern:y` makes `regexp.exec` to search exactly at position `lastIndex`, not "starting from" it.** Here's the same search with flag `pattern:y`: @@ -122,6 +131,8 @@ alert( regexp.exec(str) ); // varName (word at position 4) As we can see, regexp `pattern:/\w+/y` doesn't match at position `3` (unlike the flag `pattern:g`), but matches at position `4`. -Imagine, we have a long text, and there are no matches in it, at all. Then searching with flag `pattern:g` will go till the end of the text, and this will take significantly more time than the search with flag `pattern:y`. +Not only that's what we need, there's an important performance gain when using flag `pattern:y`. + +Imagine, we have a long text, and there are no matches in it, at all. Then a search with flag `pattern:g` will go till the end of the text and find nothing, and this will take significantly more time than the search with flag `pattern:y`, that checks only the exact position. -In such tasks like lexical analysis, there are usually many searches at an exact position. Using flag `pattern:y` is the key for a good performance. +In tasks like lexical analysis, there are usually many searches at an exact position, to check what we have there. Using flag `pattern:y` is the key for correct implementations and a good performance. From 9543c737bdfbfd05dff9ed561445dafc51a421dc Mon Sep 17 00:00:00 2001 From: start <39118555+plakxj@users.noreply.github.com> Date: Tue, 27 Oct 2020 16:18:32 +0800 Subject: [PATCH 07/34] Update article.md the match position is 7 --- 17-regexp-methods/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/17-regexp-methods/article.md b/17-regexp-methods/article.md index e4044361f..a7847c0f6 100644 --- a/17-regexp-methods/article.md +++ b/17-regexp-methods/article.md @@ -20,7 +20,7 @@ It has 3 modes: alert( result.length ); // 2 // Additional information: - alert( result.index ); // 0 (match position) + alert( result.index ); // 7 (match position) alert( result.input ); // I love JavaScript (source string) ``` From fe9635bfaab0d9bffd30e8afe696cece53cf7f9a Mon Sep 17 00:00:00 2001 From: Osvaldo Dias dos Santos Date: Sat, 31 Oct 2020 05:59:18 +0100 Subject: [PATCH 08/34] Fix typo. --- 02-regexp-character-classes/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/02-regexp-character-classes/article.md b/02-regexp-character-classes/article.md index 7baa6984b..93552a2b0 100644 --- a/02-regexp-character-classes/article.md +++ b/02-regexp-character-classes/article.md @@ -120,7 +120,7 @@ alert( "CS-4".match(regexp) ); // CS-4 alert( "CS 4".match(regexp) ); // CS 4 (space is also a character) ``` -Please note that a dot means "any character", but not the "absense of a character". There must be a character to match it: +Please note that a dot means "any character", but not the "absence of a character". There must be a character to match it: ```js run alert( "CS4".match(/CS.4/) ); // null, no match because there's no character for the dot From e09706a8687dba94caaf9b61258a147f7eb4e72a Mon Sep 17 00:00:00 2001 From: Will Atwood Mitchell Date: Sun, 1 Nov 2020 14:36:43 -0500 Subject: [PATCH 09/34] Change `var` to `let` in 7.16 regexp-sticky MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 10d1b1f25 added a hint about which word would be found by a certain regexp: "A call to str.match(/\w+/) will find only the first word in the line (var). That’s not it." Unfortunately the result is incorrect: the regexp would find `let`, not `var`. Updating the hint should resolve any confusion. The example has been tested in the console to ensure that the regexp does indeed return `let`. --- 16-regexp-sticky/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/16-regexp-sticky/article.md b/16-regexp-sticky/article.md index 161ce4dd8..373f3453e 100644 --- a/16-regexp-sticky/article.md +++ b/16-regexp-sticky/article.md @@ -13,7 +13,7 @@ E.g. we have a code string `subject:let varName = "value"`, and we need to read We'll look for variable name using regexp `pattern:\w+`. Actually, JavaScript variable names need a bit more complex regexp for accurate matching, but here it doesn't matter. -- A call to `str.match(/\w+/)` will find only the first word in the line (`var`). That's not it. +- A call to `str.match(/\w+/)` will find only the first word in the line (`let`). That's not it. - We can add the flag `pattern:g`. But then the call `str.match(/\w+/g)` will look for all words in the text, while we need one word at position `4`. Again, not what we need. **So, how to search for a regexp exactly at the given position?** From 5c492dac3eccc4859150f9cd2026b49556a32886 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Wed, 18 Nov 2020 10:37:38 +0800 Subject: [PATCH 10/34] FIX: minor typo error, missing "alert( " --- 10-regexp-greedy-and-lazy/1-lazy-greedy/task.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/10-regexp-greedy-and-lazy/1-lazy-greedy/task.md b/10-regexp-greedy-and-lazy/1-lazy-greedy/task.md index b46f55917..596f61a4e 100644 --- a/10-regexp-greedy-and-lazy/1-lazy-greedy/task.md +++ b/10-regexp-greedy-and-lazy/1-lazy-greedy/task.md @@ -3,5 +3,5 @@ What's the match here? ```js -"123 456".match(/\d+? \d+?/g) ); // ? +alert( "123 456".match(/\d+? \d+?/g) ); // ? ``` From 2b1d67040090dc9937f8a3214ac2083adc0657df Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Fri, 4 Dec 2020 18:15:41 +0300 Subject: [PATCH 11/34] selection --- 03-regexp-unicode/article.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/03-regexp-unicode/article.md b/03-regexp-unicode/article.md index 60d85ff13..0e0fb88ae 100644 --- a/03-regexp-unicode/article.md +++ b/03-regexp-unicode/article.md @@ -41,13 +41,13 @@ We can search for characters with a property, written as `pattern:\p{…}`. To u For instance, `\p{Letter}` denotes a letter in any of language. We can also use `\p{L}`, as `L` is an alias of `Letter`. There are shorter aliases for almost every property. -In the example below three kinds of letters will be found: English, Georgean and Korean. +In the example below three kinds of letters will be found: English, Georgian and Korean. ```js run let str = "A ბ ㄱ"; alert( str.match(/\p{L}/gu) ); // A,ბ,ㄱ -alert( str.match(/\p{L}/g) ); // null (no matches, as there's no flag "u") +alert( str.match(/\p{L}/g) ); // null (no matches, \p doesn't work without the flag "u") ``` Here's the main character categories and their subcategories: From f19b492f9a3437c87ada9210b803bbd6b3dcfa06 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Sat, 5 Dec 2020 19:50:31 +0300 Subject: [PATCH 12/34] minor fixes --- 15-regexp-catastrophic-backtracking/article.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/15-regexp-catastrophic-backtracking/article.md b/15-regexp-catastrophic-backtracking/article.md index c66a1a219..85343bf84 100644 --- a/15-regexp-catastrophic-backtracking/article.md +++ b/15-regexp-catastrophic-backtracking/article.md @@ -31,7 +31,7 @@ If you run the example below, you probably won't see anything, as JavaScript wil ```js run let regexp = /^(\w+\s?)*$/; -let str = "An input string that takes a long time or even makes this regexp to hang!"; +let str = "An input string that takes a long time or even makes this regexp hang!"; // will take a very long time alert( regexp.test(str) ); @@ -41,7 +41,7 @@ To be fair, let's note that some regular expression engines can handle such a se ## Simplified example -What's the matter? Why the regular expression hangs? +What's the matter? Why does the regular expression hang? To understand that, let's simplify the example: remove spaces `pattern:\s?`. Then it becomes `pattern:^(\w+)*$`. @@ -60,7 +60,7 @@ So what's wrong with the regexp? First, one may notice that the regexp `pattern:(\d+)*` is a little bit strange. The quantifier `pattern:*` looks extraneous. If we want a number, we can use `pattern:\d+`. -Indeed, the regexp is artificial, we got it by simplifying the previous example. But the reason why it is slow is the same. So let's understand it, and then the previous example will become obvious. +Indeed, the regexp is artificial; we got it by simplifying the previous example. But the reason why it is slow is the same. So let's understand it, and then the previous example will become obvious. What happens during the search of `pattern:^(\d+)*$` in the line `subject:123456789z` (shortened a bit for clarity, please note a non-digit character `subject:z` at the end, it's important), why does it take so long? @@ -111,7 +111,7 @@ Here's what the regexp engine does: ``` -4. There's no match, so the engine will continue backtracking, decreasing the number of repetitions. Backtracking generally works like this: the last greedy quantifier decreases the number of repetitions until it can. Then the previous greedy quantifier decreases, and so on. +4. There's no match, so the engine will continue backtracking, decreasing the number of repetitions. Backtracking generally works like this: the last greedy quantifier decreases the number of repetitions until it reaches the minimum. Then the previous greedy quantifier decreases, and so on. All possible combinations are attempted. Here are their examples. @@ -196,7 +196,7 @@ This regexp is equivalent to the previous one (matches the same) and works well: ```js run let regexp = /^(\w+\s)*\w*$/; -let str = "An input string that takes a long time or even makes this regex to hang!"; +let str = "An input string that takes a long time or even makes this regex hang!"; alert( regexp.test(str) ); // false ``` @@ -254,7 +254,7 @@ We can emulate them though using a "lookahead transform". So we've come to real advanced topics. We'd like a quantifier, such as `pattern:+` not to backtrack, because sometimes backtracking makes no sense. -The pattern to take as much repetitions of `pattern:\w` as possible without backtracking is: `pattern:(?=(\w+))\1`. Of course, we could take another pattern instead of `pattern:\w`. +The pattern to take as many repetitions of `pattern:\w` as possible without backtracking is: `pattern:(?=(\w+))\1`. Of course, we could take another pattern instead of `pattern:\w`. That may seem odd, but it's actually a very simple transform. @@ -293,7 +293,7 @@ let regexp = /^((?=(\w+))\2\s?)*$/; alert( regexp.test("A good string") ); // true -let str = "An input string that takes a long time or even makes this regex to hang!"; +let str = "An input string that takes a long time or even makes this regex hang!"; alert( regexp.test(str) ); // false, works and fast! ``` @@ -304,7 +304,7 @@ Here `pattern:\2` is used instead of `pattern:\1`, because there are additional // parentheses are named ?, referenced as \k let regexp = /^((?=(?\w+))\k\s?)*$/; -let str = "An input string that takes a long time or even makes this regex to hang!"; +let str = "An input string that takes a long time or even makes this regex hang!"; alert( regexp.test(str) ); // false From 2af5586190fc1f6f82cffb785ac674ce49803adb Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Mon, 7 Dec 2020 17:25:36 +0200 Subject: [PATCH 13/34] Fix typo in 9.1 (Patterns and flags) --- 01-regexp-introduction/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/01-regexp-introduction/article.md b/01-regexp-introduction/article.md index a35d19a7b..1db3f5f9a 100644 --- a/01-regexp-introduction/article.md +++ b/01-regexp-introduction/article.md @@ -29,7 +29,7 @@ In both cases `regexp` becomes an instance of the built-in `RegExp` class. The main difference between these two syntaxes is that pattern using slashes `/.../` does not allow for expressions to be inserted (like string template literals with `${...}`). They are fully static. -Slashes are used when we know the regular expression at the code writing time -- and that's the most common situation. While `new RegExp`, is more often used when we need to create a regexp "on the fly" from a dynamically generated string. For instance: +Slashes are used when we know the regular expression at the code writing time -- and that's the most common situation. While `new RegExp` is more often used when we need to create a regexp "on the fly" from a dynamically generated string. For instance: ```js let tag = prompt("What tag do you want to find?", "h2"); From 0e5c20deba31b02d828fbe68f9b2c49db07e1327 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Mon, 7 Dec 2020 17:42:51 +0200 Subject: [PATCH 14/34] Fix some issues in 9.2 (Character classes) * Fix typos. * Update a support note. * Add a reference for a yet unexplained pattern. --- 02-regexp-character-classes/article.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/02-regexp-character-classes/article.md b/02-regexp-character-classes/article.md index 93552a2b0..201c78a05 100644 --- a/02-regexp-character-classes/article.md +++ b/02-regexp-character-classes/article.md @@ -8,7 +8,7 @@ A *character class* is a special notation that matches any symbol from a certain For the start, let's explore the "digit" class. It's written as `pattern:\d` and corresponds to "any single digit". -For instance, the let's find the first digit in the phone number: +For instance, let's find the first digit in the phone number: ```js run let str = "+7(903)-123-45-67"; @@ -144,10 +144,10 @@ That's what flag `pattern:s` does. If a regexp has it, then a dot `pattern:.` ma alert( "A\nB".match(/A.B/s) ); // A\nB (match!) ``` -````warn header="Not supported in Firefox, IE, Edge" -Check for the most recent state of support. At the time of writing it doesn't include Firefox, IE, Edge. +````warn header="Not supported in IE" +The `pattern:s` flag is not supported in IE. -Luckily, there's an alternative, that works everywhere. We can use a regexp like `pattern:[\s\S]` to match "any character". +Luckily, there's an alternative, that works everywhere. We can use a regexp like `pattern:[\s\S]` to match "any character" (this pattern will be covered in the article ). ```js run alert( "A\nB".match(/A[\s\S]B/) ); // A\nB (match!) @@ -179,7 +179,7 @@ alert( "1 - 5".match(/\d\s-\s\d/) ); // 1 - 5, also works **A space is a character. Equal in importance with any other character.** -We can't add or remove spaces from a regular expression and expect to work the same. +We can't add or remove spaces from a regular expression and expect it to work the same. In other words, in a regular expression all characters matter, spaces too. ```` @@ -198,6 +198,6 @@ There exist following character classes: ...But that's not all! -Unicode encoding, used by JavaScript for strings, provides many properties for characters, like: which language the letter belongs to (if it's a letter) it is it a punctuation sign, etc. +Unicode encoding, used by JavaScript for strings, provides many properties for characters, like: which language the letter belongs to (if it's a letter), is it a punctuation sign, etc. We can search by these properties as well. That requires flag `pattern:u`, covered in the next article. From 694ff66530340150593418b956130c6018358554 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Mon, 7 Dec 2020 18:05:47 +0200 Subject: [PATCH 15/34] Replace unicode with Unicode all over the book --- 01-regexp-introduction/article.md | 2 +- 03-regexp-unicode/article.md | 8 ++++---- 08-regexp-character-sets-and-ranges/article.md | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/01-regexp-introduction/article.md b/01-regexp-introduction/article.md index 1db3f5f9a..ade62cdf2 100644 --- a/01-regexp-introduction/article.md +++ b/01-regexp-introduction/article.md @@ -56,7 +56,7 @@ There are only 6 of them in JavaScript: : Enables "dotall" mode, that allows a dot `pattern:.` to match newline character `\n` (covered in the chapter ). `pattern:u` -: Enables full unicode support. The flag enables correct processing of surrogate pairs. More about that in the chapter . +: Enables full Unicode support. The flag enables correct processing of surrogate pairs. More about that in the chapter . `pattern:y` : "Sticky" mode: searching at the exact position in the text (covered in the chapter ) diff --git a/03-regexp-unicode/article.md b/03-regexp-unicode/article.md index 0e0fb88ae..3f23ec5de 100644 --- a/03-regexp-unicode/article.md +++ b/03-regexp-unicode/article.md @@ -4,9 +4,9 @@ JavaScript uses [Unicode encoding](https://en.wikipedia.org/wiki/Unicode) for st That range is not big enough to encode all possible characters, that's why some rare characters are encoded with 4 bytes, for instance like `𝒳` (mathematical X) or `😄` (a smile), some hieroglyphs and so on. -Here are the unicode values of some characters: +Here are the Unicode values of some characters: -| Character | Unicode | Bytes count in unicode | +| Character | Unicode | Bytes count in Unicode | |------------|---------|--------| | a | `0x0061` | 2 | | ≈ | `0x2248` | 2 | @@ -121,7 +121,7 @@ alert("number: xAF".match(regexp)); // xAF Let's look for Chinese hieroglyphs. -There's a unicode property `Script` (a writing system), that may have a value: `Cyrillic`, `Greek`, `Arabic`, `Han` (Chinese) and so on, [here's the full list](https://en.wikipedia.org/wiki/Script_(Unicode)). +There's a Unicode property `Script` (a writing system), that may have a value: `Cyrillic`, `Greek`, `Arabic`, `Han` (Chinese) and so on, [here's the full list](https://en.wikipedia.org/wiki/Script_(Unicode)). To look for characters in a given writing system we should use `pattern:Script=`, e.g. for Cyrillic letters: `pattern:\p{sc=Cyrillic}`, for Chinese hieroglyphs: `pattern:\p{sc=Han}`, and so on: @@ -135,7 +135,7 @@ alert( str.match(regexp) ); // 你,好 ### Example: currency -Characters that denote a currency, such as `$`, `€`, `¥`, have unicode property `pattern:\p{Currency_Symbol}`, the short alias: `pattern:\p{Sc}`. +Characters that denote a currency, such as `$`, `€`, `¥`, have Unicode property `pattern:\p{Currency_Symbol}`, the short alias: `pattern:\p{Sc}`. Let's use it to look for prices in the format "currency, followed by a digit": diff --git a/08-regexp-character-sets-and-ranges/article.md b/08-regexp-character-sets-and-ranges/article.md index cb6a27e9d..b24461a90 100644 --- a/08-regexp-character-sets-and-ranges/article.md +++ b/08-regexp-character-sets-and-ranges/article.md @@ -57,16 +57,16 @@ For instance: - **\d** -- is the same as `pattern:[0-9]`, - **\w** -- is the same as `pattern:[a-zA-Z0-9_]`, -- **\s** -- is the same as `pattern:[\t\n\v\f\r ]`, plus few other rare unicode space characters. +- **\s** -- is the same as `pattern:[\t\n\v\f\r ]`, plus few other rare Unicode space characters. ``` ### Example: multi-language \w As the character class `pattern:\w` is a shorthand for `pattern:[a-zA-Z0-9_]`, it can't find Chinese hieroglyphs, Cyrillic letters, etc. -We can write a more universal pattern, that looks for wordly characters in any language. That's easy with unicode properties: `pattern:[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]`. +We can write a more universal pattern, that looks for wordly characters in any language. That's easy with Unicode properties: `pattern:[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]`. -Let's decipher it. Similar to `pattern:\w`, we're making a set of our own that includes characters with following unicode properties: +Let's decipher it. Similar to `pattern:\w`, we're making a set of our own that includes characters with following Unicode properties: - `Alphabetic` (`Alpha`) - for letters, - `Mark` (`M`) - for accents, @@ -85,7 +85,7 @@ let str = `Hi 你好 12`; alert( str.match(regexp) ); // H,i,你,好,1,2 ``` -Of course, we can edit this pattern: add unicode properties or remove them. Unicode properties are covered in more details in the article . +Of course, we can edit this pattern: add Unicode properties or remove them. Unicode properties are covered in more details in the article . ```warn header="Unicode properties aren't supported in Edge and Firefox" Unicode properties `pattern:p{…}` are not yet implemented in Edge and Firefox. If we really need them, we can use library [XRegExp](http://xregexp.com/). From 6525f73e7bc602691e88076f9dac153e480e63e3 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Mon, 7 Dec 2020 22:54:54 +0200 Subject: [PATCH 16/34] Fix confusing wording in 9.6 (Word boundary: \b) `\bHello\b` pattern was just analyzed in the previous paragraph, so the current wording is a bit confusing. --- 06-regexp-boundary/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/06-regexp-boundary/article.md b/06-regexp-boundary/article.md index 7c9f442fe..06b5ac9f7 100644 --- a/06-regexp-boundary/article.md +++ b/06-regexp-boundary/article.md @@ -27,7 +27,7 @@ So, it matches the pattern `pattern:\bHello\b`, because: 2. Then matches the word `pattern:Hello`. 3. Then the test `pattern:\b` matches again, as we're between `subject:o` and a comma. -The pattern `pattern:\bHello\b` would also match. But not `pattern:\bHell\b` (because there's no word boundary after `l`) and not `Java!\b` (because the exclamation sign is not a wordly character `pattern:\w`, so there's no word boundary after it). +So the pattern `pattern:\bHello\b` would match, but not `pattern:\bHell\b` (because there's no word boundary after `l`) and not `Java!\b` (because the exclamation sign is not a wordly character `pattern:\w`, so there's no word boundary after it). ```js run alert( "Hello, Java!".match(/\bHello\b/) ); // Hello From ec2e59a650532a6edecb5edee0d847241c06f45a Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Tue, 8 Dec 2020 00:03:41 +0200 Subject: [PATCH 17/34] Fix typo in 9.7 (Escaping, special characters) --- 07-regexp-escaping/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/07-regexp-escaping/article.md b/07-regexp-escaping/article.md index 7bf989471..eed76791b 100644 --- a/07-regexp-escaping/article.md +++ b/07-regexp-escaping/article.md @@ -96,4 +96,4 @@ alert( "Chapter 5.1".match(regexp) ); // 5.1 - To search for special characters `pattern:[ \ ^ $ . | ? * + ( )` literally, we need to prepend them with a backslash `\` ("escape them"). - We also need to escape `/` if we're inside `pattern:/.../` (but not inside `new RegExp`). -- When passing a string `new RegExp`, we need to double backslashes `\\`, cause string quotes consume one of them. +- When passing a string to `new RegExp`, we need to double backslashes `\\`, cause string quotes consume one of them. From 3cfe345be4ad57ac644601d7caaf7045470b2b09 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Tue, 8 Dec 2020 17:38:02 +0200 Subject: [PATCH 18/34] Fix typo in 9.8 task solution (Sets and ranges [...]) --- 08-regexp-character-sets-and-ranges/1-find-range-1/solution.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/08-regexp-character-sets-and-ranges/1-find-range-1/solution.md b/08-regexp-character-sets-and-ranges/1-find-range-1/solution.md index 378471611..85c7748f7 100644 --- a/08-regexp-character-sets-and-ranges/1-find-range-1/solution.md +++ b/08-regexp-character-sets-and-ranges/1-find-range-1/solution.md @@ -5,7 +5,7 @@ Answers: **no, yes**. ```js run alert( "Java".match(/Java[^script]/) ); // null ``` -- Yes, because the part `pattern:[^script]` part matches the character `"S"`. It's not one of `pattern:script`. As the regexp is case-sensitive (no `pattern:i` flag), it treats `"S"` as a different character from `"s"`. +- Yes, because the `pattern:[^script]` part matches the character `"S"`. It's not one of `pattern:script`. As the regexp is case-sensitive (no `pattern:i` flag), it treats `"S"` as a different character from `"s"`. ```js run alert( "JavaScript".match(/Java[^script]/) ); // "JavaS" From 69f52f74d547350de5ea1acb0cf37b54b73dc13b Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Tue, 8 Dec 2020 17:41:33 +0200 Subject: [PATCH 19/34] Update support note in 9.8 (Sets and ranges [...]) See https://caniuse.com/mdn-javascript_builtins_regexp_property_escapes --- 08-regexp-character-sets-and-ranges/article.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/08-regexp-character-sets-and-ranges/article.md b/08-regexp-character-sets-and-ranges/article.md index b24461a90..a1b8f896d 100644 --- a/08-regexp-character-sets-and-ranges/article.md +++ b/08-regexp-character-sets-and-ranges/article.md @@ -87,8 +87,8 @@ alert( str.match(regexp) ); // H,i,你,好,1,2 Of course, we can edit this pattern: add Unicode properties or remove them. Unicode properties are covered in more details in the article . -```warn header="Unicode properties aren't supported in Edge and Firefox" -Unicode properties `pattern:p{…}` are not yet implemented in Edge and Firefox. If we really need them, we can use library [XRegExp](http://xregexp.com/). +```warn header="Unicode properties aren't supported in IE" +Unicode properties `pattern:p{…}` are not implemented in IE. If we really need them, we can use library [XRegExp](http://xregexp.com/). Or just use ranges of characters in a language that interests us, e.g. `pattern:[а-я]` for Cyrillic letters. ``` From d2a9c540f4a6f586706eb449219824af7053d94a Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Wed, 9 Dec 2020 01:45:06 +0200 Subject: [PATCH 20/34] Fix typos in 9.10 (Greedy and lazy quantifiers) --- .../4-find-html-tags-greedy-lazy/task.md | 2 +- 10-regexp-greedy-and-lazy/article.md | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md b/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md index 8e96c921d..6759152ff 100644 --- a/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md +++ b/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md @@ -12,4 +12,4 @@ let str = '<> '; alert( str.match(regexp) ); // '', '', '' ``` -Here we assume that tag attributes may not contain `<` and `>` (inside squotes too), that simplifies things a bit. +Here we assume that tag attributes may not contain `<` and `>` (inside quotes too), that simplifies things a bit. diff --git a/10-regexp-greedy-and-lazy/article.md b/10-regexp-greedy-and-lazy/article.md index 79abc559d..2f656479d 100644 --- a/10-regexp-greedy-and-lazy/article.md +++ b/10-regexp-greedy-and-lazy/article.md @@ -88,7 +88,7 @@ These common words do not make it obvious why the regexp fails, so let's elabora That's probably not what we expected, but that's how it works. -**In the greedy mode (by default) a quantifier is repeated as many times as possible.** +**In the greedy mode (by default) a quantified character is repeated as many times as possible.** The regexp engine adds to the match as many characters as it can for `pattern:.+`, and then shortens that one by one, if the rest of the pattern doesn't match. @@ -109,7 +109,7 @@ let regexp = /".+?"/g; let str = 'a "witch" and her "broom" is one'; -alert( str.match(regexp) ); // witch, broom +alert( str.match(regexp) ); // "witch", "broom" ``` To clearly understand the change, let's trace the search step by step. @@ -179,7 +179,7 @@ let regexp = /"[^"]+"/g; let str = 'a "witch" and her "broom" is one'; -alert( str.match(regexp) ); // witch, broom +alert( str.match(regexp) ); // "witch", "broom" ``` The regexp `pattern:"[^"]+"` gives correct results, because it looks for a quote `pattern:'"'` followed by one or more non-quotes `pattern:[^"]`, and then the closing quote. @@ -293,9 +293,9 @@ alert( str2.match(regexp) ); // , Date: Wed, 9 Dec 2020 18:54:47 +0200 Subject: [PATCH 21/34] Fix typos in 9.11 (Capturing groups) --- 11-regexp-groups/01-test-mac/task.md | 2 +- 11-regexp-groups/03-find-decimal-numbers/solution.md | 2 +- 11-regexp-groups/04-parse-expression/solution.md | 2 +- 11-regexp-groups/article.md | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/11-regexp-groups/01-test-mac/task.md b/11-regexp-groups/01-test-mac/task.md index 029a4803a..a2e799cfa 100644 --- a/11-regexp-groups/01-test-mac/task.md +++ b/11-regexp-groups/01-test-mac/task.md @@ -16,5 +16,5 @@ alert( regexp.test('0132546789AB') ); // false (no colons) alert( regexp.test('01:32:54:67:89') ); // false (5 numbers, must be 6) -alert( regexp.test('01:32:54:67:89:ZZ') ) // false (ZZ ad the end) +alert( regexp.test('01:32:54:67:89:ZZ') ) // false (ZZ at the end) ``` diff --git a/11-regexp-groups/03-find-decimal-numbers/solution.md b/11-regexp-groups/03-find-decimal-numbers/solution.md index c4349f9a0..813d619ef 100644 --- a/11-regexp-groups/03-find-decimal-numbers/solution.md +++ b/11-regexp-groups/03-find-decimal-numbers/solution.md @@ -1,4 +1,4 @@ -A positive number with an optional decimal part is (per previous task): `pattern:\d+(\.\d+)?`. +A positive number with an optional decimal part is: `pattern:\d+(\.\d+)?`. Let's add the optional `pattern:-` in the beginning: diff --git a/11-regexp-groups/04-parse-expression/solution.md b/11-regexp-groups/04-parse-expression/solution.md index 130c57be3..ac67519bb 100644 --- a/11-regexp-groups/04-parse-expression/solution.md +++ b/11-regexp-groups/04-parse-expression/solution.md @@ -1,4 +1,4 @@ -A regexp for a number is: `pattern:-?\d+(\.\d+)?`. We created it in previous tasks. +A regexp for a number is: `pattern:-?\d+(\.\d+)?`. We created it in the previous task. An operator is `pattern:[-+*/]`. The hyphen `pattern:-` goes first in the square brackets, because in the middle it would mean a character range, while we just want a character `-`. diff --git a/11-regexp-groups/article.md b/11-regexp-groups/article.md index e559fd87c..e0ef00bd3 100644 --- a/11-regexp-groups/article.md +++ b/11-regexp-groups/article.md @@ -249,7 +249,7 @@ The call to `matchAll` does not perform the search. Instead, it returns an itera So, there will be found as many results as needed, not more. -E.g. there are potentially 100 matches in the text, but in a `for..of` loop we found 5 of them, then decided it's enough and make a `break`. Then the engine won't spend time finding other 95 matches. +E.g. there are potentially 100 matches in the text, but in a `for..of` loop we found 5 of them, then decided it's enough and made a `break`. Then the engine won't spend time finding other 95 matches. ``` ## Named groups From d86b85cc046e8c05510f9f084fa24d61d376fd91 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Wed, 9 Dec 2020 21:19:50 +0200 Subject: [PATCH 22/34] Fix typos in 9.13 (Alternation (OR) |) --- 13-regexp-alternation/article.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/13-regexp-alternation/article.md b/13-regexp-alternation/article.md index 007d8f6a6..0fe2175c7 100644 --- a/13-regexp-alternation/article.md +++ b/13-regexp-alternation/article.md @@ -20,7 +20,7 @@ alert( str.match(regexp) ); // 'HTML', 'CSS', 'JavaScript' We already saw a similar thing -- square brackets. They allow to choose between multiple characters, for instance `pattern:gr[ae]y` matches `match:gray` or `match:grey`. -Square brackets allow only characters or character sets. Alternation allows any expressions. A regexp `pattern:A|B|C` means one of expressions `A`, `B` or `C`. +Square brackets allow only characters or character classes. Alternation allows any expressions. A regexp `pattern:A|B|C` means one of expressions `A`, `B` or `C`. For instance: @@ -33,7 +33,7 @@ To apply alternation to a chosen part of the pattern, we can enclose it in paren ## Example: regexp for time -In previous articles there was a task to build a regexp for searching time in the form `hh:mm`, for instance `12:00`. But a simple `pattern:\d\d:\d\d` is too vague. It accepts `25:99` as the time (as 99 seconds match the pattern, but that time is invalid). +In previous articles there was a task to build a regexp for searching time in the form `hh:mm`, for instance `12:00`. But a simple `pattern:\d\d:\d\d` is too vague. It accepts `25:99` as the time (as 99 minutes match the pattern, but that time is invalid). How can we make a better pattern? @@ -47,7 +47,7 @@ We can write both variants in a regexp using alternation: `pattern:[01]\d|2[0-3] Next, minutes must be from `00` to `59`. In the regular expression language that can be written as `pattern:[0-5]\d`: the first digit `0-5`, and then any digit. -If we glue minutes and seconds together, we get the pattern: `pattern:[01]\d|2[0-3]:[0-5]\d`. +If we glue hours and minutes together, we get the pattern: `pattern:[01]\d|2[0-3]:[0-5]\d`. We're almost done, but there's a problem. The alternation `pattern:|` now happens to be between `pattern:[01]\d` and `pattern:2[0-3]:[0-5]\d`. From 7f8659fad76ffd4b291289d2875aa4c5e8688e70 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Wed, 9 Dec 2020 22:37:29 +0200 Subject: [PATCH 23/34] Correct example in 9.14 (Lookahead and lookbehind) Without the `g` flag, we cannot say that the price is skipped. Without the `\b` assertion, we will have the part of the price in the result. --- 14-regexp-lookahead-lookbehind/article.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/14-regexp-lookahead-lookbehind/article.md b/14-regexp-lookahead-lookbehind/article.md index 48c82da14..9f4f4f6c6 100644 --- a/14-regexp-lookahead-lookbehind/article.md +++ b/14-regexp-lookahead-lookbehind/article.md @@ -54,7 +54,7 @@ The syntax is: `pattern:X(?!Y)`, it means "search `pattern:X`, but only if not f ```js run let str = "2 turkeys cost 60€"; -alert( str.match(/\d+(?!€)/) ); // 2 (the price is skipped) +alert( str.match(/\d+\b(?!€)/g) ); // 2 (the price is not matched) ``` ## Lookbehind @@ -81,7 +81,7 @@ And, if we need the quantity -- a number, not preceded by `subject:$`, then we c ```js run let str = "2 turkeys cost $60"; -alert( str.match(/(? Date: Thu, 10 Dec 2020 00:18:02 +0200 Subject: [PATCH 24/34] Make solution in 9.14 safer As the task uses multiline HTML and the note at the end suggests using `s` flag, the lazy quantifier would be safer. --- .../2-insert-after-head/solution.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/14-regexp-lookahead-lookbehind/2-insert-after-head/solution.md b/14-regexp-lookahead-lookbehind/2-insert-after-head/solution.md index b5915744a..f2bbd3f5b 100644 --- a/14-regexp-lookahead-lookbehind/2-insert-after-head/solution.md +++ b/14-regexp-lookahead-lookbehind/2-insert-after-head/solution.md @@ -1,4 +1,4 @@ -In order to insert after the `` tag, we must first find it. We can use the regular expression pattern `pattern:` for that. +In order to insert after the `` tag, we must first find it. We can use the regular expression pattern `pattern:` for that. In this task we don't need to modify the `` tag. We only need to add the text after it. @@ -6,18 +6,18 @@ Here's how we can do it: ```js run let str = '......'; -str = str.replace(//, '$&

Hello

'); +str = str.replace(//, '$&

Hello

'); alert(str); // ...

Hello

... ``` -In the replacement string `$&` means the match itself, that is, the part of the source text that corresponds to `pattern:`. It gets replaced by itself plus `

Hello

`. +In the replacement string `$&` means the match itself, that is, the part of the source text that corresponds to `pattern:`. It gets replaced by itself plus `

Hello

`. An alternative is to use lookbehind: ```js run let str = '......'; -str = str.replace(/(?<=)/, `

Hello

`); +str = str.replace(/(?<=)/, `

Hello

`); alert(str); // ...

Hello

... ``` @@ -26,11 +26,11 @@ As you can see, there's only lookbehind part in this regexp. It works like this: - At every position in the text. -- Check if it's preceeded by `pattern:`. +- Check if it's preceeded by `pattern:`. - If it's so then we have the match. -The tag `pattern:` won't be returned. The result of this regexp is literally an empty string, but it matches only at positions preceeded by `pattern:`. +The tag `pattern:` won't be returned. The result of this regexp is literally an empty string, but it matches only at positions preceeded by `pattern:`. -So we replaces the "empty line", preceeded by `pattern:`, with `

Hello

`. That's the insertion after ``. +So we replaces the "empty line", preceeded by `pattern:`, with `

Hello

`. That's the insertion after ``. -P.S. Regexp flags, such as `pattern:s` and `pattern:i` can also useful: `pattern://si`. The `pattern:s` flag makes the dot `pattern:.` match a newline character, and `pattern:i` flag makes `pattern:` also match `match:` case-insensitively. +P.S. Regexp flags, such as `pattern:s` and `pattern:i` can also be useful: `pattern://si`. The `pattern:s` flag makes the dot `pattern:.` match a newline character, and `pattern:i` flag makes `pattern:` also match `match:` case-insensitively. From 2fdeb293420f6d15a8c24e45f4d108520c7a1e65 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Thu, 10 Dec 2020 16:00:34 +0200 Subject: [PATCH 25/34] Fix typo, clarify wording in 9.15 (Catastrophic backtracking) --- 15-regexp-catastrophic-backtracking/article.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/15-regexp-catastrophic-backtracking/article.md b/15-regexp-catastrophic-backtracking/article.md index 85343bf84..6913a0178 100644 --- a/15-regexp-catastrophic-backtracking/article.md +++ b/15-regexp-catastrophic-backtracking/article.md @@ -160,7 +160,7 @@ Trying each of them is exactly the reason why the search takes so long. ## Back to words and strings -The similar thing happens in our first example, when we look words by pattern `pattern:^(\w+\s?)*$` in the string `subject:An input that hangs!`. +The similar thing happens in our first example, when we look for words by pattern `pattern:^(\w+\s?)*$` in the string `subject:An input that hangs!`. The reason is that a word can be represented as one `pattern:\w+` or many: @@ -238,7 +238,7 @@ E.g. in the regexp `pattern:(\d+)*$` it's obvious for a human, that `pattern:+` (1234)(56789)! ``` -And in the original example `pattern:^(\w+\s?)*$` we may want to forbid backtracking in `pattern:\w+`. That is: `pattern:\w+` should match a whole word, with the maximal possible length. There's no need to lower the repetitions count in `pattern:\w+`, try to split it into two words `pattern:\w+\w+` and so on. +And in the original example `pattern:^(\w+\s?)*$` we may want to forbid backtracking in `pattern:\w+`. That is: `pattern:\w+` should match a whole word, with the maximal possible length. There's no need to lower the repetitions count in `pattern:\w+` or to split it into two words `pattern:\w+\w+` and so on. Modern regular expression engines support possessive quantifiers for that. Regular quantifiers become possessive if we add `pattern:+` after them. That is, we use `pattern:\d++` instead of `pattern:\d+` to stop `pattern:+` from backtracking. From e1bffa01ae360908e769bd09453a6a4b9758d14b Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Thu, 10 Dec 2020 16:59:05 +0200 Subject: [PATCH 26/34] Update support note in 9.15 (Catastrophic backtracking) Refs: https://bugs.chromium.org/p/v8/issues/detail?id=10765 Refs: https://bugs.chromium.org/p/v8/issues/detail?id=11021 Can be tested in the Goole Chrome Beta, Dev, and Canary branches now, as well as in the Node.js v8-canary branch. --- 15-regexp-catastrophic-backtracking/article.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/15-regexp-catastrophic-backtracking/article.md b/15-regexp-catastrophic-backtracking/article.md index 85343bf84..080f92af2 100644 --- a/15-regexp-catastrophic-backtracking/article.md +++ b/15-regexp-catastrophic-backtracking/article.md @@ -37,7 +37,7 @@ let str = "An input string that takes a long time or even makes this regexp hang alert( regexp.test(str) ); ``` -To be fair, let's note that some regular expression engines can handle such a search effectively. But most of them can't. Browser engines usually hang. +To be fair, let's note that some regular expression engines can handle such a search effectively. But most of them can't. Browser engines usually hang (Google Chrome can handle such a search since version 88 due to a new experimental regular expression engine shipped in V8, JavaScript engine for browsers and Node.js). ## Simplified example @@ -74,7 +74,7 @@ Here's what the regexp engine does: ``` After all digits are consumed, `pattern:\d+` is considered found (as `match:123456789`). - + Then the star quantifier `pattern:(\d+)*` applies. But there are no more digits in the text, so the star doesn't give anything. The next character in the pattern is the string end `pattern:$`. But in the text we have `subject:z` instead, so there's no match: @@ -220,7 +220,7 @@ The time needed to try a lot of (actually most of) combinations is now saved. ## Preventing backtracking -It's not always convenient to rewrite a regexp though. In the example above it was easy, but it's not always obvious how to do it. +It's not always convenient to rewrite a regexp though. In the example above it was easy, but it's not always obvious how to do it. Besides, a rewritten regexp is usually more complex, and that's not good. Regexps are complex enough without extra efforts. @@ -246,7 +246,7 @@ Possessive quantifiers are in fact simpler than "regular" ones. They just match There are also so-called "atomic capturing groups" - a way to disable backtracking inside parentheses. -...But the bad news is that, unfortunately, in JavaScript they are not supported. +...But the bad news is that, unfortunately, in JavaScript they are not supported. We can emulate them though using a "lookahead transform". From 281bd56f9ab036e9650125ac27830b9fe32d44b9 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Thu, 10 Dec 2020 20:42:09 +0200 Subject: [PATCH 27/34] Fix typo in 9.16 (Sticky flag "y"...) --- 16-regexp-sticky/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/16-regexp-sticky/article.md b/16-regexp-sticky/article.md index 373f3453e..ece2c960f 100644 --- a/16-regexp-sticky/article.md +++ b/16-regexp-sticky/article.md @@ -93,7 +93,7 @@ The result is correct. ...But wait, not so fast. -Please note: the `regexp.exec` call start searching at position `lastIndex` and then goes further. If there's no word at position `lastIndex`, but it's somewhere after it, then it will be found: +Please note: the `regexp.exec` call starts searching at position `lastIndex` and then goes further. If there's no word at position `lastIndex`, but it's somewhere after it, then it will be found: ```js run let str = 'let varName = "value"'; From 3a1091a7d0a984b03bb9091cb92c0597ae471b16 Mon Sep 17 00:00:00 2001 From: Vse Mozhe Buty Date: Fri, 11 Dec 2020 00:40:16 +0200 Subject: [PATCH 28/34] Fix typo in 9.17 (Methods of RegExp and String) --- 17-regexp-methods/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/17-regexp-methods/article.md b/17-regexp-methods/article.md index a7847c0f6..9654b1423 100644 --- a/17-regexp-methods/article.md +++ b/17-regexp-methods/article.md @@ -142,7 +142,7 @@ To find all hyphens, we need to use not the string `"-"`, but a regexp `pattern: alert( '12-34-56'.replace( *!*/-/g*/!*, ":" ) ) // 12:34:56 ``` -The second argument is a replacement string. We can use special character in it: +The second argument is a replacement string. We can use special characters in it: | Symbols | Action in the replacement string | |--------|--------| From c6bf5f1adacef9c7f7732b0051ecf68e792aabad Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Sun, 13 Dec 2020 20:15:24 +0300 Subject: [PATCH 29/34] minor fixes --- 15-regexp-catastrophic-backtracking/article.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/15-regexp-catastrophic-backtracking/article.md b/15-regexp-catastrophic-backtracking/article.md index 080f92af2..3411e7eb2 100644 --- a/15-regexp-catastrophic-backtracking/article.md +++ b/15-regexp-catastrophic-backtracking/article.md @@ -37,7 +37,9 @@ let str = "An input string that takes a long time or even makes this regexp hang alert( regexp.test(str) ); ``` -To be fair, let's note that some regular expression engines can handle such a search effectively. But most of them can't. Browser engines usually hang (Google Chrome can handle such a search since version 88 due to a new experimental regular expression engine shipped in V8, JavaScript engine for browsers and Node.js). +To be fair, let's note that some regular expression engines can handle such a search effectively, while some can't. + +For example, V8 engine version starting from 8.8 can handle such a search (so Google Chrome 88 doesn't hang here). ## Simplified example From 60c400d0f27365008a82f270569539dfc16e6240 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Sun, 13 Dec 2020 20:16:17 +0300 Subject: [PATCH 30/34] minor fixes --- 15-regexp-catastrophic-backtracking/article.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/15-regexp-catastrophic-backtracking/article.md b/15-regexp-catastrophic-backtracking/article.md index 3411e7eb2..16063b157 100644 --- a/15-regexp-catastrophic-backtracking/article.md +++ b/15-regexp-catastrophic-backtracking/article.md @@ -37,9 +37,7 @@ let str = "An input string that takes a long time or even makes this regexp hang alert( regexp.test(str) ); ``` -To be fair, let's note that some regular expression engines can handle such a search effectively, while some can't. - -For example, V8 engine version starting from 8.8 can handle such a search (so Google Chrome 88 doesn't hang here). +To be fair, let's note that some regular expression engines can handle such a search effectively, for example V8 engine version starting from 8.8 can do that (so Google Chrome 88 doesn't hang here), while Firefox browser does hang. ## Simplified example From 0519e04aec6e46bddcf235f588936673bef05cb1 Mon Sep 17 00:00:00 2001 From: Ilya Kantor Date: Sun, 13 Dec 2020 21:17:05 +0300 Subject: [PATCH 31/34] closes #2382 --- 17-regexp-methods/article.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/17-regexp-methods/article.md b/17-regexp-methods/article.md index 9654b1423..0d5168b40 100644 --- a/17-regexp-methods/article.md +++ b/17-regexp-methods/article.md @@ -228,6 +228,23 @@ alert(result); // Smith, John Using a function gives us the ultimate replacement power, because it gets all the information about the match, has access to outer variables and can do everything. +## str.replaceAll(str|regexp, str|func) + +This method is essentially the same as `str.replace`, with two major differences: + +1. If the first argument is a string, it replaces *all occurences* of the string, while `replace` replaces only the *first occurence*. +2. If the first argument is a regular expression without the `g` flag, there'll be an error. With `g` flag, it works the same as `replace`. + +The main use case for `replaceAll` is replacing all occurences of a string. + +Like this: + +```js run +// replace all dashes by a colon +alert('12-34-56'.replaceAll("-", ":")) // 12:34:56 +``` + + ## regexp.exec(str) The method `regexp.exec(str)` method returns a match for `regexp` in the string `str`. Unlike previous methods, it's called on a regexp, not on a string. From 10eb3f04223ea8ffeb72dff77820ab0ebae45790 Mon Sep 17 00:00:00 2001 From: Pano Papadatos Date: Sun, 13 Dec 2020 13:35:25 -0500 Subject: [PATCH 32/34] Typo "in any *of* language" --- 03-regexp-unicode/article.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/03-regexp-unicode/article.md b/03-regexp-unicode/article.md index 3f23ec5de..3e981c80a 100644 --- a/03-regexp-unicode/article.md +++ b/03-regexp-unicode/article.md @@ -39,7 +39,7 @@ For instance, if a character has `Letter` property, it means that the character We can search for characters with a property, written as `pattern:\p{…}`. To use `pattern:\p{…}`, a regular expression must have flag `pattern:u`. -For instance, `\p{Letter}` denotes a letter in any of language. We can also use `\p{L}`, as `L` is an alias of `Letter`. There are shorter aliases for almost every property. +For instance, `\p{Letter}` denotes a letter in any language. We can also use `\p{L}`, as `L` is an alias of `Letter`. There are shorter aliases for almost every property. In the example below three kinds of letters will be found: English, Georgian and Korean. From 5a31649a459ba3688739138c1abfce01286db7ae Mon Sep 17 00:00:00 2001 From: Osvaldo Dias dos Santos Date: Fri, 18 Dec 2020 20:56:46 +0100 Subject: [PATCH 33/34] Update folder '9-regular-expressions' --- .../01-regexp-introduction}/article.md | 0 .../02-regexp-character-classes}/article.md | 0 .../02-regexp-character-classes}/love-html5-classes.svg | 0 .../03-regexp-unicode}/article.md | 0 .../04-regexp-anchors}/1-start-end/solution.md | 0 .../04-regexp-anchors}/1-start-end/task.md | 0 .../04-regexp-anchors}/article.md | 0 .../05-regexp-multiline-mode}/article.md | 0 .../06-regexp-boundary}/1-find-time-hh-mm/solution.md | 0 .../06-regexp-boundary}/1-find-time-hh-mm/task.md | 0 .../06-regexp-boundary}/article.md | 0 .../06-regexp-boundary}/hello-java-boundaries.svg | 0 .../07-regexp-escaping}/article.md | 0 .../1-find-range-1/solution.md | 0 .../08-regexp-character-sets-and-ranges}/1-find-range-1/task.md | 0 .../2-find-time-2-formats/solution.md | 0 .../2-find-time-2-formats/task.md | 0 .../08-regexp-character-sets-and-ranges}/article.md | 0 .../09-regexp-quantifiers}/1-find-text-manydots/solution.md | 0 .../09-regexp-quantifiers}/1-find-text-manydots/task.md | 0 .../09-regexp-quantifiers}/2-find-html-colors-6hex/solution.md | 0 .../09-regexp-quantifiers}/2-find-html-colors-6hex/task.md | 0 .../09-regexp-quantifiers}/article.md | 0 .../10-regexp-greedy-and-lazy}/1-lazy-greedy/solution.md | 0 .../10-regexp-greedy-and-lazy}/1-lazy-greedy/task.md | 0 .../10-regexp-greedy-and-lazy}/3-find-html-comments/solution.md | 0 .../10-regexp-greedy-and-lazy}/3-find-html-comments/task.md | 0 .../4-find-html-tags-greedy-lazy/solution.md | 0 .../4-find-html-tags-greedy-lazy/task.md | 0 .../10-regexp-greedy-and-lazy}/article.md | 0 .../10-regexp-greedy-and-lazy}/witch_greedy1.svg | 0 .../10-regexp-greedy-and-lazy}/witch_greedy2.svg | 0 .../10-regexp-greedy-and-lazy}/witch_greedy3.svg | 0 .../10-regexp-greedy-and-lazy}/witch_greedy4.svg | 0 .../10-regexp-greedy-and-lazy}/witch_greedy5.svg | 0 .../10-regexp-greedy-and-lazy}/witch_greedy6.svg | 0 .../10-regexp-greedy-and-lazy}/witch_lazy3.svg | 0 .../10-regexp-greedy-and-lazy}/witch_lazy4.svg | 0 .../10-regexp-greedy-and-lazy}/witch_lazy5.svg | 0 .../10-regexp-greedy-and-lazy}/witch_lazy6.svg | 0 .../11-regexp-groups}/01-test-mac/solution.md | 0 .../11-regexp-groups}/01-test-mac/task.md | 0 .../11-regexp-groups}/02-find-webcolor-3-or-6/solution.md | 0 .../11-regexp-groups}/02-find-webcolor-3-or-6/task.md | 0 .../11-regexp-groups}/03-find-decimal-numbers/solution.md | 0 .../11-regexp-groups}/03-find-decimal-numbers/task.md | 0 .../11-regexp-groups}/04-parse-expression/solution.md | 0 .../11-regexp-groups}/04-parse-expression/task.md | 0 .../11-regexp-groups}/article.md | 0 .../11-regexp-groups}/regexp-nested-groups-matches.svg | 0 .../11-regexp-groups}/regexp-nested-groups-pattern.svg | 0 .../12-regexp-backreferences}/article.md | 0 .../01-find-programming-language/solution.md | 0 .../13-regexp-alternation}/01-find-programming-language/task.md | 0 .../13-regexp-alternation}/02-find-matching-bbtags/solution.md | 0 .../13-regexp-alternation}/02-find-matching-bbtags/task.md | 0 .../13-regexp-alternation}/03-match-quoted-string/solution.md | 0 .../13-regexp-alternation}/03-match-quoted-string/task.md | 0 .../13-regexp-alternation}/04-match-exact-tag/solution.md | 0 .../13-regexp-alternation}/04-match-exact-tag/task.md | 0 .../13-regexp-alternation}/article.md | 0 .../1-find-non-negative-integers/solution.md | 0 .../1-find-non-negative-integers/task.md | 0 .../2-insert-after-head/solution.md | 0 .../14-regexp-lookahead-lookbehind}/2-insert-after-head/task.md | 0 .../14-regexp-lookahead-lookbehind}/article.md | 0 .../15-regexp-catastrophic-backtracking}/article.md | 0 .../16-regexp-sticky}/article.md | 0 .../17-regexp-methods}/article.md | 0 index.md => 9-regular-expressions/index.md | 0 70 files changed, 0 insertions(+), 0 deletions(-) rename {01-regexp-introduction => 9-regular-expressions/01-regexp-introduction}/article.md (100%) rename {02-regexp-character-classes => 9-regular-expressions/02-regexp-character-classes}/article.md (100%) rename {02-regexp-character-classes => 9-regular-expressions/02-regexp-character-classes}/love-html5-classes.svg (100%) rename {03-regexp-unicode => 9-regular-expressions/03-regexp-unicode}/article.md (100%) rename {04-regexp-anchors => 9-regular-expressions/04-regexp-anchors}/1-start-end/solution.md (100%) rename {04-regexp-anchors => 9-regular-expressions/04-regexp-anchors}/1-start-end/task.md (100%) rename {04-regexp-anchors => 9-regular-expressions/04-regexp-anchors}/article.md (100%) rename {05-regexp-multiline-mode => 9-regular-expressions/05-regexp-multiline-mode}/article.md (100%) rename {06-regexp-boundary => 9-regular-expressions/06-regexp-boundary}/1-find-time-hh-mm/solution.md (100%) rename {06-regexp-boundary => 9-regular-expressions/06-regexp-boundary}/1-find-time-hh-mm/task.md (100%) rename {06-regexp-boundary => 9-regular-expressions/06-regexp-boundary}/article.md (100%) rename {06-regexp-boundary => 9-regular-expressions/06-regexp-boundary}/hello-java-boundaries.svg (100%) rename {07-regexp-escaping => 9-regular-expressions/07-regexp-escaping}/article.md (100%) rename {08-regexp-character-sets-and-ranges => 9-regular-expressions/08-regexp-character-sets-and-ranges}/1-find-range-1/solution.md (100%) rename {08-regexp-character-sets-and-ranges => 9-regular-expressions/08-regexp-character-sets-and-ranges}/1-find-range-1/task.md (100%) rename {08-regexp-character-sets-and-ranges => 9-regular-expressions/08-regexp-character-sets-and-ranges}/2-find-time-2-formats/solution.md (100%) rename {08-regexp-character-sets-and-ranges => 9-regular-expressions/08-regexp-character-sets-and-ranges}/2-find-time-2-formats/task.md (100%) rename {08-regexp-character-sets-and-ranges => 9-regular-expressions/08-regexp-character-sets-and-ranges}/article.md (100%) rename {09-regexp-quantifiers => 9-regular-expressions/09-regexp-quantifiers}/1-find-text-manydots/solution.md (100%) rename {09-regexp-quantifiers => 9-regular-expressions/09-regexp-quantifiers}/1-find-text-manydots/task.md (100%) rename {09-regexp-quantifiers => 9-regular-expressions/09-regexp-quantifiers}/2-find-html-colors-6hex/solution.md (100%) rename {09-regexp-quantifiers => 9-regular-expressions/09-regexp-quantifiers}/2-find-html-colors-6hex/task.md (100%) rename {09-regexp-quantifiers => 9-regular-expressions/09-regexp-quantifiers}/article.md (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/1-lazy-greedy/solution.md (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/1-lazy-greedy/task.md (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/3-find-html-comments/solution.md (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/3-find-html-comments/task.md (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/4-find-html-tags-greedy-lazy/solution.md (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/4-find-html-tags-greedy-lazy/task.md (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/article.md (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/witch_greedy1.svg (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/witch_greedy2.svg (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/witch_greedy3.svg (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/witch_greedy4.svg (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/witch_greedy5.svg (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/witch_greedy6.svg (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/witch_lazy3.svg (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/witch_lazy4.svg (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/witch_lazy5.svg (100%) rename {10-regexp-greedy-and-lazy => 9-regular-expressions/10-regexp-greedy-and-lazy}/witch_lazy6.svg (100%) rename {11-regexp-groups => 9-regular-expressions/11-regexp-groups}/01-test-mac/solution.md (100%) rename {11-regexp-groups => 9-regular-expressions/11-regexp-groups}/01-test-mac/task.md (100%) rename {11-regexp-groups => 9-regular-expressions/11-regexp-groups}/02-find-webcolor-3-or-6/solution.md (100%) rename {11-regexp-groups => 9-regular-expressions/11-regexp-groups}/02-find-webcolor-3-or-6/task.md (100%) rename {11-regexp-groups => 9-regular-expressions/11-regexp-groups}/03-find-decimal-numbers/solution.md (100%) rename {11-regexp-groups => 9-regular-expressions/11-regexp-groups}/03-find-decimal-numbers/task.md (100%) rename {11-regexp-groups => 9-regular-expressions/11-regexp-groups}/04-parse-expression/solution.md (100%) rename {11-regexp-groups => 9-regular-expressions/11-regexp-groups}/04-parse-expression/task.md (100%) rename {11-regexp-groups => 9-regular-expressions/11-regexp-groups}/article.md (100%) rename {11-regexp-groups => 9-regular-expressions/11-regexp-groups}/regexp-nested-groups-matches.svg (100%) rename {11-regexp-groups => 9-regular-expressions/11-regexp-groups}/regexp-nested-groups-pattern.svg (100%) rename {12-regexp-backreferences => 9-regular-expressions/12-regexp-backreferences}/article.md (100%) rename {13-regexp-alternation => 9-regular-expressions/13-regexp-alternation}/01-find-programming-language/solution.md (100%) rename {13-regexp-alternation => 9-regular-expressions/13-regexp-alternation}/01-find-programming-language/task.md (100%) rename {13-regexp-alternation => 9-regular-expressions/13-regexp-alternation}/02-find-matching-bbtags/solution.md (100%) rename {13-regexp-alternation => 9-regular-expressions/13-regexp-alternation}/02-find-matching-bbtags/task.md (100%) rename {13-regexp-alternation => 9-regular-expressions/13-regexp-alternation}/03-match-quoted-string/solution.md (100%) rename {13-regexp-alternation => 9-regular-expressions/13-regexp-alternation}/03-match-quoted-string/task.md (100%) rename {13-regexp-alternation => 9-regular-expressions/13-regexp-alternation}/04-match-exact-tag/solution.md (100%) rename {13-regexp-alternation => 9-regular-expressions/13-regexp-alternation}/04-match-exact-tag/task.md (100%) rename {13-regexp-alternation => 9-regular-expressions/13-regexp-alternation}/article.md (100%) rename {14-regexp-lookahead-lookbehind => 9-regular-expressions/14-regexp-lookahead-lookbehind}/1-find-non-negative-integers/solution.md (100%) rename {14-regexp-lookahead-lookbehind => 9-regular-expressions/14-regexp-lookahead-lookbehind}/1-find-non-negative-integers/task.md (100%) rename {14-regexp-lookahead-lookbehind => 9-regular-expressions/14-regexp-lookahead-lookbehind}/2-insert-after-head/solution.md (100%) rename {14-regexp-lookahead-lookbehind => 9-regular-expressions/14-regexp-lookahead-lookbehind}/2-insert-after-head/task.md (100%) rename {14-regexp-lookahead-lookbehind => 9-regular-expressions/14-regexp-lookahead-lookbehind}/article.md (100%) rename {15-regexp-catastrophic-backtracking => 9-regular-expressions/15-regexp-catastrophic-backtracking}/article.md (100%) rename {16-regexp-sticky => 9-regular-expressions/16-regexp-sticky}/article.md (100%) rename {17-regexp-methods => 9-regular-expressions/17-regexp-methods}/article.md (100%) rename index.md => 9-regular-expressions/index.md (100%) diff --git a/01-regexp-introduction/article.md b/9-regular-expressions/01-regexp-introduction/article.md similarity index 100% rename from 01-regexp-introduction/article.md rename to 9-regular-expressions/01-regexp-introduction/article.md diff --git a/02-regexp-character-classes/article.md b/9-regular-expressions/02-regexp-character-classes/article.md similarity index 100% rename from 02-regexp-character-classes/article.md rename to 9-regular-expressions/02-regexp-character-classes/article.md diff --git a/02-regexp-character-classes/love-html5-classes.svg b/9-regular-expressions/02-regexp-character-classes/love-html5-classes.svg similarity index 100% rename from 02-regexp-character-classes/love-html5-classes.svg rename to 9-regular-expressions/02-regexp-character-classes/love-html5-classes.svg diff --git a/03-regexp-unicode/article.md b/9-regular-expressions/03-regexp-unicode/article.md similarity index 100% rename from 03-regexp-unicode/article.md rename to 9-regular-expressions/03-regexp-unicode/article.md diff --git a/04-regexp-anchors/1-start-end/solution.md b/9-regular-expressions/04-regexp-anchors/1-start-end/solution.md similarity index 100% rename from 04-regexp-anchors/1-start-end/solution.md rename to 9-regular-expressions/04-regexp-anchors/1-start-end/solution.md diff --git a/04-regexp-anchors/1-start-end/task.md b/9-regular-expressions/04-regexp-anchors/1-start-end/task.md similarity index 100% rename from 04-regexp-anchors/1-start-end/task.md rename to 9-regular-expressions/04-regexp-anchors/1-start-end/task.md diff --git a/04-regexp-anchors/article.md b/9-regular-expressions/04-regexp-anchors/article.md similarity index 100% rename from 04-regexp-anchors/article.md rename to 9-regular-expressions/04-regexp-anchors/article.md diff --git a/05-regexp-multiline-mode/article.md b/9-regular-expressions/05-regexp-multiline-mode/article.md similarity index 100% rename from 05-regexp-multiline-mode/article.md rename to 9-regular-expressions/05-regexp-multiline-mode/article.md diff --git a/06-regexp-boundary/1-find-time-hh-mm/solution.md b/9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/solution.md similarity index 100% rename from 06-regexp-boundary/1-find-time-hh-mm/solution.md rename to 9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/solution.md diff --git a/06-regexp-boundary/1-find-time-hh-mm/task.md b/9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/task.md similarity index 100% rename from 06-regexp-boundary/1-find-time-hh-mm/task.md rename to 9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/task.md diff --git a/06-regexp-boundary/article.md b/9-regular-expressions/06-regexp-boundary/article.md similarity index 100% rename from 06-regexp-boundary/article.md rename to 9-regular-expressions/06-regexp-boundary/article.md diff --git a/06-regexp-boundary/hello-java-boundaries.svg b/9-regular-expressions/06-regexp-boundary/hello-java-boundaries.svg similarity index 100% rename from 06-regexp-boundary/hello-java-boundaries.svg rename to 9-regular-expressions/06-regexp-boundary/hello-java-boundaries.svg diff --git a/07-regexp-escaping/article.md b/9-regular-expressions/07-regexp-escaping/article.md similarity index 100% rename from 07-regexp-escaping/article.md rename to 9-regular-expressions/07-regexp-escaping/article.md diff --git a/08-regexp-character-sets-and-ranges/1-find-range-1/solution.md b/9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/solution.md similarity index 100% rename from 08-regexp-character-sets-and-ranges/1-find-range-1/solution.md rename to 9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/solution.md diff --git a/08-regexp-character-sets-and-ranges/1-find-range-1/task.md b/9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/task.md similarity index 100% rename from 08-regexp-character-sets-and-ranges/1-find-range-1/task.md rename to 9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/task.md diff --git a/08-regexp-character-sets-and-ranges/2-find-time-2-formats/solution.md b/9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/solution.md similarity index 100% rename from 08-regexp-character-sets-and-ranges/2-find-time-2-formats/solution.md rename to 9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/solution.md diff --git a/08-regexp-character-sets-and-ranges/2-find-time-2-formats/task.md b/9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/task.md similarity index 100% rename from 08-regexp-character-sets-and-ranges/2-find-time-2-formats/task.md rename to 9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/task.md diff --git a/08-regexp-character-sets-and-ranges/article.md b/9-regular-expressions/08-regexp-character-sets-and-ranges/article.md similarity index 100% rename from 08-regexp-character-sets-and-ranges/article.md rename to 9-regular-expressions/08-regexp-character-sets-and-ranges/article.md diff --git a/09-regexp-quantifiers/1-find-text-manydots/solution.md b/9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/solution.md similarity index 100% rename from 09-regexp-quantifiers/1-find-text-manydots/solution.md rename to 9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/solution.md diff --git a/09-regexp-quantifiers/1-find-text-manydots/task.md b/9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/task.md similarity index 100% rename from 09-regexp-quantifiers/1-find-text-manydots/task.md rename to 9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/task.md diff --git a/09-regexp-quantifiers/2-find-html-colors-6hex/solution.md b/9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/solution.md similarity index 100% rename from 09-regexp-quantifiers/2-find-html-colors-6hex/solution.md rename to 9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/solution.md diff --git a/09-regexp-quantifiers/2-find-html-colors-6hex/task.md b/9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/task.md similarity index 100% rename from 09-regexp-quantifiers/2-find-html-colors-6hex/task.md rename to 9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/task.md diff --git a/09-regexp-quantifiers/article.md b/9-regular-expressions/09-regexp-quantifiers/article.md similarity index 100% rename from 09-regexp-quantifiers/article.md rename to 9-regular-expressions/09-regexp-quantifiers/article.md diff --git a/10-regexp-greedy-and-lazy/1-lazy-greedy/solution.md b/9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/solution.md similarity index 100% rename from 10-regexp-greedy-and-lazy/1-lazy-greedy/solution.md rename to 9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/solution.md diff --git a/10-regexp-greedy-and-lazy/1-lazy-greedy/task.md b/9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/task.md similarity index 100% rename from 10-regexp-greedy-and-lazy/1-lazy-greedy/task.md rename to 9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/task.md diff --git a/10-regexp-greedy-and-lazy/3-find-html-comments/solution.md b/9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/solution.md similarity index 100% rename from 10-regexp-greedy-and-lazy/3-find-html-comments/solution.md rename to 9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/solution.md diff --git a/10-regexp-greedy-and-lazy/3-find-html-comments/task.md b/9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/task.md similarity index 100% rename from 10-regexp-greedy-and-lazy/3-find-html-comments/task.md rename to 9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/task.md diff --git a/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/solution.md b/9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/solution.md similarity index 100% rename from 10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/solution.md rename to 9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/solution.md diff --git a/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md b/9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md similarity index 100% rename from 10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md rename to 9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md diff --git a/10-regexp-greedy-and-lazy/article.md b/9-regular-expressions/10-regexp-greedy-and-lazy/article.md similarity index 100% rename from 10-regexp-greedy-and-lazy/article.md rename to 9-regular-expressions/10-regexp-greedy-and-lazy/article.md diff --git a/10-regexp-greedy-and-lazy/witch_greedy1.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy1.svg similarity index 100% rename from 10-regexp-greedy-and-lazy/witch_greedy1.svg rename to 9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy1.svg diff --git a/10-regexp-greedy-and-lazy/witch_greedy2.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy2.svg similarity index 100% rename from 10-regexp-greedy-and-lazy/witch_greedy2.svg rename to 9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy2.svg diff --git a/10-regexp-greedy-and-lazy/witch_greedy3.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy3.svg similarity index 100% rename from 10-regexp-greedy-and-lazy/witch_greedy3.svg rename to 9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy3.svg diff --git a/10-regexp-greedy-and-lazy/witch_greedy4.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy4.svg similarity index 100% rename from 10-regexp-greedy-and-lazy/witch_greedy4.svg rename to 9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy4.svg diff --git a/10-regexp-greedy-and-lazy/witch_greedy5.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy5.svg similarity index 100% rename from 10-regexp-greedy-and-lazy/witch_greedy5.svg rename to 9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy5.svg diff --git a/10-regexp-greedy-and-lazy/witch_greedy6.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy6.svg similarity index 100% rename from 10-regexp-greedy-and-lazy/witch_greedy6.svg rename to 9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy6.svg diff --git a/10-regexp-greedy-and-lazy/witch_lazy3.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy3.svg similarity index 100% rename from 10-regexp-greedy-and-lazy/witch_lazy3.svg rename to 9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy3.svg diff --git a/10-regexp-greedy-and-lazy/witch_lazy4.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy4.svg similarity index 100% rename from 10-regexp-greedy-and-lazy/witch_lazy4.svg rename to 9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy4.svg diff --git a/10-regexp-greedy-and-lazy/witch_lazy5.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy5.svg similarity index 100% rename from 10-regexp-greedy-and-lazy/witch_lazy5.svg rename to 9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy5.svg diff --git a/10-regexp-greedy-and-lazy/witch_lazy6.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy6.svg similarity index 100% rename from 10-regexp-greedy-and-lazy/witch_lazy6.svg rename to 9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy6.svg diff --git a/11-regexp-groups/01-test-mac/solution.md b/9-regular-expressions/11-regexp-groups/01-test-mac/solution.md similarity index 100% rename from 11-regexp-groups/01-test-mac/solution.md rename to 9-regular-expressions/11-regexp-groups/01-test-mac/solution.md diff --git a/11-regexp-groups/01-test-mac/task.md b/9-regular-expressions/11-regexp-groups/01-test-mac/task.md similarity index 100% rename from 11-regexp-groups/01-test-mac/task.md rename to 9-regular-expressions/11-regexp-groups/01-test-mac/task.md diff --git a/11-regexp-groups/02-find-webcolor-3-or-6/solution.md b/9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/solution.md similarity index 100% rename from 11-regexp-groups/02-find-webcolor-3-or-6/solution.md rename to 9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/solution.md diff --git a/11-regexp-groups/02-find-webcolor-3-or-6/task.md b/9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/task.md similarity index 100% rename from 11-regexp-groups/02-find-webcolor-3-or-6/task.md rename to 9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/task.md diff --git a/11-regexp-groups/03-find-decimal-numbers/solution.md b/9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/solution.md similarity index 100% rename from 11-regexp-groups/03-find-decimal-numbers/solution.md rename to 9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/solution.md diff --git a/11-regexp-groups/03-find-decimal-numbers/task.md b/9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/task.md similarity index 100% rename from 11-regexp-groups/03-find-decimal-numbers/task.md rename to 9-regular-expressions/11-regexp-groups/03-find-decimal-numbers/task.md diff --git a/11-regexp-groups/04-parse-expression/solution.md b/9-regular-expressions/11-regexp-groups/04-parse-expression/solution.md similarity index 100% rename from 11-regexp-groups/04-parse-expression/solution.md rename to 9-regular-expressions/11-regexp-groups/04-parse-expression/solution.md diff --git a/11-regexp-groups/04-parse-expression/task.md b/9-regular-expressions/11-regexp-groups/04-parse-expression/task.md similarity index 100% rename from 11-regexp-groups/04-parse-expression/task.md rename to 9-regular-expressions/11-regexp-groups/04-parse-expression/task.md diff --git a/11-regexp-groups/article.md b/9-regular-expressions/11-regexp-groups/article.md similarity index 100% rename from 11-regexp-groups/article.md rename to 9-regular-expressions/11-regexp-groups/article.md diff --git a/11-regexp-groups/regexp-nested-groups-matches.svg b/9-regular-expressions/11-regexp-groups/regexp-nested-groups-matches.svg similarity index 100% rename from 11-regexp-groups/regexp-nested-groups-matches.svg rename to 9-regular-expressions/11-regexp-groups/regexp-nested-groups-matches.svg diff --git a/11-regexp-groups/regexp-nested-groups-pattern.svg b/9-regular-expressions/11-regexp-groups/regexp-nested-groups-pattern.svg similarity index 100% rename from 11-regexp-groups/regexp-nested-groups-pattern.svg rename to 9-regular-expressions/11-regexp-groups/regexp-nested-groups-pattern.svg diff --git a/12-regexp-backreferences/article.md b/9-regular-expressions/12-regexp-backreferences/article.md similarity index 100% rename from 12-regexp-backreferences/article.md rename to 9-regular-expressions/12-regexp-backreferences/article.md diff --git a/13-regexp-alternation/01-find-programming-language/solution.md b/9-regular-expressions/13-regexp-alternation/01-find-programming-language/solution.md similarity index 100% rename from 13-regexp-alternation/01-find-programming-language/solution.md rename to 9-regular-expressions/13-regexp-alternation/01-find-programming-language/solution.md diff --git a/13-regexp-alternation/01-find-programming-language/task.md b/9-regular-expressions/13-regexp-alternation/01-find-programming-language/task.md similarity index 100% rename from 13-regexp-alternation/01-find-programming-language/task.md rename to 9-regular-expressions/13-regexp-alternation/01-find-programming-language/task.md diff --git a/13-regexp-alternation/02-find-matching-bbtags/solution.md b/9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/solution.md similarity index 100% rename from 13-regexp-alternation/02-find-matching-bbtags/solution.md rename to 9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/solution.md diff --git a/13-regexp-alternation/02-find-matching-bbtags/task.md b/9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/task.md similarity index 100% rename from 13-regexp-alternation/02-find-matching-bbtags/task.md rename to 9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/task.md diff --git a/13-regexp-alternation/03-match-quoted-string/solution.md b/9-regular-expressions/13-regexp-alternation/03-match-quoted-string/solution.md similarity index 100% rename from 13-regexp-alternation/03-match-quoted-string/solution.md rename to 9-regular-expressions/13-regexp-alternation/03-match-quoted-string/solution.md diff --git a/13-regexp-alternation/03-match-quoted-string/task.md b/9-regular-expressions/13-regexp-alternation/03-match-quoted-string/task.md similarity index 100% rename from 13-regexp-alternation/03-match-quoted-string/task.md rename to 9-regular-expressions/13-regexp-alternation/03-match-quoted-string/task.md diff --git a/13-regexp-alternation/04-match-exact-tag/solution.md b/9-regular-expressions/13-regexp-alternation/04-match-exact-tag/solution.md similarity index 100% rename from 13-regexp-alternation/04-match-exact-tag/solution.md rename to 9-regular-expressions/13-regexp-alternation/04-match-exact-tag/solution.md diff --git a/13-regexp-alternation/04-match-exact-tag/task.md b/9-regular-expressions/13-regexp-alternation/04-match-exact-tag/task.md similarity index 100% rename from 13-regexp-alternation/04-match-exact-tag/task.md rename to 9-regular-expressions/13-regexp-alternation/04-match-exact-tag/task.md diff --git a/13-regexp-alternation/article.md b/9-regular-expressions/13-regexp-alternation/article.md similarity index 100% rename from 13-regexp-alternation/article.md rename to 9-regular-expressions/13-regexp-alternation/article.md diff --git a/14-regexp-lookahead-lookbehind/1-find-non-negative-integers/solution.md b/9-regular-expressions/14-regexp-lookahead-lookbehind/1-find-non-negative-integers/solution.md similarity index 100% rename from 14-regexp-lookahead-lookbehind/1-find-non-negative-integers/solution.md rename to 9-regular-expressions/14-regexp-lookahead-lookbehind/1-find-non-negative-integers/solution.md diff --git a/14-regexp-lookahead-lookbehind/1-find-non-negative-integers/task.md b/9-regular-expressions/14-regexp-lookahead-lookbehind/1-find-non-negative-integers/task.md similarity index 100% rename from 14-regexp-lookahead-lookbehind/1-find-non-negative-integers/task.md rename to 9-regular-expressions/14-regexp-lookahead-lookbehind/1-find-non-negative-integers/task.md diff --git a/14-regexp-lookahead-lookbehind/2-insert-after-head/solution.md b/9-regular-expressions/14-regexp-lookahead-lookbehind/2-insert-after-head/solution.md similarity index 100% rename from 14-regexp-lookahead-lookbehind/2-insert-after-head/solution.md rename to 9-regular-expressions/14-regexp-lookahead-lookbehind/2-insert-after-head/solution.md diff --git a/14-regexp-lookahead-lookbehind/2-insert-after-head/task.md b/9-regular-expressions/14-regexp-lookahead-lookbehind/2-insert-after-head/task.md similarity index 100% rename from 14-regexp-lookahead-lookbehind/2-insert-after-head/task.md rename to 9-regular-expressions/14-regexp-lookahead-lookbehind/2-insert-after-head/task.md diff --git a/14-regexp-lookahead-lookbehind/article.md b/9-regular-expressions/14-regexp-lookahead-lookbehind/article.md similarity index 100% rename from 14-regexp-lookahead-lookbehind/article.md rename to 9-regular-expressions/14-regexp-lookahead-lookbehind/article.md diff --git a/15-regexp-catastrophic-backtracking/article.md b/9-regular-expressions/15-regexp-catastrophic-backtracking/article.md similarity index 100% rename from 15-regexp-catastrophic-backtracking/article.md rename to 9-regular-expressions/15-regexp-catastrophic-backtracking/article.md diff --git a/16-regexp-sticky/article.md b/9-regular-expressions/16-regexp-sticky/article.md similarity index 100% rename from 16-regexp-sticky/article.md rename to 9-regular-expressions/16-regexp-sticky/article.md diff --git a/17-regexp-methods/article.md b/9-regular-expressions/17-regexp-methods/article.md similarity index 100% rename from 17-regexp-methods/article.md rename to 9-regular-expressions/17-regexp-methods/article.md diff --git a/index.md b/9-regular-expressions/index.md similarity index 100% rename from index.md rename to 9-regular-expressions/index.md From ad806d428938a3f80c217a364ec0ac64d3aa4571 Mon Sep 17 00:00:00 2001 From: Osvaldo Dias dos Santos Date: Fri, 18 Dec 2020 21:28:59 +0100 Subject: [PATCH 34/34] Delete old directory --- .../01-regexp-introduction/article-pt.md | 127 ------ .../01-regexp-introduction/article.md | 177 --------- .../02-regexp-character-classes/article.md | 203 ---------- .../love-html5-classes.svg | 1 - .../03-regexp-character-classes/article.md | 265 ------------- .../love-html5-classes.svg | 1 - .../03-regexp-unicode/article.md | 161 -------- .../04-regexp-anchors/1-start-end/solution.md | 5 - .../04-regexp-anchors/1-start-end/task.md | 3 - .../04-regexp-anchors/article.md | 52 --- .../article.md | 114 ------ .../05-regexp-multiline-mode/article.md | 87 ----- .../1-find-time-hh-mm/solution.md | 6 - .../1-find-time-hh-mm/task.md | 9 - .../06-regexp-boundary/article.md | 52 --- .../hello-java-boundaries.svg | 1 - .../07-regexp-escaping/article.md | 99 ----- .../1-find-range-1/solution.md | 12 - .../1-find-range-1/task.md | 5 - .../2-find-time-2-formats/solution.md | 8 - .../2-find-time-2-formats/task.md | 12 - .../article.md | 197 ---------- .../solution.md | 18 - .../3-find-decimal-positive-numbers/task.md | 12 - .../4-find-decimal-numbers/solution.md | 11 - .../4-find-decimal-numbers/task.md | 13 - .../09-regexp-groups/article.md | 237 ------------ .../1-find-text-manydots/solution.md | 9 - .../1-find-text-manydots/task.md | 14 - .../2-find-html-colors-6hex/solution.md | 31 -- .../2-find-html-colors-6hex/task.md | 15 - .../09-regexp-quantifiers/article.md | 142 ------- .../10-regexp-backreferences/article.md | 65 ---- .../1-lazy-greedy/solution.md | 6 - .../1-lazy-greedy/task.md | 7 - .../3-find-html-comments/solution.md | 15 - .../3-find-html-comments/task.md | 13 - .../4-find-html-tags-greedy-lazy/solution.md | 10 - .../4-find-html-tags-greedy-lazy/task.md | 15 - .../10-regexp-greedy-and-lazy/article.md | 301 --------------- .../witch_greedy1.svg | 1 - .../witch_greedy2.svg | 1 - .../witch_greedy3.svg | 1 - .../witch_greedy4.svg | 1 - .../witch_greedy5.svg | 1 - .../witch_greedy6.svg | 1 - .../10-regexp-greedy-and-lazy/witch_lazy3.svg | 1 - .../10-regexp-greedy-and-lazy/witch_lazy4.svg | 1 - .../10-regexp-greedy-and-lazy/witch_lazy5.svg | 1 - .../10-regexp-greedy-and-lazy/witch_lazy6.svg | 1 - .../02-find-matching-bbtags/solution.md | 23 -- .../11-regexp-alternation/article.md | 59 --- .../11-regexp-groups/01-test-mac/solution.md | 21 - .../11-regexp-groups/01-test-mac/task.md | 20 - .../02-find-webcolor-3-or-6/solution.md | 27 -- .../02-find-webcolor-3-or-6/task.md | 14 - .../04-parse-expression/solution.md | 56 --- .../04-parse-expression/task.md | 28 -- .../11-regexp-groups/article.md | 364 ------------------ .../regexp-nested-groups-matches.svg | 1 - .../regexp-nested-groups-pattern.svg | 1 - .../12-regexp-backreferences/article.md | 72 ---- .../01-find-programming-language/solution.md | 33 -- .../01-find-programming-language/task.md | 11 - .../02-find-matching-bbtags/solution.md | 23 -- .../02-find-matching-bbtags/task.md | 48 --- .../03-match-quoted-string/solution.md | 17 - .../03-match-quoted-string/task.md | 32 -- .../04-match-exact-tag/solution.md | 16 - .../04-match-exact-tag/task.md | 13 - .../13-regexp-alternation/article.md | 70 ---- .../13-regexp-multiline-mode/article.md | 76 ---- .../1-find-non-negative-integers/solution.md | 28 -- .../1-find-non-negative-integers/task.md | 14 - .../2-insert-after-head/solution.md | 36 -- .../2-insert-after-head/task.md | 30 -- .../14-regexp-lookahead-lookbehind/article.md | 130 ------- .../article.md | 318 --------------- .../article.md | 293 -------------- .../16-regexp-sticky/article.md | 138 ------- .../17-regexp-methods/article.md | 361 ----------------- .../20-regexp-unicode/article.md | 89 ----- .../21-regexp-unicode-properties/article.md | 86 ----- .../22-regexp-sticky/article.md | 71 ---- 9-regular-expressions/index.md | 3 - 85 files changed, 5163 deletions(-) delete mode 100644 9-regular-expressions/01-regexp-introduction/article-pt.md delete mode 100644 9-regular-expressions/01-regexp-introduction/article.md delete mode 100644 9-regular-expressions/02-regexp-character-classes/article.md delete mode 100644 9-regular-expressions/02-regexp-character-classes/love-html5-classes.svg delete mode 100644 9-regular-expressions/03-regexp-character-classes/article.md delete mode 100644 9-regular-expressions/03-regexp-character-classes/love-html5-classes.svg delete mode 100644 9-regular-expressions/03-regexp-unicode/article.md delete mode 100644 9-regular-expressions/04-regexp-anchors/1-start-end/solution.md delete mode 100644 9-regular-expressions/04-regexp-anchors/1-start-end/task.md delete mode 100644 9-regular-expressions/04-regexp-anchors/article.md delete mode 100644 9-regular-expressions/05-regexp-character-sets-and-ranges/article.md delete mode 100644 9-regular-expressions/05-regexp-multiline-mode/article.md delete mode 100644 9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/solution.md delete mode 100644 9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/task.md delete mode 100644 9-regular-expressions/06-regexp-boundary/article.md delete mode 100644 9-regular-expressions/06-regexp-boundary/hello-java-boundaries.svg delete mode 100644 9-regular-expressions/07-regexp-escaping/article.md delete mode 100644 9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/solution.md delete mode 100644 9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/task.md delete mode 100644 9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/solution.md delete mode 100644 9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/task.md delete mode 100644 9-regular-expressions/08-regexp-character-sets-and-ranges/article.md delete mode 100644 9-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/solution.md delete mode 100644 9-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/task.md delete mode 100644 9-regular-expressions/09-regexp-groups/4-find-decimal-numbers/solution.md delete mode 100644 9-regular-expressions/09-regexp-groups/4-find-decimal-numbers/task.md delete mode 100644 9-regular-expressions/09-regexp-groups/article.md delete mode 100644 9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/solution.md delete mode 100644 9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/task.md delete mode 100644 9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/solution.md delete mode 100644 9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/task.md delete mode 100644 9-regular-expressions/09-regexp-quantifiers/article.md delete mode 100644 9-regular-expressions/10-regexp-backreferences/article.md delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/solution.md delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/task.md delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/solution.md delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/task.md delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/solution.md delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/article.md delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy1.svg delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy2.svg delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy3.svg delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy4.svg delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy5.svg delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy6.svg delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy3.svg delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy4.svg delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy5.svg delete mode 100644 9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy6.svg delete mode 100644 9-regular-expressions/11-regexp-alternation/02-find-matching-bbtags/solution.md delete mode 100644 9-regular-expressions/11-regexp-alternation/article.md delete mode 100644 9-regular-expressions/11-regexp-groups/01-test-mac/solution.md delete mode 100644 9-regular-expressions/11-regexp-groups/01-test-mac/task.md delete mode 100644 9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/solution.md delete mode 100644 9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/task.md delete mode 100644 9-regular-expressions/11-regexp-groups/04-parse-expression/solution.md delete mode 100644 9-regular-expressions/11-regexp-groups/04-parse-expression/task.md delete mode 100644 9-regular-expressions/11-regexp-groups/article.md delete mode 100644 9-regular-expressions/11-regexp-groups/regexp-nested-groups-matches.svg delete mode 100644 9-regular-expressions/11-regexp-groups/regexp-nested-groups-pattern.svg delete mode 100644 9-regular-expressions/12-regexp-backreferences/article.md delete mode 100644 9-regular-expressions/13-regexp-alternation/01-find-programming-language/solution.md delete mode 100644 9-regular-expressions/13-regexp-alternation/01-find-programming-language/task.md delete mode 100644 9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/solution.md delete mode 100644 9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/task.md delete mode 100644 9-regular-expressions/13-regexp-alternation/03-match-quoted-string/solution.md delete mode 100644 9-regular-expressions/13-regexp-alternation/03-match-quoted-string/task.md delete mode 100644 9-regular-expressions/13-regexp-alternation/04-match-exact-tag/solution.md delete mode 100644 9-regular-expressions/13-regexp-alternation/04-match-exact-tag/task.md delete mode 100644 9-regular-expressions/13-regexp-alternation/article.md delete mode 100644 9-regular-expressions/13-regexp-multiline-mode/article.md delete mode 100644 9-regular-expressions/14-regexp-lookahead-lookbehind/1-find-non-negative-integers/solution.md delete mode 100644 9-regular-expressions/14-regexp-lookahead-lookbehind/1-find-non-negative-integers/task.md delete mode 100644 9-regular-expressions/14-regexp-lookahead-lookbehind/2-insert-after-head/solution.md delete mode 100644 9-regular-expressions/14-regexp-lookahead-lookbehind/2-insert-after-head/task.md delete mode 100644 9-regular-expressions/14-regexp-lookahead-lookbehind/article.md delete mode 100644 9-regular-expressions/15-regexp-catastrophic-backtracking/article.md delete mode 100644 9-regular-expressions/15-regexp-infinite-backtracking-problem/article.md delete mode 100644 9-regular-expressions/16-regexp-sticky/article.md delete mode 100644 9-regular-expressions/17-regexp-methods/article.md delete mode 100644 9-regular-expressions/20-regexp-unicode/article.md delete mode 100644 9-regular-expressions/21-regexp-unicode-properties/article.md delete mode 100644 9-regular-expressions/22-regexp-sticky/article.md delete mode 100644 9-regular-expressions/index.md diff --git a/9-regular-expressions/01-regexp-introduction/article-pt.md b/9-regular-expressions/01-regexp-introduction/article-pt.md deleted file mode 100644 index 23b70545e..000000000 --- a/9-regular-expressions/01-regexp-introduction/article-pt.md +++ /dev/null @@ -1,127 +0,0 @@ -# Padrões e flags - -Expressão regular é uma maneira poderosa de buscar e substituir dentro de uma string. - -Em JavaScript, expressões regulares são implementadas usando objetos de uma classe 'RegExp' embutida e integrada com strings. - -Observe que as expressões regulares variam entre linguagens de programação. Nesse tutorial, nos concentramos em JavaScript. Claro que há muito em comum, mas existem algumas diferenças em Perl, Ruby, PHP, etc. - -## Expressões regulares - -Uma expressão regular (também "regexp", ou apenas "reg") consiste em um *padrão* e opcionais *flags*. - -Existem duas sintaxes para criar um objeto de expressão regular. - -A sintaxe longa: - -```js -regexp = new RegExp("padrão", "flags"); -``` - -...E a curta, usando barras `"/"`: - -```js -regexp = /padrão/; // sem flags -regexp = /padrão/gmi; // com flags `g`, `m` e `i` (que serão detalhadas em breve) -``` - -Barras `"/"` dizem ao JavaScript que estamos criando uma expressão regular. Elas desempenham o mesmo papel que as aspas para strings. - -## Uso - -Para buscar dentro de uma string, nós podemos usar o método [search](mdn:js/String/search). - -Aqui está um exemplo: - -```js run -let str = "Eu amo JavaScript!"; // irá buscar aqui - -let regexp = /amo/; -alert( str.search(regexp) ); // 3 -``` - -O método `str.search` busca pelo `padrão:/amo/` e retorna a posição dentro da string. Como devemos adivinhar, `padrão:/love/` é o padrão mais simples possível. O que ele faz é uma simples pesquisa de substring. - -O código acima é o mesmo que: - -```js run -let str = "Eu amo JavaScript!"; // irá buscar aqui - -let substr = 'amo'; -alert( str.search(substr) ); // 3 -``` - -Então procurar pelo `padrão:/amo/` é o mesmo que procurar por `"amo"`. - -Mas isso é apenas por agora. Em breve nós iremos criar expressões regulares mais complexas com muito mais poder de busca. - -```smart header="Cores" -A partir daqui, o esquema de cores é: - -- regexp -- `padrão:vermelho` -- string (onde nós buscamos) -- `sujeito:azul` -- result -- `correspondência:verde` -``` - - -````smart header="Quando usar `new RegExp`?" -Normalmente nós usamos a sintaxe curta `/.../`. Mas não suporta inserções variáveis `${...}`. - -Por outro lado, `new RegExp` permite construir um padrão dinamicamente de uma string, então é mais flexível. - -Aqui está um exemplo de uma regexp gerada dinamicamente: - -```js run -let tag = prompt("Qual tag quer procurar?", "h2"); -let regexp = new RegExp(`<${tag}>`); - -// procura

por padrão -alert( "

".search(regexp)); -``` -```` - - -## Flags - -Expressões regulares podem ter flags que afetam a busca. - -Existem apenas 5 delas em JavaScript: - -`i` -: Com essa flag a busca é case-insensitive: não faz distinção entre `A` e `a` (veja o exemplo abaixo). - -`g` -: Com essa flag a busca procura por todas as correspondências, sem ela -- apenas a primeira (nós veremos usos no próximo capítulo). - -`m` -: Modo multilinha (abordado no capítulo ). - -`s` -: Modo "dotall", permite que o `.` corresponda às novas linhas (abordado no capítulo ). - -`u` -: Habilita o suporte completo a unicode. A flag permite o processamento correto dos pares substitutos. Mais sobre isso no capítulo . - -`y` -: Modo fixo (abordado no capítulo ) - -Abordaremos todas essas flags mais adiante no tutorial. - -Por agora, a mais simples flag é `i`, aqui está um exemplo: - -```js run -let str = "Eu amo JavaScript!"; - -alert( str.search(/AMO/i) ); // 3 (encontrado em minúsculas) - -alert( str.search(/AMO/) ); // -1 (nada encontrado sem a 'i' flag) -``` - -Então a `i` flag já faz as expressões regulares mais poderosas do que uma simples pesquisa de substring. Mas ainda há muito mais. Abodaremos outras flags e recursos nos próximos capítulos. - - -## Sumário - -- Uma expressão regular consiste de um padrão e flags opcionais: `g`, `i`, `m`, `u`, `s`, `y`. -- Sem flags e símbolos especiais que iremos estudar depois, a busca por uma regexp é o mesmo que uma pesquisa de substring. -- O método `str.search(regexp)` retorna o índice onde a correspondência é encontrada ou `-1` se não há correspondência. No próximo capítulo veremos outros métodos. diff --git a/9-regular-expressions/01-regexp-introduction/article.md b/9-regular-expressions/01-regexp-introduction/article.md deleted file mode 100644 index ade62cdf2..000000000 --- a/9-regular-expressions/01-regexp-introduction/article.md +++ /dev/null @@ -1,177 +0,0 @@ -# Patterns and flags - -Regular expressions are patterns that provide a powerful way to search and replace in text. - -In JavaScript, they are available via the [RegExp](mdn:js/RegExp) object, as well as being integrated in methods of strings. - -## Regular Expressions - -A regular expression (also "regexp", or just "reg") consists of a *pattern* and optional *flags*. - -There are two syntaxes that can be used to create a regular expression object. - -The "long" syntax: - -```js -regexp = new RegExp("pattern", "flags"); -``` - -And the "short" one, using slashes `"/"`: - -```js -regexp = /pattern/; // no flags -regexp = /pattern/gmi; // with flags g,m and i (to be covered soon) -``` - -Slashes `pattern:/.../` tell JavaScript that we are creating a regular expression. They play the same role as quotes for strings. - -In both cases `regexp` becomes an instance of the built-in `RegExp` class. - -The main difference between these two syntaxes is that pattern using slashes `/.../` does not allow for expressions to be inserted (like string template literals with `${...}`). They are fully static. - -Slashes are used when we know the regular expression at the code writing time -- and that's the most common situation. While `new RegExp` is more often used when we need to create a regexp "on the fly" from a dynamically generated string. For instance: - -```js -let tag = prompt("What tag do you want to find?", "h2"); - -let regexp = new RegExp(`<${tag}>`); // same as /

/ if answered "h2" in the prompt above -``` - -## Flags - -Regular expressions may have flags that affect the search. - -There are only 6 of them in JavaScript: - -`pattern:i` -: With this flag the search is case-insensitive: no difference between `A` and `a` (see the example below). - -`pattern:g` -: With this flag the search looks for all matches, without it -- only the first match is returned. - -`pattern:m` -: Multiline mode (covered in the chapter ). - -`pattern:s` -: Enables "dotall" mode, that allows a dot `pattern:.` to match newline character `\n` (covered in the chapter ). - -`pattern:u` -: Enables full Unicode support. The flag enables correct processing of surrogate pairs. More about that in the chapter . - -`pattern:y` -: "Sticky" mode: searching at the exact position in the text (covered in the chapter ) - -```smart header="Colors" -From here on the color scheme is: - -- regexp -- `pattern:red` -- string (where we search) -- `subject:blue` -- result -- `match:green` -``` - -## Searching: str.match - -As mentioned previously, regular expressions are integrated with string methods. - -The method `str.match(regexp)` finds all matches of `regexp` in the string `str`. - -It has 3 working modes: - -1. If the regular expression has flag `pattern:g`, it returns an array of all matches: - ```js run - let str = "We will, we will rock you"; - - alert( str.match(/we/gi) ); // We,we (an array of 2 substrings that match) - ``` - Please note that both `match:We` and `match:we` are found, because flag `pattern:i` makes the regular expression case-insensitive. - -2. If there's no such flag it returns only the first match in the form of an array, with the full match at index `0` and some additional details in properties: - ```js run - let str = "We will, we will rock you"; - - let result = str.match(/we/i); // without flag g - - alert( result[0] ); // We (1st match) - alert( result.length ); // 1 - - // Details: - alert( result.index ); // 0 (position of the match) - alert( result.input ); // We will, we will rock you (source string) - ``` - The array may have other indexes, besides `0` if a part of the regular expression is enclosed in parentheses. We'll cover that in the chapter . - -3. And, finally, if there are no matches, `null` is returned (doesn't matter if there's flag `pattern:g` or not). - - This a very important nuance. If there are no matches, we don't receive an empty array, but instead receive `null`. Forgetting about that may lead to errors, e.g.: - - ```js run - let matches = "JavaScript".match(/HTML/); // = null - - if (!matches.length) { // Error: Cannot read property 'length' of null - alert("Error in the line above"); - } - ``` - - If we'd like the result to always be an array, we can write it this way: - - ```js run - let matches = "JavaScript".match(/HTML/)*!* || []*/!*; - - if (!matches.length) { - alert("No matches"); // now it works - } - ``` - -## Replacing: str.replace - -The method `str.replace(regexp, replacement)` replaces matches found using `regexp` in string `str` with `replacement` (all matches if there's flag `pattern:g`, otherwise, only the first one). - -For instance: - -```js run -// no flag g -alert( "We will, we will".replace(/we/i, "I") ); // I will, we will - -// with flag g -alert( "We will, we will".replace(/we/ig, "I") ); // I will, I will -``` - -The second argument is the `replacement` string. We can use special character combinations in it to insert fragments of the match: - -| Symbols | Action in the replacement string | -|--------|--------| -|`$&`|inserts the whole match| -|$`|inserts a part of the string before the match| -|`$'`|inserts a part of the string after the match| -|`$n`|if `n` is a 1-2 digit number, then it inserts the contents of n-th parentheses, more about it in the chapter | -|`$`|inserts the contents of the parentheses with the given `name`, more about it in the chapter | -|`$$`|inserts character `$` | - -An example with `pattern:$&`: - -```js run -alert( "I love HTML".replace(/HTML/, "$& and JavaScript") ); // I love HTML and JavaScript -``` - -## Testing: regexp.test - -The method `regexp.test(str)` looks for at least one match, if found, returns `true`, otherwise `false`. - -```js run -let str = "I love JavaScript"; -let regexp = /LOVE/i; - -alert( regexp.test(str) ); // true -``` - -Later in this chapter we'll study more regular expressions, walk through more examples, and also meet other methods. - -Full information about the methods is given in the article . - -## Summary - -- A regular expression consists of a pattern and optional flags: `pattern:g`, `pattern:i`, `pattern:m`, `pattern:u`, `pattern:s`, `pattern:y`. -- Without flags and special symbols (that we'll study later), the search by a regexp is the same as a substring search. -- The method `str.match(regexp)` looks for matches: all of them if there's `pattern:g` flag, otherwise, only the first one. -- The method `str.replace(regexp, replacement)` replaces matches found using `regexp` with `replacement`: all of them if there's `pattern:g` flag, otherwise only the first one. -- The method `regexp.test(str)` returns `true` if there's at least one match, otherwise, it returns `false`. diff --git a/9-regular-expressions/02-regexp-character-classes/article.md b/9-regular-expressions/02-regexp-character-classes/article.md deleted file mode 100644 index 6917b29f4..000000000 --- a/9-regular-expressions/02-regexp-character-classes/article.md +++ /dev/null @@ -1,203 +0,0 @@ -# Classes de caracteres - -Considere uma tarefa prática - temos um número de telefone como `"+7(903)-123-45-67"` e precisamos transformá-lo em números puros: `79031234567`. - -Para fazer isso, podemos encontrar e remover qualquer coisa que não seja um número. Classes de personagens podem ajudar com isso. - -Uma *classe de caracteres* é uma notação especial que corresponde a qualquer símbolo de um determinado conjunto. - -Para começar, vamos explorar a classe "digit". Está escrito como `padrão: \d` e corresponde a "qualquer dígito único". - -Por exemplo, vamos encontrar o primeiro dígito no número de telefone: - -```js run -let str = "+7(903)-123-45-67"; - -let regexp = /\d/; - -alert( str.match(regexp) ); // 7 -``` - -Sem a flag `padrão:g`, a expressão regular procura apenas a primeira correspondência, que é o primeiro dígito `padrão:\d`. - -Vamos adicionar a flag `padrão:g` para encontrar todos os dígitos: - -```js run -let str = "+7(903)-123-45-67"; - -let regexp = /\d/g; - -alert( str.match(regexp) ); // matriz de correspondências: 7,9,0,3,1,2,3,4,5,6,7 - -// vamos criar o número de telefone apenas com dígitos: -alert( str.match(regexp).join('') ); // 79031234567 -``` - -Essa era uma classe de caracteres para dígitos. Existem outras classes de caracteres também. - -As mais usadas são: - -`padrão:\d` ("d" é de "digit") -: Um dígito: um caractere de `0` a `9`. - -`padrão:\s` ("s" é de "space") -: Um símbolo de espaço: inclui espaços, tabulações `\t`, novas linhas `\n` e alguns outros caracteres raros, como `\v`, `\f` and `\r`. - -`padrão:\w` ("w" é de "word") -: Um caractere de texto: uma letra do alfabeto latino ou um dígito ou um sublinhado `_`. Letras não latinas (como cirílico ou hindu) não pertencem ao `padrão:\w`. - -Por exemplo, `padrão:\d\s\w` significa um "dígito" seguido de um "caractere de espaço" seguido de um "caractere de texto", como `correspondência:1 a`. - -**Uma regexp pode conter símbolos regulares e classes de caracteres.** - -Por exemplo, `padrão:CSS\d` corresponde a uma string `correspondência:CSS` com um dígito após: - -```js run -let str = "Existe CSS4?"; -let regexp = /CSS\d/ - -alert( str.match(regexp) ); // CSS4 -``` - -Também podemos usar muitas classes de caracteres: - -```js run -alert( "Eu amo HTML5!".match(/\s\w\w\w\w\d/) ); // ' HTML5' -``` - -A correspondência (cada classe de caracteres regexp possui o caractere de resultado correspondente): - -![](love-html5-classes.svg) - -## Classes inversas - -Para cada classe de caractere existe uma "classe inversa", denotada com a mesma letra, mas em maiúsculas. - -O "inverso" significa que ele corresponde a todos os outros caracteres, por exemplo: - -`padrão:\D` -: Sem dígito: qualquer caractere, exceto `padrão:\d`, por exemplo, uma letra. - -`padrão:\S` -: Sem espaço: qualquer caractere, exceto `padrão:\s`, por exemplo, uma letra. - -`padrão:\W` -: Caractere não verbal: qualquer coisa, exceto `padrão:\w`, por exemplo uma letra não latina ou um espaço. - -No início do capítulo, vimos como criar um número de telefone somente para números a partir de uma string como `subject:+7(903)-123-45-67`: encontre todos os dígitos e junte-se a eles. - -```js run -let str = "+7(903)-123-45-67"; - -alert( str.match(/\d/g).join('') ); // 79031234567 -``` - -Uma maneira alternativa e mais curta é encontrar um `padrão:\D` não-dígito e removê-lo da string: - -```js run -let str = "+7(903)-123-45-67"; - -alert( str.replace(/\D/g, "") ); // 79031234567 -``` - -## Um ponto é "qualquer caractere" - -Um ponto `padrão:.` é uma classe de caractere especial que corresponde a "qualquer caractere, exceto uma nova linha". - -Por exemplo: - -```js run -alert( "Z".match(/./) ); // Z -``` - -Ou no meio de uma regexp: - -```js run -let regexp = /CS.4/; - -alert( "CSS4".match(regexp) ); // CSS4 -alert( "CS-4".match(regexp) ); // CS-4 -alert( "CS 4".match(regexp) ); // CS 4 (o espaço é também um caractere) -``` - -Observe que um ponto significa "qualquer caractere", mas não a "ausência de um caractere". Deve haver um caractere para corresponder a ele: - -```js run -alert( "CS4".match(/CS.4/) ); // null, sem correspondência porque não há caractere para o ponto -``` - -### Ponto como literalmente qualquer caractere com a flag "s" - -Por padrão, um ponto não corresponde ao caractere de nova linha `\n`. - -Por exemplo, a regexp `padrão:A.B` corresponde `corresponde:A` e, em seguida, `corresponde:B` com qualquer caractere entre eles, exceto uma nova linha `\n`: - -```js run -alert( "A\nB".match(/A.B/) ); // null (sem correspondência) -``` - -Há muitas situações em que gostaríamos que um ponto significasse literalmente "qualquer caractere", incluindo a nova linha. - -É o que flag `padrão:s` faz. Se uma regexp possui, então um ponto `padrão:.` corresponde literalmente a qualquer caractere: - -```js run -alert( "A\nB".match(/A.B/s) ); // A\nB (correspondência!) -``` - -````warn header="Não suportado no IE" -A flag `padrão:s` não é suportada no IE. - -Felizmente, há uma alternativa, que funciona em qualquer lugar. Podemos usar uma regexp como `padrão:[\s\S]` para corresponder a "qualquer caractere" (este padrão irá ser estudado no artigo ). - -```js run -alert( "A\nB".match(/A[\s\S]B/) ); // A\nB (match!) -``` - -O padrão `padrão:[\s\S]` diz literalmente: "um caractere de espaço OU não um caractere de espaço". Em outras palavras, "qualquer coisa". Poderíamos usar outro par de classes complementares, como `padrão:[\d\D]`, que não importa. Ou mesmo o padrão `padrão:[^]` - pois significa corresponder a qualquer caractere, exceto nada. - -Também podemos usar esse truque se quisermos os dois tipos de "pontos" no mesmo padrão: o ponto real `padrão:.` comportando-se da maneira regular ("não incluindo uma nova linha") e também uma maneira de combinar "qualquer caractere" com `padrão:[\s\S]` ou similar. -```` - -````warn header="Preste atenção nos espaços" -Geralmente prestamos pouca atenção aos espaços. Para nós, as strings `sujeito:1-5` e `sujeito:1 - 5` são quase idênticas. - -Mas se uma regexp não leva em consideração os espaços, ela pode falhar. - -Vamos tentar encontrar dígitos separados por um hífen: - -```js run -alert( "1 - 5".match(/\d-\d/) ); // null, sem correspondência! -``` - -Vamos corrigi-lo adicionando espaços ao padrão regexp `padrão:\d - \d`: - -```js run -alert( "1 - 5".match(/\d - \d/) ); // 1 - 5, agora funciona -// ou podemos usar a classe \s: -alert( "1 - 5".match(/\d\s-\s\d/) ); // 1 - 5, também funciona -``` - -**Um espaço é um caractere. Igual em importância com qualquer outro caractere.** - -Não podemos adicionar ou remover espaços de uma expressão regular e esperar que funcione da mesma maneira. - -Em outras palavras, em uma expressão regular, todos os caracteres são importantes, espaços também. -```` - -## Resumo - -Existem as seguintes classes de caracteres: - -- `padrão:\d` - dígitos. -- `padrão:\D` - sem dígitos. -- `padrão:\s` - símbolos de espaço, tabulações, novas linhas. -- `padrão:\S` - todos, exceto `padrão:\s`. -- `padrão:\w` - Letras latinas, dígitos, sublinhado `'_'`. -- `padrão:\W` - todos, exceto `padrão:\w`. -- `padrão:.` - qualquer caractere se estiver com a flag regexp `'s' `; caso contrário, qualquer um, exceto uma nova linha `\n`. - -...Mas isso não é tudo! - -A codificação unicode, usada pelo JavaScript para strings, fornece muitas propriedades para caracteres, como: a qual idioma a letra pertence (se é uma letra), será que é um sinal de pontuação, etc. - -Também podemos pesquisar por essas propriedades. Isso requer a flag `padrão:u`, abordada no próximo artigo. diff --git a/9-regular-expressions/02-regexp-character-classes/love-html5-classes.svg b/9-regular-expressions/02-regexp-character-classes/love-html5-classes.svg deleted file mode 100644 index 9c88cc088..000000000 --- a/9-regular-expressions/02-regexp-character-classes/love-html5-classes.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/9-regular-expressions/03-regexp-character-classes/article.md b/9-regular-expressions/03-regexp-character-classes/article.md deleted file mode 100644 index a2f9869fb..000000000 --- a/9-regular-expressions/03-regexp-character-classes/article.md +++ /dev/null @@ -1,265 +0,0 @@ -# Character classes - -Consider a practical task -- we have a phone number `"+7(903)-123-45-67"`, and we need to turn it into pure numbers: `79035419441`. - -To do so, we can find and remove anything that's not a number. Character classes can help with that. - -A character class is a special notation that matches any symbol from a certain set. - -For the start, let's explore a "digit" class. It's written as `\d`. We put it in the pattern, that means "any single digit". - -For instance, the let's find the first digit in the phone number: - -```js run -let str = "+7(903)-123-45-67"; - -let reg = /\d/; - -alert( str.match(reg) ); // 7 -``` - -Without the flag `g`, the regular expression only looks for the first match, that is the first digit `\d`. - -Let's add the `g` flag to find all digits: - -```js run -let str = "+7(903)-123-45-67"; - -let reg = /\d/g; - -alert( str.match(reg) ); // array of matches: 7,9,0,3,1,2,3,4,5,6,7 - -alert( str.match(reg).join('') ); // 79035419441 -``` - -That was a character class for digits. There are other character classes as well. - -Most used are: - -`\d` ("d" is from "digit") -: A digit: a character from `0` to `9`. - -`\s` ("s" is from "space") -: A space symbol: that includes spaces, tabs, newlines. - -`\w` ("w" is from "word") -: A "wordly" character: either a letter of English alphabet or a digit or an underscore. Non-english letters (like cyrillic or hindi) do not belong to `\w`. - -For instance, `pattern:\d\s\w` means a "digit" followed by a "space character" followed by a "wordly character", like `"1 a"`. - -**A regexp may contain both regular symbols and character classes.** - -For instance, `pattern:CSS\d` matches a string `match:CSS` with a digit after it: - -```js run -let str = "CSS4 is cool"; -let reg = /CSS\d/ - -alert( str.match(reg) ); // CSS4 -``` - -Also we can use many character classes: - -```js run -alert( "I love HTML5!".match(/\s\w\w\w\w\d/) ); // 'HTML5' -``` - -The match (each character class corresponds to one result character): - -![](love-html5-classes.svg) - -## Word boundary: \b - -A word boundary `pattern:\b` -- is a special character class. - -It does not denote a character, but rather a boundary between characters. - -For instance, `pattern:\bJava\b` matches `match:Java` in the string `subject:Hello, Java!`, but not in the script `subject:Hello, JavaScript!`. - -```js run -alert( "Hello, Java!".match(/\bJava\b/) ); // Java -alert( "Hello, JavaScript!".match(/\bJava\b/) ); // null -``` - -The boundary has "zero width" in a sense that usually a character class means a character in the result (like a wordly character or a digit), but not in this case. - -The boundary is a test. - -When regular expression engine is doing the search, it's moving along the string in an attempt to find the match. At each string position it tries to find the pattern. - -When the pattern contains `pattern:\b`, it tests that the position in string is a word boundary, that is one of three variants: - -- Immediately before is `\w`, and immediately after -- not `\w`, or vise versa. -- At string start, and the first string character is `\w`. -- At string end, and the last string character is `\w`. - -For instance, in the string `subject:Hello, Java!` the following positions match `\b`: - -![](hello-java-boundaries.svg) - -So it matches `pattern:\bHello\b`, because: - -1. At the beginning of the string the first `\b` test matches. -2. Then the word `Hello` matches. -3. Then `\b` matches, as we're between `o` and a space. - -Pattern `pattern:\bJava\b` also matches. But not `pattern:\bHell\b` (because there's no word boundary after `l`) and not `Java!\b` (because the exclamation sign is not a wordly character, so there's no word boundary after it). - - -```js run -alert( "Hello, Java!".match(/\bHello\b/) ); // Hello -alert( "Hello, Java!".match(/\bJava\b/) ); // Java -alert( "Hello, Java!".match(/\bHell\b/) ); // null (no match) -alert( "Hello, Java!".match(/\bJava!\b/) ); // null (no match) -``` - -Once again let's note that `pattern:\b` makes the searching engine to test for the boundary, so that `pattern:Java\b` finds `match:Java` only when followed by a word boundary, but it does not add a letter to the result. - -Usually we use `\b` to find standalone English words. So that if we want `"Java"` language then `pattern:\bJava\b` finds exactly a standalone word and ignores it when it's a part of `"JavaScript"`. - -Another example: a regexp `pattern:\b\d\d\b` looks for standalone two-digit numbers. In other words, it requires that before and after `pattern:\d\d` must be a symbol different from `\w` (or beginning/end of the string). - -```js run -alert( "1 23 456 78".match(/\b\d\d\b/g) ); // 23,78 -``` - -```warn header="Word boundary doesn't work for non-English alphabets" -The word boundary check `\b` tests for a boundary between `\w` and something else. But `\w` means an English letter (or a digit or an underscore), so the test won't work for other characters (like cyrillic or hieroglyphs). -``` - - -## Inverse classes - -For every character class there exists an "inverse class", denoted with the same letter, but uppercased. - -The "reverse" means that it matches all other characters, for instance: - -`\D` -: Non-digit: any character except `\d`, for instance a letter. - -`\S` -: Non-space: any character except `\s`, for instance a letter. - -`\W` -: Non-wordly character: anything but `\w`. - -`\B` -: Non-boundary: a test reverse to `\b`. - -In the beginning of the chapter we saw how to get all digits from the phone `subject:+7(903)-123-45-67`. - -One way was to match all digits and join them: - -```js run -let str = "+7(903)-123-45-67"; - -alert( str.match(/\d/g).join('') ); // 79031234567 -``` - -An alternative, shorter way is to find non-digits `\D` and remove them from the string: - - -```js run -let str = "+7(903)-123-45-67"; - -alert( str.replace(/\D/g, "") ); // 79031234567 -``` - -## Spaces are regular characters - -Usually we pay little attention to spaces. For us strings `subject:1-5` and `subject:1 - 5` are nearly identical. - -But if a regexp doesn't take spaces into account, it may fail to work. - -Let's try to find digits separated by a dash: - -```js run -alert( "1 - 5".match(/\d-\d/) ); // null, no match! -``` - -Here we fix it by adding spaces into the regexp `pattern:\d - \d`: - -```js run -alert( "1 - 5".match(/\d - \d/) ); // 1 - 5, now it works -``` - -**A space is a character. Equal in importance with any other character.** - -Of course, spaces in a regexp are needed only if we look for them. Extra spaces (just like any other extra characters) may prevent a match: - -```js run -alert( "1-5".match(/\d - \d/) ); // null, because the string 1-5 has no spaces -``` - -In other words, in a regular expression all characters matter, spaces too. - -## A dot is any character - -The dot `"."` is a special character class that matches "any character except a newline". - -For instance: - -```js run -alert( "Z".match(/./) ); // Z -``` - -Or in the middle of a regexp: - -```js run -let reg = /CS.4/; - -alert( "CSS4".match(reg) ); // CSS4 -alert( "CS-4".match(reg) ); // CS-4 -alert( "CS 4".match(reg) ); // CS 4 (space is also a character) -``` - -Please note that the dot means "any character", but not the "absense of a character". There must be a character to match it: - -```js run -alert( "CS4".match(/CS.4/) ); // null, no match because there's no character for the dot -``` - -### The dotall "s" flag - -Usually a dot doesn't match a newline character. - -For instance, this doesn't match: - -```js run -alert( "A\nB".match(/A.B/) ); // null (no match) - -// a space character would match -// or a letter, but not \n -``` - -Sometimes it's inconvenient, we really want "any character", newline included. - -That's what `s` flag does. If a regexp has it, then the dot `"."` match literally any character: - -```js run -alert( "A\nB".match(/A.B/s) ); // A\nB (match!) -``` - - -## Summary - -There exist following character classes: - -- `pattern:\d` -- digits. -- `pattern:\D` -- non-digits. -- `pattern:\s` -- space symbols, tabs, newlines. -- `pattern:\S` -- all but `pattern:\s`. -- `pattern:\w` -- English letters, digits, underscore `'_'`. -- `pattern:\W` -- all but `pattern:\w`. -- `pattern:.` -- any character if with the regexp `'s'` flag, otherwise any except a newline. - -...But that's not all! - -Modern JavaScript also allows to look for characters by their Unicode properties, for instance: - -- A cyrillic letter is: `pattern:\p{Script=Cyrillic}` or `pattern:\p{sc=Cyrillic}`. -- A dash (be it a small hyphen `-` or a long dash `—`): `pattern:\p{Dash_Punctuation}` or `pattern:\p{pd}`. -- A currency symbol: `pattern:\p{Currency_Symbol}` or `pattern:\p{sc}`. -- ...And much more. Unicode has a lot of character categories that we can select from. - -These patterns require `'u'` regexp flag to work. More about that in the chapter [](info:regexp-unicode). diff --git a/9-regular-expressions/03-regexp-character-classes/love-html5-classes.svg b/9-regular-expressions/03-regexp-character-classes/love-html5-classes.svg deleted file mode 100644 index c91cd03d5..000000000 --- a/9-regular-expressions/03-regexp-character-classes/love-html5-classes.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/9-regular-expressions/03-regexp-unicode/article.md b/9-regular-expressions/03-regexp-unicode/article.md deleted file mode 100644 index 3e981c80a..000000000 --- a/9-regular-expressions/03-regexp-unicode/article.md +++ /dev/null @@ -1,161 +0,0 @@ -# Unicode: flag "u" and class \p{...} - -JavaScript uses [Unicode encoding](https://en.wikipedia.org/wiki/Unicode) for strings. Most characters are encoded with 2 bytes, but that allows to represent at most 65536 characters. - -That range is not big enough to encode all possible characters, that's why some rare characters are encoded with 4 bytes, for instance like `𝒳` (mathematical X) or `😄` (a smile), some hieroglyphs and so on. - -Here are the Unicode values of some characters: - -| Character | Unicode | Bytes count in Unicode | -|------------|---------|--------| -| a | `0x0061` | 2 | -| ≈ | `0x2248` | 2 | -|𝒳| `0x1d4b3` | 4 | -|𝒴| `0x1d4b4` | 4 | -|😄| `0x1f604` | 4 | - -So characters like `a` and `≈` occupy 2 bytes, while codes for `𝒳`, `𝒴` and `😄` are longer, they have 4 bytes. - -Long time ago, when JavaScript language was created, Unicode encoding was simpler: there were no 4-byte characters. So, some language features still handle them incorrectly. - -For instance, `length` thinks that here are two characters: - -```js run -alert('😄'.length); // 2 -alert('𝒳'.length); // 2 -``` - -...But we can see that there's only one, right? The point is that `length` treats 4 bytes as two 2-byte characters. That's incorrect, because they must be considered only together (so-called "surrogate pair", you can read about them in the article ). - -By default, regular expressions also treat 4-byte "long characters" as a pair of 2-byte ones. And, as it happens with strings, that may lead to odd results. We'll see that a bit later, in the article . - -Unlike strings, regular expressions have flag `pattern:u` that fixes such problems. With such flag, a regexp handles 4-byte characters correctly. And also Unicode property search becomes available, we'll get to it next. - -## Unicode properties \p{...} - -Every character in Unicode has a lot of properties. They describe what "category" the character belongs to, contain miscellaneous information about it. - -For instance, if a character has `Letter` property, it means that the character belongs to an alphabet (of any language). And `Number` property means that it's a digit: maybe Arabic or Chinese, and so on. - -We can search for characters with a property, written as `pattern:\p{…}`. To use `pattern:\p{…}`, a regular expression must have flag `pattern:u`. - -For instance, `\p{Letter}` denotes a letter in any language. We can also use `\p{L}`, as `L` is an alias of `Letter`. There are shorter aliases for almost every property. - -In the example below three kinds of letters will be found: English, Georgian and Korean. - -```js run -let str = "A ბ ㄱ"; - -alert( str.match(/\p{L}/gu) ); // A,ბ,ㄱ -alert( str.match(/\p{L}/g) ); // null (no matches, \p doesn't work without the flag "u") -``` - -Here's the main character categories and their subcategories: - -- Letter `L`: - - lowercase `Ll` - - modifier `Lm`, - - titlecase `Lt`, - - uppercase `Lu`, - - other `Lo`. -- Number `N`: - - decimal digit `Nd`, - - letter number `Nl`, - - other `No`. -- Punctuation `P`: - - connector `Pc`, - - dash `Pd`, - - initial quote `Pi`, - - final quote `Pf`, - - open `Ps`, - - close `Pe`, - - other `Po`. -- Mark `M` (accents etc): - - spacing combining `Mc`, - - enclosing `Me`, - - non-spacing `Mn`. -- Symbol `S`: - - currency `Sc`, - - modifier `Sk`, - - math `Sm`, - - other `So`. -- Separator `Z`: - - line `Zl`, - - paragraph `Zp`, - - space `Zs`. -- Other `C`: - - control `Cc`, - - format `Cf`, - - not assigned `Cn`, - - private use `Co`, - - surrogate `Cs`. - - -So, e.g. if we need letters in lower case, we can write `pattern:\p{Ll}`, punctuation signs: `pattern:\p{P}` and so on. - -There are also other derived categories, like: -- `Alphabetic` (`Alpha`), includes Letters `L`, plus letter numbers `Nl` (e.g. Ⅻ - a character for the roman number 12), plus some other symbols `Other_Alphabetic` (`OAlpha`). -- `Hex_Digit` includes hexadecimal digits: `0-9`, `a-f`. -- ...And so on. - -Unicode supports many different properties, their full list would require a lot of space, so here are the references: - -- List all properties by a character: . -- List all characters by a property: . -- Short aliases for properties: . -- A full base of Unicode characters in text format, with all properties, is here: . - -### Example: hexadecimal numbers - -For instance, let's look for hexadecimal numbers, written as `xFF`, where `F` is a hex digit (0..1 or A..F). - -A hex digit can be denoted as `pattern:\p{Hex_Digit}`: - -```js run -let regexp = /x\p{Hex_Digit}\p{Hex_Digit}/u; - -alert("number: xAF".match(regexp)); // xAF -``` - -### Example: Chinese hieroglyphs - -Let's look for Chinese hieroglyphs. - -There's a Unicode property `Script` (a writing system), that may have a value: `Cyrillic`, `Greek`, `Arabic`, `Han` (Chinese) and so on, [here's the full list](https://en.wikipedia.org/wiki/Script_(Unicode)). - -To look for characters in a given writing system we should use `pattern:Script=`, e.g. for Cyrillic letters: `pattern:\p{sc=Cyrillic}`, for Chinese hieroglyphs: `pattern:\p{sc=Han}`, and so on: - -```js run -let regexp = /\p{sc=Han}/gu; // returns Chinese hieroglyphs - -let str = `Hello Привет 你好 123_456`; - -alert( str.match(regexp) ); // 你,好 -``` - -### Example: currency - -Characters that denote a currency, such as `$`, `€`, `¥`, have Unicode property `pattern:\p{Currency_Symbol}`, the short alias: `pattern:\p{Sc}`. - -Let's use it to look for prices in the format "currency, followed by a digit": - -```js run -let regexp = /\p{Sc}\d/gu; - -let str = `Prices: $2, €1, ¥9`; - -alert( str.match(regexp) ); // $2,€1,¥9 -``` - -Later, in the article we'll see how to look for numbers that contain many digits. - -## Summary - -Flag `pattern:u` enables the support of Unicode in regular expressions. - -That means two things: - -1. Characters of 4 bytes are handled correctly: as a single character, not two 2-byte characters. -2. Unicode properties can be used in the search: `\p{…}`. - -With Unicode properties we can look for words in given languages, special characters (quotes, currencies) and so on. diff --git a/9-regular-expressions/04-regexp-anchors/1-start-end/solution.md b/9-regular-expressions/04-regexp-anchors/1-start-end/solution.md deleted file mode 100644 index 702f992d7..000000000 --- a/9-regular-expressions/04-regexp-anchors/1-start-end/solution.md +++ /dev/null @@ -1,5 +0,0 @@ -An empty string is the only match: it starts and immediately finishes. - -The task once again demonstrates that anchors are not characters, but tests. - -The string is empty `""`. The engine first matches the `pattern:^` (input start), yes it's there, and then immediately the end `pattern:$`, it's here too. So there's a match. diff --git a/9-regular-expressions/04-regexp-anchors/1-start-end/task.md b/9-regular-expressions/04-regexp-anchors/1-start-end/task.md deleted file mode 100644 index abdfec938..000000000 --- a/9-regular-expressions/04-regexp-anchors/1-start-end/task.md +++ /dev/null @@ -1,3 +0,0 @@ -# Regexp ^$ - -Which string matches the pattern `pattern:^$`? diff --git a/9-regular-expressions/04-regexp-anchors/article.md b/9-regular-expressions/04-regexp-anchors/article.md deleted file mode 100644 index c34999ee5..000000000 --- a/9-regular-expressions/04-regexp-anchors/article.md +++ /dev/null @@ -1,52 +0,0 @@ -# Anchors: string start ^ and end $ - -The caret `pattern:^` and dollar `pattern:$` characters have special meaning in a regexp. They are called "anchors". - -The caret `pattern:^` matches at the beginning of the text, and the dollar `pattern:$` -- at the end. - -For instance, let's test if the text starts with `Mary`: - -```js run -let str1 = "Mary had a little lamb"; -alert( /^Mary/.test(str1) ); // true -``` - -The pattern `pattern:^Mary` means: "string start and then Mary". - -Similar to this, we can test if the string ends with `snow` using `pattern:snow$`: - -```js run -let str1 = "it's fleece was white as snow"; -alert( /snow$/.test(str1) ); // true -``` - -In these particular cases we could use string methods `startsWith/endsWith` instead. Regular expressions should be used for more complex tests. - -## Testing for a full match - -Both anchors together `pattern:^...$` are often used to test whether or not a string fully matches the pattern. For instance, to check if the user input is in the right format. - -Let's check whether or not a string is a time in `12:34` format. That is: two digits, then a colon, and then another two digits. - -In regular expressions language that's `pattern:\d\d:\d\d`: - -```js run -let goodInput = "12:34"; -let badInput = "12:345"; - -let regexp = /^\d\d:\d\d$/; -alert( regexp.test(goodInput) ); // true -alert( regexp.test(badInput) ); // false -``` - -Here the match for `pattern:\d\d:\d\d` must start exactly after the beginning of the text `pattern:^`, and the end `pattern:$` must immediately follow. - -The whole string must be exactly in this format. If there's any deviation or an extra character, the result is `false`. - -Anchors behave differently if flag `pattern:m` is present. We'll see that in the next article. - -```smart header="Anchors have \"zero width\"" -Anchors `pattern:^` and `pattern:$` are tests. They have zero width. - -In other words, they do not match a character, but rather force the regexp engine to check the condition (text start/end). -``` diff --git a/9-regular-expressions/05-regexp-character-sets-and-ranges/article.md b/9-regular-expressions/05-regexp-character-sets-and-ranges/article.md deleted file mode 100644 index 5c8a8babb..000000000 --- a/9-regular-expressions/05-regexp-character-sets-and-ranges/article.md +++ /dev/null @@ -1,114 +0,0 @@ -# Sets and ranges [...] - -Several characters or character classes inside square brackets `[…]` mean to "search for any character among given". - -## Sets - -For instance, `pattern:[eao]` means any of the 3 characters: `'a'`, `'e'`, or `'o'`. - -That's called a *set*. Sets can be used in a regexp along with regular characters: - -```js run -// find [t or m], and then "op" -alert( "Mop top".match(/[tm]op/gi) ); // "Mop", "top" -``` - -Please note that although there are multiple characters in the set, they correspond to exactly one character in the match. - -So the example above gives no matches: - -```js run -// find "V", then [o or i], then "la" -alert( "Voila".match(/V[oi]la/) ); // null, no matches -``` - -The pattern assumes: - -- `pattern:V`, -- then *one* of the letters `pattern:[oi]`, -- then `pattern:la`. - -So there would be a match for `match:Vola` or `match:Vila`. - -## Ranges - -Square brackets may also contain *character ranges*. - -For instance, `pattern:[a-z]` is a character in range from `a` to `z`, and `pattern:[0-5]` is a digit from `0` to `5`. - -In the example below we're searching for `"x"` followed by two digits or letters from `A` to `F`: - -```js run -alert( "Exception 0xAF".match(/x[0-9A-F][0-9A-F]/g) ); // xAF -``` - -Please note that in the word `subject:Exception` there's a substring `subject:xce`. It didn't match the pattern, because the letters are lowercase, while in the set `pattern:[0-9A-F]` they are uppercase. - -If we want to find it too, then we can add a range `a-f`: `pattern:[0-9A-Fa-f]`. The `i` flag would allow lowercase too. - -**Character classes are shorthands for certain character sets.** - -For instance: - -- **\d** -- is the same as `pattern:[0-9]`, -- **\w** -- is the same as `pattern:[a-zA-Z0-9_]`, -- **\s** -- is the same as `pattern:[\t\n\v\f\r ]` plus few other unicode space characters. - -We can use character classes inside `[…]` as well. - -For instance, we want to match all wordly characters or a dash, for words like "twenty-third". We can't do it with `pattern:\w+`, because `pattern:\w` class does not include a dash. But we can use `pattern:[\w-]`. - -We also can use a combination of classes to cover every possible character, like `pattern:[\s\S]`. That matches spaces or non-spaces -- any character. That's wider than a dot `"."`, because the dot matches any character except a newline. - -## Excluding ranges - -Besides normal ranges, there are "excluding" ranges that look like `pattern:[^…]`. - -They are denoted by a caret character `^` at the start and match any character *except the given ones*. - -For instance: - -- `pattern:[^aeyo]` -- any character except `'a'`, `'e'`, `'y'` or `'o'`. -- `pattern:[^0-9]` -- any character except a digit, the same as `\D`. -- `pattern:[^\s]` -- any non-space character, same as `\S`. - -The example below looks for any characters except letters, digits and spaces: - -```js run -alert( "alice15@gmail.com".match(/[^\d\sA-Z]/gi) ); // @ and . -``` - -## No escaping in […] - -Usually when we want to find exactly the dot character, we need to escape it like `pattern:\.`. And if we need a backslash, then we use `pattern:\\`. - -In square brackets the vast majority of special characters can be used without escaping: - -- A dot `pattern:'.'`. -- A plus `pattern:'+'`. -- Parentheses `pattern:'( )'`. -- Dash `pattern:'-'` in the beginning or the end (where it does not define a range). -- A caret `pattern:'^'` if not in the beginning (where it means exclusion). -- And the opening square bracket `pattern:'['`. - -In other words, all special characters are allowed except where they mean something for square brackets. - -A dot `"."` inside square brackets means just a dot. The pattern `pattern:[.,]` would look for one of characters: either a dot or a comma. - -In the example below the regexp `pattern:[-().^+]` looks for one of the characters `-().^+`: - -```js run -// No need to escape -let reg = /[-().^+]/g; - -alert( "1 + 2 - 3".match(reg) ); // Matches +, - -``` - -...But if you decide to escape them "just in case", then there would be no harm: - -```js run -// Escaped everything -let reg = /[\-\(\)\.\^\+]/g; - -alert( "1 + 2 - 3".match(reg) ); // also works: +, - -``` diff --git a/9-regular-expressions/05-regexp-multiline-mode/article.md b/9-regular-expressions/05-regexp-multiline-mode/article.md deleted file mode 100644 index f8ac08ec7..000000000 --- a/9-regular-expressions/05-regexp-multiline-mode/article.md +++ /dev/null @@ -1,87 +0,0 @@ -# Multiline mode of anchors ^ $, flag "m" - -The multiline mode is enabled by the flag `pattern:m`. - -It only affects the behavior of `pattern:^` and `pattern:$`. - -In the multiline mode they match not only at the beginning and the end of the string, but also at start/end of line. - -## Searching at line start ^ - -In the example below the text has multiple lines. The pattern `pattern:/^\d/gm` takes a digit from the beginning of each line: - -```js run -let str = `1st place: Winnie -2nd place: Piglet -3rd place: Eeyore`; - -*!* -alert( str.match(/^\d/gm) ); // 1, 2, 3 -*/!* -``` - -Without the flag `pattern:m` only the first digit is matched: - -```js run -let str = `1st place: Winnie -2nd place: Piglet -3rd place: Eeyore`; - -*!* -alert( str.match(/^\d/g) ); // 1 -*/!* -``` - -That's because by default a caret `pattern:^` only matches at the beginning of the text, and in the multiline mode -- at the start of any line. - -```smart -"Start of a line" formally means "immediately after a line break": the test `pattern:^` in multiline mode matches at all positions preceeded by a newline character `\n`. - -And at the text start. -``` - -## Searching at line end $ - -The dollar sign `pattern:$` behaves similarly. - -The regular expression `pattern:\d$` finds the last digit in every line - -```js run -let str = `Winnie: 1 -Piglet: 2 -Eeyore: 3`; - -alert( str.match(/\d$/gm) ); // 1,2,3 -``` - -Without the flag `pattern:m`, the dollar `pattern:$` would only match the end of the whole text, so only the very last digit would be found. - -```smart -"End of a line" formally means "immediately before a line break": the test `pattern:$` in multiline mode matches at all positions succeeded by a newline character `\n`. - -And at the text end. -``` - -## Searching for \n instead of ^ $ - -To find a newline, we can use not only anchors `pattern:^` and `pattern:$`, but also the newline character `\n`. - -What's the difference? Let's see an example. - -Here we search for `pattern:\d\n` instead of `pattern:\d$`: - -```js run -let str = `Winnie: 1 -Piglet: 2 -Eeyore: 3`; - -alert( str.match(/\d\n/gm) ); // 1\n,2\n -``` - -As we can see, there are 2 matches instead of 3. - -That's because there's no newline after `subject:3` (there's text end though, so it matches `pattern:$`). - -Another difference: now every match includes a newline character `match:\n`. Unlike the anchors `pattern:^` `pattern:$`, that only test the condition (start/end of a line), `\n` is a character, so it becomes a part of the result. - -So, a `\n` in the pattern is used when we need newline characters in the result, while anchors are used to find something at the beginning/end of a line. diff --git a/9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/solution.md b/9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/solution.md deleted file mode 100644 index 829eda13e..000000000 --- a/9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/solution.md +++ /dev/null @@ -1,6 +0,0 @@ - -The answer: `pattern:\b\d\d:\d\d\b`. - -```js run -alert( "Breakfast at 09:00 in the room 123:456.".match( /\b\d\d:\d\d\b/ ) ); // 09:00 -``` diff --git a/9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/task.md b/9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/task.md deleted file mode 100644 index 95ab5777d..000000000 --- a/9-regular-expressions/06-regexp-boundary/1-find-time-hh-mm/task.md +++ /dev/null @@ -1,9 +0,0 @@ -# Find the time - -The time has a format: `hours:minutes`. Both hours and minutes has two digits, like `09:00`. - -Make a regexp to find time in the string: `subject:Breakfast at 09:00 in the room 123:456.` - -P.S. In this task there's no need to check time correctness yet, so `25:99` can also be a valid result. - -P.P.S. The regexp shouldn't match `123:456`. diff --git a/9-regular-expressions/06-regexp-boundary/article.md b/9-regular-expressions/06-regexp-boundary/article.md deleted file mode 100644 index 06b5ac9f7..000000000 --- a/9-regular-expressions/06-regexp-boundary/article.md +++ /dev/null @@ -1,52 +0,0 @@ -# Word boundary: \b - -A word boundary `pattern:\b` is a test, just like `pattern:^` and `pattern:$`. - -When the regexp engine (program module that implements searching for regexps) comes across `pattern:\b`, it checks that the position in the string is a word boundary. - -There are three different positions that qualify as word boundaries: - -- At string start, if the first string character is a word character `pattern:\w`. -- Between two characters in the string, where one is a word character `pattern:\w` and the other is not. -- At string end, if the last string character is a word character `pattern:\w`. - -For instance, regexp `pattern:\bJava\b` will be found in `subject:Hello, Java!`, where `subject:Java` is a standalone word, but not in `subject:Hello, JavaScript!`. - -```js run -alert( "Hello, Java!".match(/\bJava\b/) ); // Java -alert( "Hello, JavaScript!".match(/\bJava\b/) ); // null -``` - -In the string `subject:Hello, Java!` following positions correspond to `pattern:\b`: - -![](hello-java-boundaries.svg) - -So, it matches the pattern `pattern:\bHello\b`, because: - -1. At the beginning of the string matches the first test `pattern:\b`. -2. Then matches the word `pattern:Hello`. -3. Then the test `pattern:\b` matches again, as we're between `subject:o` and a comma. - -So the pattern `pattern:\bHello\b` would match, but not `pattern:\bHell\b` (because there's no word boundary after `l`) and not `Java!\b` (because the exclamation sign is not a wordly character `pattern:\w`, so there's no word boundary after it). - -```js run -alert( "Hello, Java!".match(/\bHello\b/) ); // Hello -alert( "Hello, Java!".match(/\bJava\b/) ); // Java -alert( "Hello, Java!".match(/\bHell\b/) ); // null (no match) -alert( "Hello, Java!".match(/\bJava!\b/) ); // null (no match) -``` - -We can use `pattern:\b` not only with words, but with digits as well. - -For example, the pattern `pattern:\b\d\d\b` looks for standalone 2-digit numbers. In other words, it looks for 2-digit numbers that are surrounded by characters different from `pattern:\w`, such as spaces or punctuation (or text start/end). - -```js run -alert( "1 23 456 78".match(/\b\d\d\b/g) ); // 23,78 -alert( "12,34,56".match(/\b\d\d\b/g) ); // 12,34,56 -``` - -```warn header="Word boundary `pattern:\b` doesn't work for non-latin alphabets" -The word boundary test `pattern:\b` checks that there should be `pattern:\w` on the one side from the position and "not `pattern:\w`" - on the other side. - -But `pattern:\w` means a latin letter `a-z` (or a digit or an underscore), so the test doesn't work for other characters, e.g. cyrillic letters or hieroglyphs. -``` diff --git a/9-regular-expressions/06-regexp-boundary/hello-java-boundaries.svg b/9-regular-expressions/06-regexp-boundary/hello-java-boundaries.svg deleted file mode 100644 index 3d421a323..000000000 --- a/9-regular-expressions/06-regexp-boundary/hello-java-boundaries.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/9-regular-expressions/07-regexp-escaping/article.md b/9-regular-expressions/07-regexp-escaping/article.md deleted file mode 100644 index eed76791b..000000000 --- a/9-regular-expressions/07-regexp-escaping/article.md +++ /dev/null @@ -1,99 +0,0 @@ - -# Escaping, special characters - -As we've seen, a backslash `pattern:\` is used to denote character classes, e.g. `pattern:\d`. So it's a special character in regexps (just like in regular strings). - -There are other special characters as well, that have special meaning in a regexp. They are used to do more powerful searches. Here's a full list of them: `pattern:[ \ ^ $ . | ? * + ( )`. - -Don't try to remember the list -- soon we'll deal with each of them separately and you'll know them by heart automatically. - -## Escaping - -Let's say we want to find literally a dot. Not "any character", but just a dot. - -To use a special character as a regular one, prepend it with a backslash: `pattern:\.`. - -That's also called "escaping a character". - -For example: -```js run -alert( "Chapter 5.1".match(/\d\.\d/) ); // 5.1 (match!) -alert( "Chapter 511".match(/\d\.\d/) ); // null (looking for a real dot \.) -``` - -Parentheses are also special characters, so if we want them, we should use `pattern:\(`. The example below looks for a string `"g()"`: - -```js run -alert( "function g()".match(/g\(\)/) ); // "g()" -``` - -If we're looking for a backslash `\`, it's a special character in both regular strings and regexps, so we should double it. - -```js run -alert( "1\\2".match(/\\/) ); // '\' -``` - -## A slash - -A slash symbol `'/'` is not a special character, but in JavaScript it is used to open and close the regexp: `pattern:/...pattern.../`, so we should escape it too. - -Here's what a search for a slash `'/'` looks like: - -```js run -alert( "/".match(/\//) ); // '/' -``` - -On the other hand, if we're not using `pattern:/.../`, but create a regexp using `new RegExp`, then we don't need to escape it: - -```js run -alert( "/".match(new RegExp("/")) ); // finds / -``` - -## new RegExp - -If we are creating a regular expression with `new RegExp`, then we don't have to escape `/`, but need to do some other escaping. - -For instance, consider this: - -```js run -let regexp = new RegExp("\d\.\d"); - -alert( "Chapter 5.1".match(regexp) ); // null -``` - -The similar search in one of previous examples worked with `pattern:/\d\.\d/`, but `new RegExp("\d\.\d")` doesn't work, why? - -The reason is that backslashes are "consumed" by a string. As we may recall, regular strings have their own special characters, such as `\n`, and a backslash is used for escaping. - -Here's how "\d\.\d" is preceived: - -```js run -alert("\d\.\d"); // d.d -``` - -String quotes "consume" backslashes and interpret them on their own, for instance: - -- `\n` -- becomes a newline character, -- `\u1234` -- becomes the Unicode character with such code, -- ...And when there's no special meaning: like `pattern:\d` or `\z`, then the backslash is simply removed. - -So `new RegExp` gets a string without backslashes. That's why the search doesn't work! - -To fix it, we need to double backslashes, because string quotes turn `\\` into `\`: - -```js run -*!* -let regStr = "\\d\\.\\d"; -*/!* -alert(regStr); // \d\.\d (correct now) - -let regexp = new RegExp(regStr); - -alert( "Chapter 5.1".match(regexp) ); // 5.1 -``` - -## Summary - -- To search for special characters `pattern:[ \ ^ $ . | ? * + ( )` literally, we need to prepend them with a backslash `\` ("escape them"). -- We also need to escape `/` if we're inside `pattern:/.../` (but not inside `new RegExp`). -- When passing a string to `new RegExp`, we need to double backslashes `\\`, cause string quotes consume one of them. diff --git a/9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/solution.md b/9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/solution.md deleted file mode 100644 index 85c7748f7..000000000 --- a/9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/solution.md +++ /dev/null @@ -1,12 +0,0 @@ -Answers: **no, yes**. - -- In the script `subject:Java` it doesn't match anything, because `pattern:[^script]` means "any character except given ones". So the regexp looks for `"Java"` followed by one such symbol, but there's a string end, no symbols after it. - - ```js run - alert( "Java".match(/Java[^script]/) ); // null - ``` -- Yes, because the `pattern:[^script]` part matches the character `"S"`. It's not one of `pattern:script`. As the regexp is case-sensitive (no `pattern:i` flag), it treats `"S"` as a different character from `"s"`. - - ```js run - alert( "JavaScript".match(/Java[^script]/) ); // "JavaS" - ``` diff --git a/9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/task.md b/9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/task.md deleted file mode 100644 index 5a48e01e7..000000000 --- a/9-regular-expressions/08-regexp-character-sets-and-ranges/1-find-range-1/task.md +++ /dev/null @@ -1,5 +0,0 @@ -# Java[^script] - -We have a regexp `pattern:/Java[^script]/`. - -Does it match anything in the string `subject:Java`? In the string `subject:JavaScript`? diff --git a/9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/solution.md b/9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/solution.md deleted file mode 100644 index 69ade1b19..000000000 --- a/9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/solution.md +++ /dev/null @@ -1,8 +0,0 @@ -Answer: `pattern:\d\d[-:]\d\d`. - -```js run -let regexp = /\d\d[-:]\d\d/g; -alert( "Breakfast at 09:00. Dinner at 21-30".match(regexp) ); // 09:00, 21-30 -``` - -Please note that the dash `pattern:'-'` has a special meaning in square brackets, but only between other characters, not when it's in the beginning or at the end, so we don't need to escape it. diff --git a/9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/task.md b/9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/task.md deleted file mode 100644 index c8441caf4..000000000 --- a/9-regular-expressions/08-regexp-character-sets-and-ranges/2-find-time-2-formats/task.md +++ /dev/null @@ -1,12 +0,0 @@ -# Find the time as hh:mm or hh-mm - -The time can be in the format `hours:minutes` or `hours-minutes`. Both hours and minutes have 2 digits: `09:00` or `21-30`. - -Write a regexp to find time: - -```js -let regexp = /your regexp/g; -alert( "Breakfast at 09:00. Dinner at 21-30".match(regexp) ); // 09:00, 21-30 -``` - -P.S. In this task we assume that the time is always correct, there's no need to filter out bad strings like "45:67". Later we'll deal with that too. diff --git a/9-regular-expressions/08-regexp-character-sets-and-ranges/article.md b/9-regular-expressions/08-regexp-character-sets-and-ranges/article.md deleted file mode 100644 index a1b8f896d..000000000 --- a/9-regular-expressions/08-regexp-character-sets-and-ranges/article.md +++ /dev/null @@ -1,197 +0,0 @@ -# Sets and ranges [...] - -Several characters or character classes inside square brackets `[…]` mean to "search for any character among given". - -## Sets - -For instance, `pattern:[eao]` means any of the 3 characters: `'a'`, `'e'`, or `'o'`. - -That's called a *set*. Sets can be used in a regexp along with regular characters: - -```js run -// find [t or m], and then "op" -alert( "Mop top".match(/[tm]op/gi) ); // "Mop", "top" -``` - -Please note that although there are multiple characters in the set, they correspond to exactly one character in the match. - -So the example below gives no matches: - -```js run -// find "V", then [o or i], then "la" -alert( "Voila".match(/V[oi]la/) ); // null, no matches -``` - -The pattern searches for: - -- `pattern:V`, -- then *one* of the letters `pattern:[oi]`, -- then `pattern:la`. - -So there would be a match for `match:Vola` or `match:Vila`. - -## Ranges - -Square brackets may also contain *character ranges*. - -For instance, `pattern:[a-z]` is a character in range from `a` to `z`, and `pattern:[0-5]` is a digit from `0` to `5`. - -In the example below we're searching for `"x"` followed by two digits or letters from `A` to `F`: - -```js run -alert( "Exception 0xAF".match(/x[0-9A-F][0-9A-F]/g) ); // xAF -``` - -Here `pattern:[0-9A-F]` has two ranges: it searches for a character that is either a digit from `0` to `9` or a letter from `A` to `F`. - -If we'd like to look for lowercase letters as well, we can add the range `a-f`: `pattern:[0-9A-Fa-f]`. Or add the flag `pattern:i`. - -We can also use character classes inside `[…]`. - -For instance, if we'd like to look for a wordly character `pattern:\w` or a hyphen `pattern:-`, then the set is `pattern:[\w-]`. - -Combining multiple classes is also possible, e.g. `pattern:[\s\d]` means "a space character or a digit". - -```smart header="Character classes are shorthands for certain character sets" -For instance: - -- **\d** -- is the same as `pattern:[0-9]`, -- **\w** -- is the same as `pattern:[a-zA-Z0-9_]`, -- **\s** -- is the same as `pattern:[\t\n\v\f\r ]`, plus few other rare Unicode space characters. -``` - -### Example: multi-language \w - -As the character class `pattern:\w` is a shorthand for `pattern:[a-zA-Z0-9_]`, it can't find Chinese hieroglyphs, Cyrillic letters, etc. - -We can write a more universal pattern, that looks for wordly characters in any language. That's easy with Unicode properties: `pattern:[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]`. - -Let's decipher it. Similar to `pattern:\w`, we're making a set of our own that includes characters with following Unicode properties: - -- `Alphabetic` (`Alpha`) - for letters, -- `Mark` (`M`) - for accents, -- `Decimal_Number` (`Nd`) - for digits, -- `Connector_Punctuation` (`Pc`) - for the underscore `'_'` and similar characters, -- `Join_Control` (`Join_C`) - two special codes `200c` and `200d`, used in ligatures, e.g. in Arabic. - -An example of use: - -```js run -let regexp = /[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]/gu; - -let str = `Hi 你好 12`; - -// finds all letters and digits: -alert( str.match(regexp) ); // H,i,你,好,1,2 -``` - -Of course, we can edit this pattern: add Unicode properties or remove them. Unicode properties are covered in more details in the article . - -```warn header="Unicode properties aren't supported in IE" -Unicode properties `pattern:p{…}` are not implemented in IE. If we really need them, we can use library [XRegExp](http://xregexp.com/). - -Or just use ranges of characters in a language that interests us, e.g. `pattern:[а-я]` for Cyrillic letters. -``` - -## Excluding ranges - -Besides normal ranges, there are "excluding" ranges that look like `pattern:[^…]`. - -They are denoted by a caret character `^` at the start and match any character *except the given ones*. - -For instance: - -- `pattern:[^aeyo]` -- any character except `'a'`, `'e'`, `'y'` or `'o'`. -- `pattern:[^0-9]` -- any character except a digit, the same as `pattern:\D`. -- `pattern:[^\s]` -- any non-space character, same as `\S`. - -The example below looks for any characters except letters, digits and spaces: - -```js run -alert( "alice15@gmail.com".match(/[^\d\sA-Z]/gi) ); // @ and . -``` - -## Escaping in […] - -Usually when we want to find exactly a special character, we need to escape it like `pattern:\.`. And if we need a backslash, then we use `pattern:\\`, and so on. - -In square brackets we can use the vast majority of special characters without escaping: - -- Symbols `pattern:. + ( )` never need escaping. -- A hyphen `pattern:-` is not escaped in the beginning or the end (where it does not define a range). -- A caret `pattern:^` is only escaped in the beginning (where it means exclusion). -- The closing square bracket `pattern:]` is always escaped (if we need to look for that symbol). - -In other words, all special characters are allowed without escaping, except when they mean something for square brackets. - -A dot `.` inside square brackets means just a dot. The pattern `pattern:[.,]` would look for one of characters: either a dot or a comma. - -In the example below the regexp `pattern:[-().^+]` looks for one of the characters `-().^+`: - -```js run -// No need to escape -let regexp = /[-().^+]/g; - -alert( "1 + 2 - 3".match(regexp) ); // Matches +, - -``` - -...But if you decide to escape them "just in case", then there would be no harm: - -```js run -// Escaped everything -let regexp = /[\-\(\)\.\^\+]/g; - -alert( "1 + 2 - 3".match(regexp) ); // also works: +, - -``` - -## Ranges and flag "u" - -If there are surrogate pairs in the set, flag `pattern:u` is required for them to work correctly. - -For instance, let's look for `pattern:[𝒳𝒴]` in the string `subject:𝒳`: - -```js run -alert( '𝒳'.match(/[𝒳𝒴]/) ); // shows a strange character, like [?] -// (the search was performed incorrectly, half-character returned) -``` - -The result is incorrect, because by default regular expressions "don't know" about surrogate pairs. - -The regular expression engine thinks that `[𝒳𝒴]` -- are not two, but four characters: -1. left half of `𝒳` `(1)`, -2. right half of `𝒳` `(2)`, -3. left half of `𝒴` `(3)`, -4. right half of `𝒴` `(4)`. - -We can see their codes like this: - -```js run -for(let i=0; i<'𝒳𝒴'.length; i++) { - alert('𝒳𝒴'.charCodeAt(i)); // 55349, 56499, 55349, 56500 -}; -``` - -So, the example above finds and shows the left half of `𝒳`. - -If we add flag `pattern:u`, then the behavior will be correct: - -```js run -alert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳 -``` - -The similar situation occurs when looking for a range, such as `[𝒳-𝒴]`. - -If we forget to add flag `pattern:u`, there will be an error: - -```js run -'𝒳'.match(/[𝒳-𝒴]/); // Error: Invalid regular expression -``` - -The reason is that without flag `pattern:u` surrogate pairs are perceived as two characters, so `[𝒳-𝒴]` is interpreted as `[<55349><56499>-<55349><56500>]` (every surrogate pair is replaced with its codes). Now it's easy to see that the range `56499-55349` is invalid: its starting code `56499` is greater than the end `55349`. That's the formal reason for the error. - -With the flag `pattern:u` the pattern works correctly: - -```js run -// look for characters from 𝒳 to 𝒵 -alert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴 -``` diff --git a/9-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/solution.md b/9-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/solution.md deleted file mode 100644 index 23065413e..000000000 --- a/9-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/solution.md +++ /dev/null @@ -1,18 +0,0 @@ - -An non-negative integer number is `pattern:\d+`. We should exclude `0` as the first digit, as we don't need zero, but we can allow it in further digits. - -So that gives us `pattern:[1-9]\d*`. - -A decimal part is: `pattern:\.\d+`. - -Because the decimal part is optional, let's put it in parentheses with the quantifier `pattern:'?'`. - -Finally we have the regexp: `pattern:[1-9]\d*(\.\d+)?`: - -```js run -let reg = /[1-9]\d*(\.\d+)?/g; - -let str = "1.5 0 -5 12. 123.4."; - -alert( str.match(reg) ); // 1.5, 0, 12, 123.4 -``` diff --git a/9-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/task.md b/9-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/task.md deleted file mode 100644 index ad8c81eae..000000000 --- a/9-regular-expressions/09-regexp-groups/3-find-decimal-positive-numbers/task.md +++ /dev/null @@ -1,12 +0,0 @@ -# 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 -5 12. 123.4."; - -alert( str.match(reg) ); // 1.5, 12, 123.4 (ignores 0 and -5) -``` diff --git a/9-regular-expressions/09-regexp-groups/4-find-decimal-numbers/solution.md b/9-regular-expressions/09-regexp-groups/4-find-decimal-numbers/solution.md deleted file mode 100644 index 5bb2ad063..000000000 --- a/9-regular-expressions/09-regexp-groups/4-find-decimal-numbers/solution.md +++ /dev/null @@ -1,11 +0,0 @@ -A positive number with an optional decimal part is: `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 -``` diff --git a/9-regular-expressions/09-regexp-groups/4-find-decimal-numbers/task.md b/9-regular-expressions/09-regexp-groups/4-find-decimal-numbers/task.md deleted file mode 100644 index 121a18a41..000000000 --- a/9-regular-expressions/09-regexp-groups/4-find-decimal-numbers/task.md +++ /dev/null @@ -1,13 +0,0 @@ -# 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 -``` diff --git a/9-regular-expressions/09-regexp-groups/article.md b/9-regular-expressions/09-regexp-groups/article.md deleted file mode 100644 index 00f6a08da..000000000 --- a/9-regular-expressions/09-regexp-groups/article.md +++ /dev/null @@ -1,237 +0,0 @@ -# Capturing groups - -A part of a pattern can be enclosed in parentheses `pattern:(...)`. This is called a "capturing group". - -That has two effects: - -1. It allows to place a part of the match into a separate array. -2. If we put a quantifier after the parentheses, it applies to the parentheses as a whole, not the last character. - -## Example - -In the example below the pattern `pattern:(go)+` finds one or more `match:'go'`: - -```js run -alert( 'Gogogo now!'.match(/(go)+/i) ); // "Gogogo" -``` - -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. - -Let's make something more complex -- a regexp to match an email. - -Examples of emails: - -``` -my@mail.com -john.smith@site.com.uk -``` - -The pattern: `pattern:[-.\w]+@([\w-]+\.)+[\w-]{2,20}`. - -1. The first part `pattern:[-.\w]+` (before `@`) may include any alphanumeric word characters, a dot and a dash, to match `match:john.smith`. -2. Then `pattern:@`, and the domain. It may be a subdomain like `host.site.com.uk`, so we match it as "a word followed by a dot `pattern:([\w-]+\.)` (repeated), and then the last part must be a word: `match:com` or `match:uk` (but not very long: 2-20 characters). - -That regexp is not perfect, but good enough to fix errors or occasional mistypes. - -For instance, we can find all emails in the string: - -```js run -let reg = /[-.\w]+@([\w-]+\.)+[\w-]{2,20}/g; - -alert("my@mail.com @ his@site.com.uk".match(reg)); // my@mail.com, his@site.com.uk -``` - -In this example parentheses were used to make a group for repetitions `pattern:([\w-]+\.)+`. But there are other uses too, let's see them. - -## Contents of parentheses - -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. - -For instance, we'd like to find HTML tags `pattern:<.*?>`, and process them. - -Let's wrap the inner content into parentheses, like this: `pattern:<(.*?)>`. - -Then we'll get both the tag as a whole and its content: - -```js run -let str = '

Hello, world!

'; -let reg = /<(.*?)>/; - -alert( str.match(reg) ); // Array: ["

", "h1"] -``` - -The call to [String#match](mdn:js/String/match) returns groups only if the regexp has no `pattern:/.../g` flag. - -If we need all matches with their groups then we can use `.matchAll` or `regexp.exec` as described in : - -```js run -let str = '

Hello, world!

'; - -// two matches: opening

and closing

tags -let reg = /<(.*?)>/g; - -let matches = Array.from( str.matchAll(reg) ); - -alert(matches[0]); // Array: ["

", "h1"] -alert(matches[1]); // Array: ["

", "/h1"] -``` - -Here we have two matches for `pattern:<(.*?)>`, each of them is an array with the full match and groups. - -## Nested groups - -Parentheses can be nested. In this case the numbering also goes from left to right. - -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 -let str = ''; - -let reg = /<(([a-z]+)\s*([^>]*))>/; - -let result = str.match(reg); -alert(result); // , span class="my", span, class="my" -``` - -Here's how groups look: - -![](regexp-nested-groups.svg) - -At the zero index of the `result` is always the full match. - -Then groups, numbered from left to right. Whichever opens first gives the first group `result[1]`. Here it encloses the whole tag content. - -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]`. - -**Even if a group is optional and doesn't exist in the match, the corresponding `result` array item is present (and equals `undefined`).** - -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 -let match = 'a'.match(/a(z)?(c)?/); - -alert( match.length ); // 3 -alert( match[0] ); // a (whole match) -alert( match[1] ); // undefined -alert( match[2] ); // undefined -``` - -The array has the length of `3`, but all groups are empty. - -And here's a more complex match for the string `subject:ack`: - -```js run -let match = 'ack'.match(/a(z)?(c)?/) - -alert( match.length ); // 3 -alert( match[0] ); // ac (whole match) -alert( match[1] ); // undefined, because there's nothing for (z)? -alert( match[2] ); // c -``` - -The array length is permanent: `3`. But there's nothing for the group `pattern:(z)?`, so the result is `["ac", undefined, "c"]`. - -## Named groups - -Remembering groups by their numbers is hard. For simple patterns it's doable, but for more complex ones we can give names to parentheses. - -That's done by putting `pattern:?` immediately after the opening paren, like this: - -```js run -*!* -let dateRegexp = /(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})/; -*/!* -let str = "2019-04-30"; - -let groups = str.match(dateRegexp).groups; - -alert(groups.year); // 2019 -alert(groups.month); // 04 -alert(groups.day); // 30 -``` - -As you can see, the groups reside in the `.groups` property of the match. - -We can also use them in replacements, as `pattern:$` (like `$1..9`, but name instead of a digit). - -For instance, let's rearrange the date into `day.month.year`: - -```js run -let dateRegexp = /(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})/; - -let str = "2019-04-30"; - -let rearranged = str.replace(dateRegexp, '$.$.$'); - -alert(rearranged); // 30.04.2019 -``` - -If we use a function, then named `groups` object is always the last argument: - -```js run -let dateRegexp = /(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})/; - -let str = "2019-04-30"; - -let rearranged = str.replace(dateRegexp, - (str, year, month, day, offset, input, groups) => - `${groups.day}.${groups.month}.${groups.year}` -); - -alert(rearranged); // 30.04.2019 -``` - -Usually, when we intend to use named groups, we don't need positional arguments of the function. For the majority of real-life cases we only need `str` and `groups`. - -So we can write it a little bit shorter: - -```js -let rearranged = str.replace(dateRegexp, (str, ...args) => { - let {year, month, day} = args.pop(); - alert(str); // 2019-04-30 - alert(year); // 2019 - alert(month); // 04 - alert(day); // 30 -}); -``` - - -## Non-capturing groups with ?: - -Sometimes we need parentheses to correctly apply a quantifier, but we don't want their contents in results. - -A group may be excluded by adding `pattern:?:` in the beginning. - -For instance, if we want to find `pattern:(go)+`, but don't want to remember the contents (`go`) in a separate array item, we can write: `pattern:(?:go)+`. - -In the example below we only get the name "John" as a separate member of the `results` array: - -```js run -let str = "Gogo John!"; -*!* -// exclude Gogo from capturing -let reg = /(?:go)+ (\w+)/i; -*/!* - -let result = str.match(reg); - -alert( result.length ); // 2 -alert( result[1] ); // John -``` - -## Summary - -- Parentheses can be: - - capturing `(...)`, ordered left-to-right, accessible by number. - - named capturing `(?...)`, accessible by name. - - non-capturing `(?:...)`, used only to apply quantifier to the whole groups. diff --git a/9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/solution.md b/9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/solution.md deleted file mode 100644 index 21b8762ec..000000000 --- a/9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/solution.md +++ /dev/null @@ -1,9 +0,0 @@ - -Solution: - -```js run -let regexp = /\.{3,}/g; -alert( "Hello!... How goes?.....".match(regexp) ); // ..., ..... -``` - -Please note that the dot is a special character, so we have to escape it and insert as `\.`. diff --git a/9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/task.md b/9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/task.md deleted file mode 100644 index 4140b4a98..000000000 --- a/9-regular-expressions/09-regexp-quantifiers/1-find-text-manydots/task.md +++ /dev/null @@ -1,14 +0,0 @@ -importance: 5 - ---- - -# How to find an ellipsis "..." ? - -Create a regexp to find ellipsis: 3 (or more?) dots in a row. - -Check it: - -```js -let regexp = /your regexp/g; -alert( "Hello!... How goes?.....".match(regexp) ); // ..., ..... -``` diff --git a/9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/solution.md b/9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/solution.md deleted file mode 100644 index afee89c50..000000000 --- a/9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/solution.md +++ /dev/null @@ -1,31 +0,0 @@ -We need to look for `#` followed by 6 hexadecimal characters. - -A hexadecimal character can be described as `pattern:[0-9a-fA-F]`. Or if we use the `pattern:i` flag, then just `pattern:[0-9a-f]`. - -Then we can look for 6 of them using the quantifier `pattern:{6}`. - -As a result, we have the regexp: `pattern:/#[a-f0-9]{6}/gi`. - -```js run -let regexp = /#[a-f0-9]{6}/gi; - -let str = "color:#121212; background-color:#AA00ef bad-colors:f#fddee #fd2" - -alert( str.match(regexp) ); // #121212,#AA00ef -``` - -The problem is that it finds the color in longer sequences: - -```js run -alert( "#12345678".match( /#[a-f0-9]{6}/gi ) ) // #123456 -``` - -To fix that, we can add `pattern:\b` to the end: - -```js run -// color -alert( "#123456".match( /#[a-f0-9]{6}\b/gi ) ); // #123456 - -// not a color -alert( "#12345678".match( /#[a-f0-9]{6}\b/gi ) ); // null -``` diff --git a/9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/task.md b/9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/task.md deleted file mode 100644 index 9a1923a7e..000000000 --- a/9-regular-expressions/09-regexp-quantifiers/2-find-html-colors-6hex/task.md +++ /dev/null @@ -1,15 +0,0 @@ -# Regexp for HTML colors - -Create a regexp to search HTML-colors written as `#ABCDEF`: first `#` and then 6 hexadecimal characters. - -An example of use: - -```js -let regexp = /...your regexp.../ - -let str = "color:#121212; background-color:#AA00ef bad-colors:f#fddee #fd2 #12345678"; - -alert( str.match(regexp) ) // #121212,#AA00ef -``` - -P.S. In this task we do not need other color formats like `#123` or `rgb(1,2,3)` etc. diff --git a/9-regular-expressions/09-regexp-quantifiers/article.md b/9-regular-expressions/09-regexp-quantifiers/article.md deleted file mode 100644 index 1a7eecfeb..000000000 --- a/9-regular-expressions/09-regexp-quantifiers/article.md +++ /dev/null @@ -1,142 +0,0 @@ -# Quantifiers +, *, ? and {n} - -Let's say we have a string like `+7(903)-123-45-67` and want to find all numbers in it. But unlike before, we are interested not in single digits, but full numbers: `7, 903, 123, 45, 67`. - -A number is a sequence of 1 or more digits `pattern:\d`. To mark how many we need, we can append a *quantifier*. - -## Quantity {n} - -The simplest quantifier is a number in curly braces: `pattern:{n}`. - -A quantifier is appended to a character (or a character class, or a `[...]` set etc) and specifies how many we need. - -It has a few advanced forms, let's see examples: - -The exact count: `pattern:{5}` -: `pattern:\d{5}` denotes exactly 5 digits, the same as `pattern:\d\d\d\d\d`. - - The example below looks for a 5-digit number: - - ```js run - alert( "I'm 12345 years old".match(/\d{5}/) ); // "12345" - ``` - - We can add `\b` to exclude longer numbers: `pattern:\b\d{5}\b`. - -The range: `pattern:{3,5}`, match 3-5 times -: To find numbers from 3 to 5 digits we can put the limits into curly braces: `pattern:\d{3,5}` - - ```js run - alert( "I'm not 12, but 1234 years old".match(/\d{3,5}/) ); // "1234" - ``` - - We can omit the upper limit. - - Then a regexp `pattern:\d{3,}` looks for sequences of digits of length `3` or more: - - ```js run - alert( "I'm not 12, but 345678 years old".match(/\d{3,}/) ); // "345678" - ``` - -Let's return to the string `+7(903)-123-45-67`. - -A number is a sequence of one or more digits in a row. So the regexp is `pattern:\d{1,}`: - -```js run -let str = "+7(903)-123-45-67"; - -let numbers = str.match(/\d{1,}/g); - -alert(numbers); // 7,903,123,45,67 -``` - -## Shorthands - -There are shorthands for most used quantifiers: - -`pattern:+` -: Means "one or more", the same as `pattern:{1,}`. - - For instance, `pattern:\d+` looks for numbers: - - ```js run - let str = "+7(903)-123-45-67"; - - alert( str.match(/\d+/g) ); // 7,903,123,45,67 - ``` - -`pattern:?` -: Means "zero or one", the same as `pattern:{0,1}`. In other words, it makes the symbol optional. - - For instance, the pattern `pattern:ou?r` looks for `match:o` followed by zero or one `match:u`, and then `match:r`. - - So, `pattern:colou?r` finds both `match:color` and `match:colour`: - - ```js run - let str = "Should I write color or colour?"; - - alert( str.match(/colou?r/g) ); // color, colour - ``` - -`pattern:*` -: Means "zero or more", the same as `pattern:{0,}`. That is, the character may repeat any times or be absent. - - For example, `pattern:\d0*` looks for a digit followed by any number of zeroes (may be many or none): - - ```js run - alert( "100 10 1".match(/\d0*/g) ); // 100, 10, 1 - ``` - - Compare it with `pattern:+` (one or more): - - ```js run - alert( "100 10 1".match(/\d0+/g) ); // 100, 10 - // 1 not matched, as 0+ requires at least one zero - ``` - -## More examples - -Quantifiers are used very often. They serve as the main "building block" of complex regular expressions, so let's see more examples. - -**Regexp for decimal fractions (a number with a floating point): `pattern:\d+\.\d+`** - -In action: -```js run -alert( "0 1 12.345 7890".match(/\d+\.\d+/g) ); // 12.345 -``` - -**Regexp for an "opening HTML-tag without attributes", such as `` or `

`.** - -1. The simplest one: `pattern:/<[a-z]+>/i` - - ```js run - alert( " ... ".match(/<[a-z]+>/gi) ); // - ``` - - The regexp looks for character `pattern:'<'` followed by one or more Latin letters, and then `pattern:'>'`. - -2. Improved: `pattern:/<[a-z][a-z0-9]*>/i` - - According to the standard, HTML tag name may have a digit at any position except the first one, like `

`. - - ```js run - alert( "

Hi!

".match(/<[a-z][a-z0-9]*>/gi) ); //

- ``` - -**Regexp "opening or closing HTML-tag without attributes": `pattern:/<\/?[a-z][a-z0-9]*>/i`** - -We added an optional slash `pattern:/?` near the beginning of the pattern. Had to escape it with a backslash, otherwise JavaScript would think it is the pattern end. - -```js run -alert( "

Hi!

".match(/<\/?[a-z][a-z0-9]*>/gi) ); //

,

-``` - -```smart header="To make a regexp more precise, we often need make it more complex" -We can see one common rule in these examples: the more precise is the regular expression -- the longer and more complex it is. - -For instance, for HTML tags we could use a simpler regexp: `pattern:<\w+>`. But as HTML has stricter restrictions for a tag name, `pattern:<[a-z][a-z0-9]*>` is more reliable. - -Can we use `pattern:<\w+>` or we need `pattern:<[a-z][a-z0-9]*>`? - -In real life both variants are acceptable. Depends on how tolerant we can be to "extra" matches and whether it's difficult or not to remove them from the result by other means. -``` diff --git a/9-regular-expressions/10-regexp-backreferences/article.md b/9-regular-expressions/10-regexp-backreferences/article.md deleted file mode 100644 index a7a934e4a..000000000 --- a/9-regular-expressions/10-regexp-backreferences/article.md +++ /dev/null @@ -1,65 +0,0 @@ -# Backreferences in pattern: \n and \k - -Capturing groups can be accessed not only in the result or in the replacement string, but also in the pattern itself. - -## Backreference by number: \n - -A group can be referenced in the pattern using `\n`, where `n` is the group number. - -To make things clear let's consider a task. - -We need to find a quoted string: either a single-quoted `subject:'...'` or a double-quoted `subject:"..."` -- both variants need to match. - -How to look for them? - -We can put two kinds of quotes in the pattern: `pattern:['"](.*?)['"]`, but it would find strings with mixed quotes, like `match:"...'` and `match:'..."`. That would lead to incorrect matches when one quote appears inside other ones, like the string `subject:"She's the one!"`: - -```js run -let str = `He said: "She's the one!".`; - -let reg = /['"](.*?)['"]/g; - -// The result is not what we expect -alert( str.match(reg) ); // "She' -``` - -As we can see, the pattern found an opening quote `match:"`, then the text is consumed lazily till the other quote `match:'`, that closes the match. - -To make sure that the pattern looks for the closing quote exactly the same as the opening one, we can make a groups of it and use the backreference. - -Here's the correct code: - -```js run -let str = `He said: "She's the one!".`; - -*!* -let reg = /(['"])(.*?)\1/g; -*/!* - -alert( str.match(reg) ); // "She's the one!" -``` - -Now it works! The regular expression engine finds the first quote `pattern:(['"])` and remembers the content of `pattern:(...)`, that's the first capturing group. - -Further in the pattern `pattern:\1` means "find the same text as in the first group", exactly the same quote in our case. - -Please note: - -- To reference a group inside a replacement string -- we use `$1`, while in the pattern -- a backslash `\1`. -- If we use `?:` in the group, then we can't reference it. Groups that are excluded from capturing `(?:...)` are not remembered by the engine. - -## Backreference by name: `\k` - -For named groups, we can backreference by `\k`. - -The same example with the named group: - -```js run -let str = `He said: "She's the one!".`; - -*!* -let reg = /(?['"])(.*?)\k/g; -*/!* - -alert( str.match(reg) ); // "She's the one!" -``` diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/solution.md b/9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/solution.md deleted file mode 100644 index b8e022223..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/solution.md +++ /dev/null @@ -1,6 +0,0 @@ - -The result is: `match:123 4`. - -First the lazy `pattern:\d+?` tries to take as little digits as it can, but it has to reach the space, so it takes `match:123`. - -Then the second `\d+?` takes only one digit, because that's enough. diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/task.md b/9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/task.md deleted file mode 100644 index 596f61a4e..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/1-lazy-greedy/task.md +++ /dev/null @@ -1,7 +0,0 @@ -# A match for /d+? d+?/ - -What's the match here? - -```js -alert( "123 456".match(/\d+? \d+?/g) ); // ? -``` diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/solution.md b/9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/solution.md deleted file mode 100644 index 0244963d1..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/solution.md +++ /dev/null @@ -1,15 +0,0 @@ -We need to find the beginning of the comment `match:`. - -An acceptable variant is `pattern:` -- the lazy quantifier makes the dot stop right before `match:-->`. We also need to add flag `pattern:s` for the dot to include newlines. - -Otherwise multiline comments won't be found: - -```js run -let regexp = //gs; - -let str = `... .. .. -`; - -alert( str.match(regexp) ); // '', '' -``` diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/task.md b/9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/task.md deleted file mode 100644 index 551d9c725..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/3-find-html-comments/task.md +++ /dev/null @@ -1,13 +0,0 @@ -# Find HTML comments - -Find all HTML comments in the text: - -```js -let regexp = /your regexp/g; - -let str = `... .. .. -`; - -alert( str.match(regexp) ); // '', '' -``` diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/solution.md b/9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/solution.md deleted file mode 100644 index b4d9f7496..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/solution.md +++ /dev/null @@ -1,10 +0,0 @@ - -The solution is `pattern:<[^<>]+>`. - -```js run -let regexp = /<[^<>]+>/g; - -let str = '<>
'; - -alert( str.match(regexp) ); // '', '', '' -``` diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md b/9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md deleted file mode 100644 index 6759152ff..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/4-find-html-tags-greedy-lazy/task.md +++ /dev/null @@ -1,15 +0,0 @@ -# Find HTML tags - -Create a regular expression to find all (opening and closing) HTML tags with their attributes. - -An example of use: - -```js run -let regexp = /your regexp/g; - -let str = '<> '; - -alert( str.match(regexp) ); // '', '', '' -``` - -Here we assume that tag attributes may not contain `<` and `>` (inside quotes too), that simplifies things a bit. diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/article.md b/9-regular-expressions/10-regexp-greedy-and-lazy/article.md deleted file mode 100644 index 2f656479d..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/article.md +++ /dev/null @@ -1,301 +0,0 @@ -# Greedy and lazy quantifiers - -Quantifiers are very simple from the first sight, but in fact they can be tricky. - -We should understand how the search works very well if we plan to look for something more complex than `pattern:/\d+/`. - -Let's take the following task as an example. - -We have a text and need to replace all quotes `"..."` with guillemet marks: `«...»`. They are preferred for typography in many countries. - -For instance: `"Hello, world"` should become `«Hello, world»`. There exist other quotes, such as `„Witam, świat!”` (Polish) or `「你好,世界」` (Chinese), but for our task let's choose `«...»`. - -The first thing to do is to locate quoted strings, and then we can replace them. - -A regular expression like `pattern:/".+"/g` (a quote, then something, then the other quote) may seem like a good fit, but it isn't! - -Let's try it: - -```js run -let regexp = /".+"/g; - -let str = 'a "witch" and her "broom" is one'; - -alert( str.match(regexp) ); // "witch" and her "broom" -``` - -...We can see that it works not as intended! - -Instead of finding two matches `match:"witch"` and `match:"broom"`, it finds one: `match:"witch" and her "broom"`. - -That can be described as "greediness is the cause of all evil". - -## Greedy search - -To find a match, the regular expression engine uses the following algorithm: - -- For every position in the string - - Try to match the pattern at that position. - - If there's no match, go to the next position. - -These common words do not make it obvious why the regexp fails, so let's elaborate how the search works for the pattern `pattern:".+"`. - -1. The first pattern character is a quote `pattern:"`. - - The regular expression engine tries to find it at the zero position of the source string `subject:a "witch" and her "broom" is one`, but there's `subject:a` there, so there's immediately no match. - - Then it advances: goes to the next positions in the source string and tries to find the first character of the pattern there, fails again, and finally finds the quote at the 3rd position: - - ![](witch_greedy1.svg) - -2. The quote is detected, and then the engine tries to find a match for the rest of the pattern. It tries to see if the rest of the subject string conforms to `pattern:.+"`. - - In our case the next pattern character is `pattern:.` (a dot). It denotes "any character except a newline", so the next string letter `match:'w'` fits: - - ![](witch_greedy2.svg) - -3. Then the dot repeats because of the quantifier `pattern:.+`. The regular expression engine adds to the match one character after another. - - ...Until when? All characters match the dot, so it only stops when it reaches the end of the string: - - ![](witch_greedy3.svg) - -4. Now the engine finished repeating `pattern:.+` and tries to find the next character of the pattern. It's the quote `pattern:"`. But there's a problem: the string has finished, there are no more characters! - - The regular expression engine understands that it took too many `pattern:.+` and starts to *backtrack*. - - In other words, it shortens the match for the quantifier by one character: - - ![](witch_greedy4.svg) - - Now it assumes that `pattern:.+` ends one character before the string end and tries to match the rest of the pattern from that position. - - If there were a quote there, then the search would end, but the last character is `subject:'e'`, so there's no match. - -5. ...So the engine decreases the number of repetitions of `pattern:.+` by one more character: - - ![](witch_greedy5.svg) - - The quote `pattern:'"'` does not match `subject:'n'`. - -6. The engine keep backtracking: it decreases the count of repetition for `pattern:'.'` until the rest of the pattern (in our case `pattern:'"'`) matches: - - ![](witch_greedy6.svg) - -7. The match is complete. - -8. So the first match is `match:"witch" and her "broom"`. If the regular expression has flag `pattern:g`, then the search will continue from where the first match ends. There are no more quotes in the rest of the string `subject:is one`, so no more results. - -That's probably not what we expected, but that's how it works. - -**In the greedy mode (by default) a quantified character is repeated as many times as possible.** - -The regexp engine adds to the match as many characters as it can for `pattern:.+`, and then shortens that one by one, if the rest of the pattern doesn't match. - -For our task we want another thing. That's where a lazy mode can help. - -## Lazy mode - -The lazy mode of quantifiers is an opposite to the greedy mode. It means: "repeat minimal number of times". - -We can enable it by putting a question mark `pattern:'?'` after the quantifier, so that it becomes `pattern:*?` or `pattern:+?` or even `pattern:??` for `pattern:'?'`. - -To make things clear: usually a question mark `pattern:?` is a quantifier by itself (zero or one), but if added *after another quantifier (or even itself)* it gets another meaning -- it switches the matching mode from greedy to lazy. - -The regexp `pattern:/".+?"/g` works as intended: it finds `match:"witch"` and `match:"broom"`: - -```js run -let regexp = /".+?"/g; - -let str = 'a "witch" and her "broom" is one'; - -alert( str.match(regexp) ); // "witch", "broom" -``` - -To clearly understand the change, let's trace the search step by step. - -1. The first step is the same: it finds the pattern start `pattern:'"'` at the 3rd position: - - ![](witch_greedy1.svg) - -2. The next step is also similar: the engine finds a match for the dot `pattern:'.'`: - - ![](witch_greedy2.svg) - -3. And now the search goes differently. Because we have a lazy mode for `pattern:+?`, the engine doesn't try to match a dot one more time, but stops and tries to match the rest of the pattern `pattern:'"'` right now: - - ![](witch_lazy3.svg) - - If there were a quote there, then the search would end, but there's `'i'`, so there's no match. -4. Then the regular expression engine increases the number of repetitions for the dot and tries one more time: - - ![](witch_lazy4.svg) - - Failure again. Then the number of repetitions is increased again and again... -5. ...Till the match for the rest of the pattern is found: - - ![](witch_lazy5.svg) - -6. The next search starts from the end of the current match and yield one more result: - - ![](witch_lazy6.svg) - -In this example we saw how the lazy mode works for `pattern:+?`. Quantifiers `pattern:*?` and `pattern:??` work the similar way -- the regexp engine increases the number of repetitions only if the rest of the pattern can't match on the given position. - -**Laziness is only enabled for the quantifier with `?`.** - -Other quantifiers remain greedy. - -For instance: - -```js run -alert( "123 456".match(/\d+ \d+?/) ); // 123 4 -``` - -1. The pattern `pattern:\d+` tries to match as many digits as it can (greedy mode), so it finds `match:123` and stops, because the next character is a space `pattern:' '`. -2. Then there's a space in the pattern, it matches. -3. Then there's `pattern:\d+?`. The quantifier is in lazy mode, so it finds one digit `match:4` and tries to check if the rest of the pattern matches from there. - - ...But there's nothing in the pattern after `pattern:\d+?`. - - The lazy mode doesn't repeat anything without a need. The pattern finished, so we're done. We have a match `match:123 4`. - -```smart header="Optimizations" -Modern regular expression engines can optimize internal algorithms to work faster. So they may work a bit differently from the described algorithm. - -But to understand how regular expressions work and to build regular expressions, we don't need to know about that. They are only used internally to optimize things. - -Complex regular expressions are hard to optimize, so the search may work exactly as described as well. -``` - -## Alternative approach - -With regexps, there's often more than one way to do the same thing. - -In our case we can find quoted strings without lazy mode using the regexp `pattern:"[^"]+"`: - -```js run -let regexp = /"[^"]+"/g; - -let str = 'a "witch" and her "broom" is one'; - -alert( str.match(regexp) ); // "witch", "broom" -``` - -The regexp `pattern:"[^"]+"` gives correct results, because it looks for a quote `pattern:'"'` followed by one or more non-quotes `pattern:[^"]`, and then the closing quote. - -When the regexp engine looks for `pattern:[^"]+` it stops the repetitions when it meets the closing quote, and we're done. - -Please note, that this logic does not replace lazy quantifiers! - -It is just different. There are times when we need one or another. - -**Let's see an example where lazy quantifiers fail and this variant works right.** - -For instance, we want to find links of the form ``, with any `href`. - -Which regular expression to use? - -The first idea might be: `pattern://g`. - -Let's check it: -```js run -let str = '......'; -let regexp = //g; - -// Works! -alert( str.match(regexp) ); // -``` - -It worked. But let's see what happens if there are many links in the text? - -```js run -let str = '...... ...'; -let regexp = //g; - -// Whoops! Two links in one match! -alert( str.match(regexp) ); // ... -``` - -Now the result is wrong for the same reason as our "witches" example. The quantifier `pattern:.*` took too many characters. - -The match looks like this: - -```html - -... -``` - -Let's modify the pattern by making the quantifier `pattern:.*?` lazy: - -```js run -let str = '...... ...'; -let regexp = //g; - -// Works! -alert( str.match(regexp) ); // , -``` - -Now it seems to work, there are two matches: - -```html - -... -``` - -...But let's test it on one more text input: - -```js run -let str = '......

...'; -let regexp = //g; - -// Wrong match! -alert( str.match(regexp) ); // ...

-``` - -Now it fails. The match includes not just a link, but also a lot of text after it, including ``. - -Why? - -That's what's going on: - -1. First the regexp finds a link start `match:` (none). -3. Then takes another character into `pattern:.*?`, and so on... until it finally reaches `match:" class="doc">`. - -But the problem is: that's already beyond the link ``, in another tag `

`. Not what we want. - -Here's the picture of the match aligned with the text: - -```html - -...

-``` - -So, we need the pattern to look for ``, but both greedy and lazy variants have problems. - -The correct variant can be: `pattern:href="[^"]*"`. It will take all characters inside the `href` attribute till the nearest quote, just what we need. - -A working example: - -```js run -let str1 = '......

...'; -let str2 = '...... ...'; -let regexp = //g; - -// Works! -alert( str1.match(regexp) ); // null, no matches, that's correct -alert( str2.match(regexp) ); // , -``` - -## Summary - -Quantifiers have two modes of work: - -Greedy -: By default the regular expression engine tries to repeat the quantified character as many times as possible. For instance, `pattern:\d+` consumes all possible digits. When it becomes impossible to consume more (no more digits or string end), then it continues to match the rest of the pattern. If there's no match then it decreases the number of repetitions (backtracks) and tries again. - -Lazy -: Enabled by the question mark `pattern:?` after the quantifier. The regexp engine tries to match the rest of the pattern before each repetition of the quantified character. - -As we've seen, the lazy mode is not a "panacea" from the greedy search. An alternative is a "fine-tuned" greedy search, with exclusions, as in the pattern `pattern:"[^"]+"`. diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy1.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy1.svg deleted file mode 100644 index 2eaf636cd..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy1.svg +++ /dev/null @@ -1 +0,0 @@ -a "witch" and her "broom" is one \ No newline at end of file diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy2.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy2.svg deleted file mode 100644 index 0489875a6..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy2.svg +++ /dev/null @@ -1 +0,0 @@ -a "witch" and her "broom" is one \ No newline at end of file diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy3.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy3.svg deleted file mode 100644 index f5175e5c3..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy3.svg +++ /dev/null @@ -1 +0,0 @@ -a "witch" and her "broom" is one \ No newline at end of file diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy4.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy4.svg deleted file mode 100644 index 61b37fb9c..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy4.svg +++ /dev/null @@ -1 +0,0 @@ -a "witch" and her "broom" is one \ No newline at end of file diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy5.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy5.svg deleted file mode 100644 index a0c5f1fb8..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy5.svg +++ /dev/null @@ -1 +0,0 @@ -a "witch" and her "broom" is one \ No newline at end of file diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy6.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy6.svg deleted file mode 100644 index c7cc7537c..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_greedy6.svg +++ /dev/null @@ -1 +0,0 @@ -a "witch" and her "broom" is one \ No newline at end of file diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy3.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy3.svg deleted file mode 100644 index 77d5d1562..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy3.svg +++ /dev/null @@ -1 +0,0 @@ -a "witch" and her "broom" is one \ No newline at end of file diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy4.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy4.svg deleted file mode 100644 index 6c9cc29cf..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy4.svg +++ /dev/null @@ -1 +0,0 @@ -a "witch" and her "broom" is one \ No newline at end of file diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy5.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy5.svg deleted file mode 100644 index 68c77d27d..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy5.svg +++ /dev/null @@ -1 +0,0 @@ -a "witch" and her "broom" is one \ No newline at end of file diff --git a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy6.svg b/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy6.svg deleted file mode 100644 index 2ee64f5b8..000000000 --- a/9-regular-expressions/10-regexp-greedy-and-lazy/witch_lazy6.svg +++ /dev/null @@ -1 +0,0 @@ -a "witch" and her "broom" is one \ No newline at end of file diff --git a/9-regular-expressions/11-regexp-alternation/02-find-matching-bbtags/solution.md b/9-regular-expressions/11-regexp-alternation/02-find-matching-bbtags/solution.md deleted file mode 100644 index 03080f86c..000000000 --- a/9-regular-expressions/11-regexp-alternation/02-find-matching-bbtags/solution.md +++ /dev/null @@ -1,23 +0,0 @@ - -Opening tag is `pattern:\[(b|url|quote)\]`. - -Then to find everything till the closing tag -- let's the pattern `pattern:[\s\S]*?` to match any character including the newline and then a backreference to the closing tag. - -The full pattern: `pattern:\[(b|url|quote)\][\s\S]*?\[/\1\]`. - -In action: - -```js run -let reg = /\[(b|url|quote)\][\s\S]*?\[\/\1\]/g; - -let str = ` - [b]hello![/b] - [quote] - [url]http://google.com[/url] - [/quote] -`; - -alert( str.match(reg) ); // [b]hello![/b],[quote][url]http://google.com[/url][/quote] -``` - -Please note that we had to escape a slash for the closing tag `pattern:[/\1]`, because normally the slash closes the pattern. diff --git a/9-regular-expressions/11-regexp-alternation/article.md b/9-regular-expressions/11-regexp-alternation/article.md deleted file mode 100644 index 0caa6de02..000000000 --- a/9-regular-expressions/11-regexp-alternation/article.md +++ /dev/null @@ -1,59 +0,0 @@ -# Alternation (OR) | - -Alternation is the term in regular expression that is actually a simple "OR". - -In a regular expression it is denoted with a vertical line character `pattern:|`. - -For instance, we need to find programming languages: HTML, PHP, Java or JavaScript. - -The corresponding regexp: `pattern:html|php|java(script)?`. - -A usage example: - -```js run -let reg = /html|php|css|java(script)?/gi; - -let str = "First HTML appeared, then CSS, then JavaScript"; - -alert( str.match(reg) ); // 'HTML', 'CSS', 'JavaScript' -``` - -We already know a similar thing -- square brackets. They allow to choose between multiple character, for instance `pattern:gr[ae]y` matches `match:gray` or `match:grey`. - -Square brackets allow only characters or character sets. Alternation allows any expressions. A regexp `pattern:A|B|C` means one of expressions `A`, `B` or `C`. - -For instance: - -- `pattern:gr(a|e)y` means exactly the same as `pattern:gr[ae]y`. -- `pattern:gra|ey` means `match:gra` or `match:ey`. - -To separate a part of the pattern for alternation we usually enclose it in parentheses, like this: `pattern:before(XXX|YYY)after`. - -## Regexp for time - -In previous chapters there was a task to build a regexp for searching time in the form `hh:mm`, for instance `12:00`. But a simple `pattern:\d\d:\d\d` is too vague. It accepts `25:99` as the time (99 seconds is valid, but shouldn't be). - -How can we make a better one? - -We can apply more careful matching. First, the hours: - -- If the first digit is `0` or `1`, then the next digit can by anything. -- Or, if the first digit is `2`, then the next must be `pattern:[0-3]`. - -As a regexp: `pattern:[01]\d|2[0-3]`. - -Next, the minutes must be from `0` to `59`. In the regexp language that means `pattern:[0-5]\d`: the first digit `0-5`, and then any digit. - -Let's glue them together into the pattern: `pattern:[01]\d|2[0-3]:[0-5]\d`. - -We're almost done, but there's a problem. The alternation `pattern:|` now happens to be between `pattern:[01]\d` and `pattern:2[0-3]:[0-5]\d`. - -That's wrong, as it should be applied only to hours `[01]\d` OR `2[0-3]`. That's a common mistake when starting to work with regular expressions. - -The correct variant: - -```js run -let reg = /([01]\d|2[0-3]):[0-5]\d/g; - -alert("00:00 10:10 23:59 25:99 1:2".match(reg)); // 00:00,10:10,23:59 -``` diff --git a/9-regular-expressions/11-regexp-groups/01-test-mac/solution.md b/9-regular-expressions/11-regexp-groups/01-test-mac/solution.md deleted file mode 100644 index 26f7888f7..000000000 --- a/9-regular-expressions/11-regexp-groups/01-test-mac/solution.md +++ /dev/null @@ -1,21 +0,0 @@ -A two-digit hex number is `pattern:[0-9a-f]{2}` (assuming the flag `pattern:i` is set). - -We need that number `NN`, and then `:NN` repeated 5 times (more numbers); - -The regexp is: `pattern:[0-9a-f]{2}(:[0-9a-f]{2}){5}` - -Now let's show that the match should capture all the text: start at the beginning and end at the end. That's done by wrapping the pattern in `pattern:^...$`. - -Finally: - -```js run -let regexp = /^[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}$/i; - -alert( regexp.test('01:32:54:67:89:AB') ); // true - -alert( regexp.test('0132546789AB') ); // false (no colons) - -alert( regexp.test('01:32:54:67:89') ); // false (5 numbers, need 6) - -alert( regexp.test('01:32:54:67:89:ZZ') ) // false (ZZ in the end) -``` diff --git a/9-regular-expressions/11-regexp-groups/01-test-mac/task.md b/9-regular-expressions/11-regexp-groups/01-test-mac/task.md deleted file mode 100644 index a2e799cfa..000000000 --- a/9-regular-expressions/11-regexp-groups/01-test-mac/task.md +++ /dev/null @@ -1,20 +0,0 @@ -# Check MAC-address - -[MAC-address](https://en.wikipedia.org/wiki/MAC_address) of a network interface consists of 6 two-digit hex numbers separated by a colon. - -For instance: `subject:'01:32:54:67:89:AB'`. - -Write a regexp that checks whether a string is MAC-address. - -Usage: -```js -let regexp = /your regexp/; - -alert( regexp.test('01:32:54:67:89:AB') ); // true - -alert( regexp.test('0132546789AB') ); // false (no colons) - -alert( regexp.test('01:32:54:67:89') ); // false (5 numbers, must be 6) - -alert( regexp.test('01:32:54:67:89:ZZ') ) // false (ZZ at the end) -``` diff --git a/9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/solution.md b/9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/solution.md deleted file mode 100644 index 0806dc4fd..000000000 --- a/9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/solution.md +++ /dev/null @@ -1,27 +0,0 @@ -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. The color has either 3 or 6 digits. - -Let's use the quantifier `pattern:{1,2}` for that: we'll have `pattern:/#([a-f0-9]{3}){1,2}/i`. - -Here the pattern `pattern:[a-f0-9]{3}` is enclosed in parentheses to apply the quantifier `pattern:{1,2}`. - -In action: - -```js run -let regexp = /#([a-f0-9]{3}){1,2}/gi; - -let str = "color: #3f3; background-color: #AA00ef; and: #abcd"; - -alert( str.match(regexp) ); // #3f3 #AA00ef #abc -``` - -There's a 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 regexp = /#([a-f0-9]{3}){1,2}\b/gi; - -let str = "color: #3f3; background-color: #AA00ef; and: #abcd"; - -alert( str.match(regexp) ); // #3f3 #AA00ef -``` diff --git a/9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/task.md b/9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/task.md deleted file mode 100644 index 09108484a..000000000 --- a/9-regular-expressions/11-regexp-groups/02-find-webcolor-3-or-6/task.md +++ /dev/null @@ -1,14 +0,0 @@ -# Find color in the format #abc or #abcdef - -Write a RegExp that matches colors in the format `#abc` or `#abcdef`. That is: `#` followed by 3 or 6 hexadecimal digits. - -Usage example: -```js -let regexp = /your regexp/g; - -let str = "color: #3f3; background-color: #AA00ef; and: #abcd"; - -alert( str.match(regexp) ); // #3f3 #AA00ef -``` - -P.S. This should be exactly 3 or 6 hex digits. Values with 4 digits, such as `#abcd`, should not match. diff --git a/9-regular-expressions/11-regexp-groups/04-parse-expression/solution.md b/9-regular-expressions/11-regexp-groups/04-parse-expression/solution.md deleted file mode 100644 index ac67519bb..000000000 --- a/9-regular-expressions/11-regexp-groups/04-parse-expression/solution.md +++ /dev/null @@ -1,56 +0,0 @@ -A regexp for a number is: `pattern:-?\d+(\.\d+)?`. We created it in the previous task. - -An operator is `pattern:[-+*/]`. The hyphen `pattern:-` goes first in the square brackets, because in the middle it would mean a character range, while we just want a character `-`. - -The slash `/` should be escaped inside a JavaScript regexp `pattern:/.../`, we'll do that later. - -We need a number, an operator, and then another number. And optional spaces between them. - -The full regular expression: `pattern:-?\d+(\.\d+)?\s*[-+*/]\s*-?\d+(\.\d+)?`. - -It has 3 parts, with `pattern:\s*` between them: -1. `pattern:-?\d+(\.\d+)?` - the first number, -1. `pattern:[-+*/]` - the operator, -1. `pattern:-?\d+(\.\d+)?` - the second number. - -To make each of these parts a separate element of the result array, let's enclose them in parentheses: `pattern:(-?\d+(\.\d+)?)\s*([-+*/])\s*(-?\d+(\.\d+)?)`. - -In action: - -```js run -let regexp = /(-?\d+(\.\d+)?)\s*([-+*\/])\s*(-?\d+(\.\d+)?)/; - -alert( "1.2 + 12".match(regexp) ); -``` - -The result includes: - -- `result[0] == "1.2 + 12"` (full match) -- `result[1] == "1.2"` (first group `(-?\d+(\.\d+)?)` -- the first number, including the decimal part) -- `result[2] == ".2"` (second group`(\.\d+)?` -- the first decimal part) -- `result[3] == "+"` (third group `([-+*\/])` -- the operator) -- `result[4] == "12"` (forth group `(-?\d+(\.\d+)?)` -- the second number) -- `result[5] == undefined` (fifth group `(\.\d+)?` -- the last decimal part is absent, so it's undefined) - -We only want the numbers and the operator, without the full match or the decimal parts, so let's "clean" the result a bit. - -The full match (the arrays first item) can be removed by shifting the array `result.shift()`. - -Groups that contain decimal parts (number 2 and 4) `pattern:(.\d+)` can be excluded by adding `pattern:?:` to the beginning: `pattern:(?:\.\d+)?`. - -The final solution: - -```js run -function parse(expr) { - let regexp = /(-?\d+(?:\.\d+)?)\s*([-+*\/])\s*(-?\d+(?:\.\d+)?)/; - - let result = expr.match(regexp); - - if (!result) return []; - result.shift(); - - return result; -} - -alert( parse("-1.23 * 3.45") ); // -1.23, *, 3.45 -``` diff --git a/9-regular-expressions/11-regexp-groups/04-parse-expression/task.md b/9-regular-expressions/11-regexp-groups/04-parse-expression/task.md deleted file mode 100644 index 8b54d4683..000000000 --- a/9-regular-expressions/11-regexp-groups/04-parse-expression/task.md +++ /dev/null @@ -1,28 +0,0 @@ -# Parse an expression - -An arithmetical expression consists of 2 numbers and an operator between them, for instance: - -- `1 + 2` -- `1.2 * 3.4` -- `-3 / -6` -- `-2 - 2` - -The operator is one of: `"+"`, `"-"`, `"*"` or `"/"`. - -There may be extra spaces at the beginning, at the end or between the parts. - -Create a function `parse(expr)` that takes an expression and returns an array of 3 items: - -1. The first number. -2. The operator. -3. The second number. - -For example: - -```js -let [a, op, b] = parse("1.2 * 3.4"); - -alert(a); // 1.2 -alert(op); // * -alert(b); // 3.4 -``` diff --git a/9-regular-expressions/11-regexp-groups/article.md b/9-regular-expressions/11-regexp-groups/article.md deleted file mode 100644 index e0ef00bd3..000000000 --- a/9-regular-expressions/11-regexp-groups/article.md +++ /dev/null @@ -1,364 +0,0 @@ -# Capturing groups - -A part of a pattern can be enclosed in parentheses `pattern:(...)`. This is called a "capturing group". - -That has two effects: - -1. It allows to get a part of the match as a separate item in the result array. -2. If we put a quantifier after the parentheses, it applies to the parentheses as a whole. - -## Examples - -Let's see how parentheses work in examples. - -### Example: gogogo - -Without parentheses, the pattern `pattern:go+` means `subject:g` character, followed by `subject:o` repeated one or more times. For instance, `match:goooo` or `match:gooooooooo`. - -Parentheses group characters together, so `pattern:(go)+` means `match:go`, `match:gogo`, `match:gogogo` and so on. - -```js run -alert( 'Gogogo now!'.match(/(go)+/ig) ); // "Gogogo" -``` - -### Example: domain - -Let's make something more complex -- a regular expression to search for a website domain. - -For example: - -``` -mail.com -users.mail.com -smith.users.mail.com -``` - -As we can see, a domain consists of repeated words, a dot after each one except the last one. - -In regular expressions that's `pattern:(\w+\.)+\w+`: - -```js run -let regexp = /(\w+\.)+\w+/g; - -alert( "site.com my.site.com".match(regexp) ); // site.com,my.site.com -``` - -The search works, but the pattern can't match a domain with a hyphen, e.g. `my-site.com`, because the hyphen does not belong to class `pattern:\w`. - -We can fix it by replacing `pattern:\w` with `pattern:[\w-]` in every word except the last one: `pattern:([\w-]+\.)+\w+`. - -### Example: email - -The previous example can be extended. We can create a regular expression for emails based on it. - -The email format is: `name@domain`. Any word can be the name, hyphens and dots are allowed. In regular expressions that's `pattern:[-.\w]+`. - -The pattern: - -```js run -let regexp = /[-.\w]+@([\w-]+\.)+[\w-]+/g; - -alert("my@mail.com @ his@site.com.uk".match(regexp)); // my@mail.com, his@site.com.uk -``` - -That regexp is not perfect, but mostly works and helps to fix accidental mistypes. The only truly reliable check for an email can only be done by sending a letter. - -## Parentheses contents in the match - -Parentheses are numbered from left to right. The search engine memorizes the content matched by each of them and allows to get it in the result. - -The method `str.match(regexp)`, if `regexp` has no flag `g`, looks for the first match and returns it as an array: - -1. At index `0`: the full match. -2. At index `1`: the contents of the first parentheses. -3. At index `2`: the contents of the second parentheses. -4. ...and so on... - -For instance, we'd like to find HTML tags `pattern:<.*?>`, and process them. It would be convenient to have tag content (what's inside the angles), in a separate variable. - -Let's wrap the inner content into parentheses, like this: `pattern:<(.*?)>`. - -Now we'll get both the tag as a whole `match:

` and its contents `match:h1` in the resulting array: - -```js run -let str = '

Hello, world!

'; - -let tag = str.match(/<(.*?)>/); - -alert( tag[0] ); //

-alert( tag[1] ); // h1 -``` - -### Nested groups - -Parentheses can be nested. In this case the numbering also goes from left to right. - -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: `pattern:<(([a-z]+)\s*([^>]*))>`. - -Here's how they are numbered (left to right, by the opening paren): - -![](regexp-nested-groups-pattern.svg) - -In action: - -```js run -let str = ''; - -let regexp = /<(([a-z]+)\s*([^>]*))>/; - -let result = str.match(regexp); -alert(result[0]); // -alert(result[1]); // span class="my" -alert(result[2]); // span -alert(result[3]); // class="my" -``` - -The zero index of `result` always holds the full match. - -Then groups, numbered from left to right by an opening paren. The first group is returned as `result[1]`. Here it encloses the whole tag content. - -Then in `result[2]` goes the group from the second opening paren `pattern:([a-z]+)` - tag name, then in `result[3]` the tag: `pattern:([^>]*)`. - -The contents of every group in the string: - -![](regexp-nested-groups-matches.svg) - -### Optional groups - -Even if a group is optional and doesn't exist in the match (e.g. has the quantifier `pattern:(...)?`), the corresponding `result` array item is present and equals `undefined`. - -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 -let match = 'a'.match(/a(z)?(c)?/); - -alert( match.length ); // 3 -alert( match[0] ); // a (whole match) -alert( match[1] ); // undefined -alert( match[2] ); // undefined -``` - -The array has the length of `3`, but all groups are empty. - -And here's a more complex match for the string `subject:ac`: - -```js run -let match = 'ac'.match(/a(z)?(c)?/) - -alert( match.length ); // 3 -alert( match[0] ); // ac (whole match) -alert( match[1] ); // undefined, because there's nothing for (z)? -alert( match[2] ); // c -``` - -The array length is permanent: `3`. But there's nothing for the group `pattern:(z)?`, so the result is `["ac", undefined, "c"]`. - -## Searching for all matches with groups: matchAll - -```warn header="`matchAll` is a new method, polyfill may be needed" -The method `matchAll` is not supported in old browsers. - -A polyfill may be required, such as . -``` - -When we search for all matches (flag `pattern:g`), the `match` method does not return contents for groups. - -For example, let's find all tags in a string: - -```js run -let str = '

'; - -let tags = str.match(/<(.*?)>/g); - -alert( tags ); //

,

-``` - -The result is an array of matches, but without details about each of them. But in practice we usually need contents of capturing groups in the result. - -To get them, we should search using the method `str.matchAll(regexp)`. - -It was added to JavaScript language long after `match`, as its "new and improved version". - -Just like `match`, it looks for matches, but there are 3 differences: - -1. It returns not an array, but an iterable object. -2. When the flag `pattern:g` is present, it returns every match as an array with groups. -3. If there are no matches, it returns not `null`, but an empty iterable object. - -For instance: - -```js run -let results = '

'.matchAll(/<(.*?)>/gi); - -// results - is not an array, but an iterable object -alert(results); // [object RegExp String Iterator] - -alert(results[0]); // undefined (*) - -results = Array.from(results); // let's turn it into array - -alert(results[0]); //

,h1 (1st tag) -alert(results[1]); //

,h2 (2nd tag) -``` - -As we can see, the first difference is very important, as demonstrated in the line `(*)`. We can't get the match as `results[0]`, because that object isn't pseudoarray. We can turn it into a real `Array` using `Array.from`. There are more details about pseudoarrays and iterables in the article . - -There's no need in `Array.from` if we're looping over results: - -```js run -let results = '

'.matchAll(/<(.*?)>/gi); - -for(let result of results) { - alert(result); - // first alert:

,h1 - // second:

,h2 -} -``` - -...Or using destructuring: - -```js -let [tag1, tag2] = '

'.matchAll(/<(.*?)>/gi); -``` - -Every match, returned by `matchAll`, has the same format as returned by `match` without flag `pattern:g`: it's an array with additional properties `index` (match index in the string) and `input` (source string): - -```js run -let results = '

'.matchAll(/<(.*?)>/gi); - -let [tag1, tag2] = results; - -alert( tag1[0] ); //

-alert( tag1[1] ); // h1 -alert( tag1.index ); // 0 -alert( tag1.input ); //

-``` - -```smart header="Why is a result of `matchAll` an iterable object, not an array?" -Why is the method designed like that? The reason is simple - for the optimization. - -The call to `matchAll` does not perform the search. Instead, it returns an iterable object, without the results initially. The search is performed each time we iterate over it, e.g. in the loop. - -So, there will be found as many results as needed, not more. - -E.g. there are potentially 100 matches in the text, but in a `for..of` loop we found 5 of them, then decided it's enough and made a `break`. Then the engine won't spend time finding other 95 matches. -``` - -## Named groups - -Remembering groups by their numbers is hard. For simple patterns it's doable, but for more complex ones counting parentheses is inconvenient. We have a much better option: give names to parentheses. - -That's done by putting `pattern:?` immediately after the opening paren. - -For example, let's look for a date in the format "year-month-day": - -```js run -*!* -let dateRegexp = /(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})/; -*/!* -let str = "2019-04-30"; - -let groups = str.match(dateRegexp).groups; - -alert(groups.year); // 2019 -alert(groups.month); // 04 -alert(groups.day); // 30 -``` - -As you can see, the groups reside in the `.groups` property of the match. - -To look for all dates, we can add flag `pattern:g`. - -We'll also need `matchAll` to obtain full matches, together with groups: - -```js run -let dateRegexp = /(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})/g; - -let str = "2019-10-30 2020-01-01"; - -let results = str.matchAll(dateRegexp); - -for(let result of results) { - let {year, month, day} = result.groups; - - alert(`${day}.${month}.${year}`); - // first alert: 30.10.2019 - // second: 01.01.2020 -} -``` - -## Capturing groups in replacement - -Method `str.replace(regexp, replacement)` that replaces all matches with `regexp` in `str` allows to use parentheses contents in the `replacement` string. That's done using `pattern:$n`, where `pattern:n` is the group number. - -For example, - -```js run -let str = "John Bull"; -let regexp = /(\w+) (\w+)/; - -alert( str.replace(regexp, '$2, $1') ); // Bull, John -``` - -For named parentheses the reference will be `pattern:$`. - -For example, let's reformat dates from "year-month-day" to "day.month.year": - -```js run -let regexp = /(?[0-9]{4})-(?[0-9]{2})-(?[0-9]{2})/g; - -let str = "2019-10-30, 2020-01-01"; - -alert( str.replace(regexp, '$.$.$') ); -// 30.10.2019, 01.01.2020 -``` - -## Non-capturing groups with ?: - -Sometimes we need parentheses to correctly apply a quantifier, but we don't want their contents in results. - -A group may be excluded by adding `pattern:?:` in the beginning. - -For instance, if we want to find `pattern:(go)+`, but don't want the parentheses contents (`go`) as a separate array item, we can write: `pattern:(?:go)+`. - -In the example below we only get the name `match:John` as a separate member of the match: - -```js run -let str = "Gogogo John!"; - -*!* -// ?: exludes 'go' from capturing -let regexp = /(?:go)+ (\w+)/i; -*/!* - -let result = str.match(regexp); - -alert( result[0] ); // Gogogo John (full match) -alert( result[1] ); // John -alert( result.length ); // 2 (no more items in the array) -``` - -## Summary - -Parentheses group together a part of the regular expression, so that the quantifier applies to it as a whole. - -Parentheses groups are numbered left-to-right, and can optionally be named with `(?...)`. - -The content, matched by a group, can be obtained in the results: - -- The method `str.match` returns capturing groups only without flag `pattern:g`. -- The method `str.matchAll` always returns capturing groups. - -If the parentheses have no name, then their contents is available in the match array by its number. Named parentheses are also available in the property `groups`. - -We can also use parentheses contents in the replacement string in `str.replace`: by the number `$n` or the name `$`. - -A group may be excluded from numbering by adding `pattern:?:` in its start. That's used when we need to apply a quantifier to the whole group, but don't want it as a separate item in the results array. We also can't reference such parentheses in the replacement string. diff --git a/9-regular-expressions/11-regexp-groups/regexp-nested-groups-matches.svg b/9-regular-expressions/11-regexp-groups/regexp-nested-groups-matches.svg deleted file mode 100644 index ce61ff3a7..000000000 --- a/9-regular-expressions/11-regexp-groups/regexp-nested-groups-matches.svg +++ /dev/null @@ -1 +0,0 @@ -< (( [a-z]+ ) \s* ( [^>]* )) >1span class="my"2span3class="my" \ No newline at end of file diff --git a/9-regular-expressions/11-regexp-groups/regexp-nested-groups-pattern.svg b/9-regular-expressions/11-regexp-groups/regexp-nested-groups-pattern.svg deleted file mode 100644 index ce61ff3a7..000000000 --- a/9-regular-expressions/11-regexp-groups/regexp-nested-groups-pattern.svg +++ /dev/null @@ -1 +0,0 @@ -< (( [a-z]+ ) \s* ( [^>]* )) >1span class="my"2span3class="my" \ No newline at end of file diff --git a/9-regular-expressions/12-regexp-backreferences/article.md b/9-regular-expressions/12-regexp-backreferences/article.md deleted file mode 100644 index b80fa85cf..000000000 --- a/9-regular-expressions/12-regexp-backreferences/article.md +++ /dev/null @@ -1,72 +0,0 @@ -# Backreferences in pattern: \N and \k - -We can use the contents of capturing groups `pattern:(...)` not only in the result or in the replacement string, but also in the pattern itself. - -## Backreference by number: \N - -A group can be referenced in the pattern using `pattern:\N`, where `N` is the group number. - -To make clear why that's helpful, let's consider a task. - -We need to find quoted strings: either single-quoted `subject:'...'` or a double-quoted `subject:"..."` -- both variants should match. - -How to find them? - -We can put both kinds of quotes in the square brackets: `pattern:['"](.*?)['"]`, but it would find strings with mixed quotes, like `match:"...'` and `match:'..."`. That would lead to incorrect matches when one quote appears inside other ones, like in the string `subject:"She's the one!"`: - -```js run -let str = `He said: "She's the one!".`; - -let regexp = /['"](.*?)['"]/g; - -// The result is not what we'd like to have -alert( str.match(regexp) ); // "She' -``` - -As we can see, the pattern found an opening quote `match:"`, then the text is consumed till the other quote `match:'`, that closes the match. - -To make sure that the pattern looks for the closing quote exactly the same as the opening one, we can wrap it into a capturing group and backreference it: `pattern:(['"])(.*?)\1`. - -Here's the correct code: - -```js run -let str = `He said: "She's the one!".`; - -*!* -let regexp = /(['"])(.*?)\1/g; -*/!* - -alert( str.match(regexp) ); // "She's the one!" -``` - -Now it works! The regular expression engine finds the first quote `pattern:(['"])` and memorizes its content. That's the first capturing group. - -Further in the pattern `pattern:\1` means "find the same text as in the first group", exactly the same quote in our case. - -Similar to that, `pattern:\2` would mean the contents of the second group, `pattern:\3` - the 3rd group, and so on. - -```smart -If we use `?:` in the group, then we can't reference it. Groups that are excluded from capturing `(?:...)` are not memorized by the engine. -``` - -```warn header="Don't mess up: in the pattern `pattern:\1`, in the replacement: `pattern:$1`" -In the replacement string we use a dollar sign: `pattern:$1`, while in the pattern - a backslash `pattern:\1`. -``` - -## Backreference by name: `\k` - -If a regexp has many parentheses, it's convenient to give them names. - -To reference a named group we can use `pattern:\k`. - -In the example below the group with quotes is named `pattern:?`, so the backreference is `pattern:\k`: - -```js run -let str = `He said: "She's the one!".`; - -*!* -let regexp = /(?['"])(.*?)\k/g; -*/!* - -alert( str.match(regexp) ); // "She's the one!" -``` diff --git a/9-regular-expressions/13-regexp-alternation/01-find-programming-language/solution.md b/9-regular-expressions/13-regexp-alternation/01-find-programming-language/solution.md deleted file mode 100644 index e33f9cf2f..000000000 --- a/9-regular-expressions/13-regexp-alternation/01-find-programming-language/solution.md +++ /dev/null @@ -1,33 +0,0 @@ - -The first idea can be to list the languages with `|` in-between. - -But that doesn't work right: - -```js run -let regexp = /Java|JavaScript|PHP|C|C\+\+/g; - -let str = "Java, JavaScript, PHP, C, C++"; - -alert( str.match(regexp) ); // Java,Java,PHP,C,C -``` - -The regular expression engine looks for alternations one-by-one. That is: first it checks if we have `match:Java`, otherwise -- looks for `match:JavaScript` and so on. - -As a result, `match:JavaScript` can never be found, just because `match:Java` is checked first. - -The same with `match:C` and `match:C++`. - -There are two solutions for that problem: - -1. Change the order to check the longer match first: `pattern:JavaScript|Java|C\+\+|C|PHP`. -2. Merge variants with the same start: `pattern:Java(Script)?|C(\+\+)?|PHP`. - -In action: - -```js run -let regexp = /Java(Script)?|C(\+\+)?|PHP/g; - -let str = "Java, JavaScript, PHP, C, C++"; - -alert( str.match(regexp) ); // Java,JavaScript,PHP,C,C++ -``` diff --git a/9-regular-expressions/13-regexp-alternation/01-find-programming-language/task.md b/9-regular-expressions/13-regexp-alternation/01-find-programming-language/task.md deleted file mode 100644 index e0f7af95c..000000000 --- a/9-regular-expressions/13-regexp-alternation/01-find-programming-language/task.md +++ /dev/null @@ -1,11 +0,0 @@ -# Find programming languages - -There are many programming languages, for instance Java, JavaScript, PHP, C, C++. - -Create a regexp that finds them in the string `subject:Java JavaScript PHP C++ C`: - -```js -let regexp = /your regexp/g; - -alert("Java JavaScript PHP C++ C".match(regexp)); // Java JavaScript PHP C++ C -``` diff --git a/9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/solution.md b/9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/solution.md deleted file mode 100644 index 9b3fa1877..000000000 --- a/9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/solution.md +++ /dev/null @@ -1,23 +0,0 @@ - -Opening tag is `pattern:\[(b|url|quote)\]`. - -Then to find everything till the closing tag -- let's use the pattern `pattern:.*?` with flag `pattern:s` to match any character including the newline and then add a backreference to the closing tag. - -The full pattern: `pattern:\[(b|url|quote)\].*?\[/\1\]`. - -In action: - -```js run -let regexp = /\[(b|url|quote)\].*?\[\/\1\]/gs; - -let str = ` - [b]hello![/b] - [quote] - [url]http://google.com[/url] - [/quote] -`; - -alert( str.match(regexp) ); // [b]hello![/b],[quote][url]http://google.com[/url][/quote] -``` - -Please note that besides escaping `pattern:[` and `pattern:]`, we had to escape a slash for the closing tag `pattern:[\/\1]`, because normally the slash closes the pattern. diff --git a/9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/task.md b/9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/task.md deleted file mode 100644 index 72d715afd..000000000 --- a/9-regular-expressions/13-regexp-alternation/02-find-matching-bbtags/task.md +++ /dev/null @@ -1,48 +0,0 @@ -# Find bbtag pairs - -A "bb-tag" looks like `[tag]...[/tag]`, where `tag` is one of: `b`, `url` or `quote`. - -For instance: -``` -[b]text[/b] -[url]http://google.com[/url] -``` - -BB-tags can be nested. But a tag can't be nested into itself, for instance: - -``` -Normal: -[url] [b]http://google.com[/b] [/url] -[quote] [b]text[/b] [/quote] - -Can't happen: -[b][b]text[/b][/b] -``` - -Tags can contain line breaks, that's normal: - -``` -[quote] - [b]text[/b] -[/quote] -``` - -Create a regexp to find all BB-tags with their contents. - -For instance: - -```js -let regexp = /your regexp/flags; - -let str = "..[url]http://google.com[/url].."; -alert( str.match(regexp) ); // [url]http://google.com[/url] -``` - -If tags are nested, then we need the outer tag (if we want we can continue the search in its content): - -```js -let regexp = /your regexp/flags; - -let str = "..[url][b]http://google.com[/b][/url].."; -alert( str.match(regexp) ); // [url][b]http://google.com[/b][/url] -``` diff --git a/9-regular-expressions/13-regexp-alternation/03-match-quoted-string/solution.md b/9-regular-expressions/13-regexp-alternation/03-match-quoted-string/solution.md deleted file mode 100644 index 5a007aee0..000000000 --- a/9-regular-expressions/13-regexp-alternation/03-match-quoted-string/solution.md +++ /dev/null @@ -1,17 +0,0 @@ -The solution: `pattern:/"(\\.|[^"\\])*"/g`. - -Step by step: - -- First we look for an opening quote `pattern:"` -- Then if we have a backslash `pattern:\\` (we technically have to double it in the pattern, because it is a special character, so that's a single backslash in fact), then any character is fine after it (a dot). -- Otherwise we take any character except a quote (that would mean the end of the string) and a backslash (to prevent lonely backslashes, the backslash is only used with some other symbol after it): `pattern:[^"\\]` -- ...And so on till the closing quote. - -In action: - -```js run -let regexp = /"(\\.|[^"\\])*"/g; -let str = ' .. "test me" .. "Say \\"Hello\\"!" .. "\\\\ \\"" .. '; - -alert( str.match(regexp) ); // "test me","Say \"Hello\"!","\\ \"" -``` diff --git a/9-regular-expressions/13-regexp-alternation/03-match-quoted-string/task.md b/9-regular-expressions/13-regexp-alternation/03-match-quoted-string/task.md deleted file mode 100644 index ad41d91b1..000000000 --- a/9-regular-expressions/13-regexp-alternation/03-match-quoted-string/task.md +++ /dev/null @@ -1,32 +0,0 @@ -# Find quoted strings - -Create a regexp to find strings in double quotes `subject:"..."`. - -The strings should support escaping, the same way as JavaScript strings do. For instance, quotes can be inserted as `subject:\"` a newline as `subject:\n`, and the slash itself as `subject:\\`. - -```js -let str = "Just like \"here\"."; -``` - -Please note, in particular, that an escaped quote `subject:\"` does not end a string. - -So we should search from one quote to the other ignoring escaped quotes on the way. - -That's the essential part of the task, otherwise it would be trivial. - -Examples of strings to match: -```js -.. *!*"test me"*/!* .. -.. *!*"Say \"Hello\"!"*/!* ... (escaped quotes inside) -.. *!*"\\"*/!* .. (double slash inside) -.. *!*"\\ \""*/!* .. (double slash and an escaped quote inside) -``` - -In JavaScript we need to double the slashes to pass them right into the string, like this: - -```js run -let str = ' .. "test me" .. "Say \\"Hello\\"!" .. "\\\\ \\"" .. '; - -// the in-memory string -alert(str); // .. "test me" .. "Say \"Hello\"!" .. "\\ \"" .. -``` diff --git a/9-regular-expressions/13-regexp-alternation/04-match-exact-tag/solution.md b/9-regular-expressions/13-regexp-alternation/04-match-exact-tag/solution.md deleted file mode 100644 index 5d4ba8d96..000000000 --- a/9-regular-expressions/13-regexp-alternation/04-match-exact-tag/solution.md +++ /dev/null @@ -1,16 +0,0 @@ - -The pattern start is obvious: `pattern:`, because `match:` would match it. - -We need either a space after `match:`. - -In the regexp language: `pattern:|\s.*?>)`. - -In action: - -```js run -let regexp = /|\s.*?>)/g; - -alert( '