From eef0576d16e916fcd0570038ddc5d2ee9ad1812e Mon Sep 17 00:00:00 2001 From: wizardforcel <562826179@qq.com> Date: Mon, 7 May 2018 11:11:18 +0800 Subject: [PATCH] 9 --- 9.md | 106 ++++++++++-------- img/9-0.jpg | Bin 0 -> 26842 bytes img/9-1.svg | 307 ++++++++++++++++++++++++++++++++++++++++++++++++++++ img/9-2.svg | 72 ++++++++++++ img/9-3.svg | 175 ++++++++++++++++++++++++++++++ 5 files changed, 614 insertions(+), 46 deletions(-) create mode 100644 img/9-0.jpg create mode 100644 img/9-1.svg create mode 100644 img/9-2.svg create mode 100644 img/9-3.svg diff --git a/9.md b/9.md index ec8b6e1..4eaabfb 100644 --- a/9.md +++ b/9.md @@ -1,9 +1,25 @@ ## 九、正则表达式 +> 原文:[Regular Expressions](https://eloquentjavascript.net/09_regexp.html) +> +> 译者:[飞龙](https://github.com/wizardforcel) +> +> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) +> +> 自豪地采用[谷歌翻译](https://translate.google.cn/) +> +> 部分参考了[《JavaScript 编程精解(第 2 版)》](https://book.douban.com/subject/26707144/) + +> 一些人遇到问题时会认为,“我知道了,我会用正则表达式。”现在它们有两个问题了。 +> +> Jamie Zawinski +> > Yuan-Ma said, 'When you cut against the grain of the wood, much strength is needed. When you program against the grain of the problem, much code is needed.' > > Master Yuan-Ma,《The Book of Programming》 +![](img/9-0.jpg) + 程序设计工具技术的发展与传播方式是在混乱中不断进化。在此过程中获胜的往往不是优雅或杰出的一方,而是那些瞄准主流市场,并能够填补市场需求的,或者碰巧与另一种成功的技术集成在一起的工具技术。 本章将会讨论正则表达式(regular expression)这种工具。正则表达式是一种描述字符串数据模式的方法。它们形成了一种小而独立的语言,也是 JavaScript 和许多其他语言和系统的一部分。 @@ -14,7 +30,7 @@ 正则表达式是一种对象类型。我们可以使用两种方法来构造正则表达式:一是使用`RegExp`构造器构造一个正则表达式对象;二是使用斜杠(`/`)字符将模式包围起来,生成一个字面值。 -``` +```js let re1 = new RegExp("abc"); let re2 = /abc/; ``` @@ -25,7 +41,7 @@ let re2 = /abc/; 第二种写法将模式写在斜杠之间,处理反斜杠的方式与第一种方法略有差别。首先,由于斜杠会结束整个模式,因此模式中包含斜杠时,需在斜杠前加上反斜杠。此外,如果反斜杠不是特殊字符代码(比如`\n`)的一部分,则会保留反斜杠,不像字符串中会将其忽略,也不会改变模式的含义。一些字符,比如问号、加号在正则表达式中有特殊含义,如果你想要表示其字符本身,需要在字符前加上反斜杠。 -``` +```js let eighteenPlus = /eighteen\+/; ``` @@ -33,7 +49,7 @@ let eighteenPlus = /eighteen\+/; 正则表达式对象有许多方法。其中最简单的就是`test`方法。`test`方法接受用户传递的字符串,并返回一个布尔值,表示字符串中是否包含能与表达式模式匹配的字符串。 -``` +```js console.log(/abc/.test("abcde")); // → true console.log(/abc/.test("abxde")); @@ -50,7 +66,7 @@ console.log(/abc/.test("abxde")); 下面两个表达式都可以匹配包含数字的字符串。 -``` +```js console.log(/[0123456789]/.test("in 1992")); // → true console.log(/[0-9]/.test("in 1992")); @@ -77,7 +93,7 @@ console.log(/[0-9]/.test("in 1992")); 因此你可以使用下面的表达式匹配类似于`30-01-2003 15:20`这样的日期数字格式: -``` +```js let dateTime = /\d\d-\d\d-\d\d\d\d \d\d:\d\d/; console.log(dateTime.test("30-01-2003 15:20")); // → true @@ -91,7 +107,7 @@ console.log(dateTime.test("30-jan-2003 15:20")); 你可以在左方括号后添加脱字符(`^`)来排除某个字符集,即表示不匹配这组字符中的任何字符。 -``` +```js let notBinary = /[^01]/; console.log(notBinary.test("1100100010100110")); // → false @@ -105,7 +121,7 @@ console.log(notBinary.test("1100100010200110")); 在正则表达式某个元素后面添加一个加号(`+`),表示该元素至少重复一次。因此`/\d+/`可以匹配一个或多个数字字符。 -``` +```js console.log(/'\d+'/.test("'123'")); // → true console.log(/'\d+'/.test("''")); @@ -120,7 +136,7 @@ console.log(/'\d*'/.test("''")); 元素后面跟一个问号表示这部分模式“可选”,即模式可能出现 0 次或 1 次。下面的例子可以匹配`neighbour`(`u`出现1次),也可以匹配`neighbor`(`u`没有出现)。 -``` +```js let neighbor = /neighbou?r/; console.log(neighbor.test("neighbour")); // → true @@ -132,7 +148,7 @@ console.log(neighbor.test("neighbor")); 这里给出另一个版本的正则表达式,可以匹配日期、月份、小时,每个数字都可以是一位或两位数字。这种形式更易于解释。 -``` +```js let dateTime = /\d{1,2}-\d{1,2}-\d{4} \d{1,2}:\d{2}/; console.log(dateTime.test("30-1-2003 8:45")); // → true @@ -144,7 +160,7 @@ console.log(dateTime.test("30-1-2003 8:45")); 为了一次性对多个元素使用`*`或者`+`,那么你必须使用圆括号,创建一个分组。对于后面的操作符来说,圆括号里的表达式算作单个元素。 -``` +```js let cartoonCrying = /boo+(hoo+)+/i; console.log(cartoonCrying.test("Boohoooohoohooo")); // → true @@ -158,7 +174,7 @@ console.log(cartoonCrying.test("Boohoooohoohooo")); `test`方法是匹配正则表达式最简单的方法。该方法只负责判断字符串是否与某个模式匹配。正则表达式还有一个`exec`(执行,execute)方法,如果无法匹配模式则返回`null`,否则返回一个表示匹配字符串信息的对象。 -``` +```js let match = /\d+/.exec("one two 100"); console.log(match); // → ["100"] @@ -170,14 +186,14 @@ console.log(match.index); 字符串也有一个类似的match方法。 -``` +```js console.log("one two 100".match(/\d+/)); // → ["100"] ``` 若正则表达式包含使用圆括号包围的子表达式分组,与这些分组匹配的文本也会出现在数组中。第一个元素是与整个模式匹配的字符串,其后是与第一个分组匹配的部分字符串(表达式中第一次出现左圆括号的那部分),然后是第二个分组。 -``` +```js let quotedText = /'([^']*)'/; console.log(quotedText.exec("she said 'hello'")); // → ["'hello'", "hello"] @@ -185,7 +201,7 @@ console.log(quotedText.exec("she said 'hello'")); 若分组最后没有匹配任何字符串(例如在元组后加上一个问号),结果数组中与该分组对应的元素将是`undefined`。类似的,若分组匹配了多个元素,则数组中只包含最后一个匹配项。 -``` +```js console.log(/bad(ly)?/.exec("bad")); // → ["bad", undefined] console.log(/(\d)+/.exec("123")); @@ -200,14 +216,14 @@ console.log(/(\d)+/.exec("123")); JavaScript 提供了用于表示日期的标准类,我们甚至可以用其表示时间点。该类型名为`Date`。如果使用`new`创建一个`Date`对象,你会得到当前的日期和时间。 -``` +```js console.log(new Date()); // → Mon Nov 13 2017 16:19:11 GMT+0100 (CET) ``` 你也可以创建表示特定时间的对象。 -``` +```js console.log(new Date(2009, 11, 9)); // → Wed Dec 09 2009 00:00:00 GMT+0100 (CET) console.log(new Date(2009, 11, 9, 12, 59, 59, 999)); @@ -220,7 +236,7 @@ JavaScript 中约定是:使用从 0 开始的数字表示月份(因此使用 时间戳存储为 UTC 时区中 1970 年以来的毫秒数。 这遵循一个由“Unix 时间”设定的约定,该约定是在那个时候发明的。 你可以对 1970 年以前的时间使用负数。 日期对象上的`getTime`方法返回这个数字。 你可以想象它会很大。 -``` +```js console.log(new Date(2013, 11, 19).getTime()); // → 1387407600000 console.log(new Date(1387407600000)); @@ -233,7 +249,7 @@ console.log(new Date(1387407600000)); 通过在希望捕获的那部分模式字符串两边加上圆括号,我们可以从字符串中创建对应的`Date`对象。 -``` +```js function getDate(string) { let [_, day, month, year] = /(\d{1,2})-(\d{1,2})-(\d{4})/.exec(string); @@ -253,7 +269,7 @@ console.log(getDate("30-1-2003")); 另一方面,如果我们想要确保日期字符串起始结束位置在单词边界上,可以使用`\b`标记。所谓单词边界,指的是起始和结束位置都是单词字符(也就是`\w`代表的字符集合),而起始位置的前一个字符以及结束位置的后一个字符不是单词字符。 -``` +```js console.log(/cat/.test("concatenate")); // → true console.log(/\bcat\b/.test("concatenate")); @@ -268,7 +284,7 @@ console.log(/\bcat\b/.test("concatenate")); 那么我们可以编写三个正则表达式并轮流测试,但还有一种更好的方式。管道符号(`|`)表示从其左侧的模式和右侧的模式任意选择一个进行匹配。因此代码如下所示。 -``` +```js let animalCount = /\b\d+ (pig|cow|chicken)s?\b/; console.log(animalCount.test("15 pigs")); // → true @@ -284,7 +300,7 @@ console.log(animalCount.test("15 pigchickens")); 为了进行实际的匹配,引擎会像处理流程图一样处理正则表达式。 这是上例中用于家畜表达式的图表: -![](../Images/00302.jpeg) +![](img/9-1.svg) 如果我们可以找到一条从图表左侧通往图表右侧的路径,则可以说“表达式产生了匹配”。我们保存在字符串中的当前位置,每移动通过一个盒子,就验证当前位置之后的部分字符串是否与该盒子匹配。 @@ -306,21 +322,19 @@ console.log(animalCount.test("15 pigchickens")); 正则表达式`/\b([01]+b|\d+|[\da-f]h)\b/`可以匹配三种字符串:以`b`结尾的二进制数字,以`h`结尾的十六进制数字(即以 16 为进制,字母`a`到`f`表示数字 10 到 15),或者没有后缀字符的常规十进制数字。这是对应的图表。 -![]() +![](img/9-2.svg) 当匹配该表达式时,常常会发生一种情况:输入的字符串进入上方(二进制)分支的匹配过程,但输入中并不包含二进制数字。我们以匹配字符串`"103"`为例,匹配过程只有遇到字符 3 时才知道进入了错误分支。该字符串匹配我们给出的表达式,但没有匹配目前应当处于的分支。 因此匹配器执行“回溯”。进入一个分支时,匹配器会记住当前位置(在本例中,是在字符串起始,刚刚通过图中第一个表示边界的盒子),因此若当前分支无法匹配,可以回退并尝试另一条分支。对于字符串`"103"`,遇到字符 3 之后,它会开始尝试匹配十六进制数字的分支,它会再次失败,因为数字后面没有`h`。所以它尝试匹配进制数字的分支,由于这条分支可以匹配,因此匹配器最后的会返回十进制数的匹配信息。 -![](../Images/00303.jpeg) - 一旦字符串与模式完全匹配,匹配器就会停止。这意味着多个分支都可能匹配一个字符串,但匹配器最后只会使用第一条分支(按照出现在正则表达式中的出现顺序排序)。 回溯也会发生在处理重复模式运算符(比如`+`和`*`)时。如果使用`"abcxe"`匹配`/^.*x/`,`.*`部分,首先尝试匹配整个字符串,接着引擎发现匹配模式还需要一个字符`x`。由于字符串结尾没有`x`,因此`*`运算符尝试少匹配一个字符。但匹配器依然无法在`abcx`之后找到`x`字符,因此它会再次回溯,此时`*`运算符只匹配`abc`。现在匹配器发现了所需的`x`,接着报告从位置 0 到位置 4 匹配成功。 我们有可能编写需要大量回溯的正则表达式。当模式能够以许多种不同方式匹配输入的一部分时,这种问题就会出现。例如,若我们在编写匹配二进制数字的正则表达式时,一时糊涂,可能会写出诸如`/([01]+)+b/`之类的表达式。 -![](../Images/00304.jpeg) +![](img/9-3.svg) 若我们尝试匹配一些只由 0 与 1 组成的长序列,匹配器首先会不断执行内部循环,直到它发现没有数字为止。接下来匹配器注意到,这里不存在`b`,因此向前回溯一个位置,开始执行外部循环,接着再次放弃,再次尝试执行一次内部循环。该过程会尝试这两个循环的所有可能路径。这意味着每多出一个字符,其工作量就会加倍。甚至只需较少的一堆字符,就可使匹配实际上永不停息地执行下去。 @@ -328,14 +342,14 @@ console.log(animalCount.test("15 pigchickens")); 字符串有一个`replace`方法,该方法可用于将字符串中的一部分替换为另一个字符串。 -``` +```js console.log("papa".replace("p", "m")); // → mapa ``` 该方法第一个参数也可以是正则表达式,这种情况下会替换正则表达式首先匹配的部分字符串。若在正则表达式后追加`g`选项(全局,Global),该方法会替换字符串中所有匹配项,而不是只替换第一个。 -``` +```js console.log("Borobudur".replace(/[ou]/, "a")); // → Barobudur console.log("Borobudur".replace(/[ou]/g, "a")); @@ -346,7 +360,7 @@ console.log("Borobudur".replace(/[ou]/g, "a")); 如果我们在替换字符串中使用元组,就可以体现出`replace`方法的真实威力。例如,假设我们有一个规模很大的字符串,包含了人的名字,每个名字占据一行,名字格式为“姓,名”。若我们想要交换姓名,并移除中间的逗号(转变成“名,姓”这种格式),我们可以使用下面的代码: -``` +```js console.log( "Liskov, Barbara\nMcCarthy, John\nWadler, Philip" .replace(/(\w+), (\w+)/g, "$2 $1")); @@ -361,7 +375,7 @@ console.log( 这里给出一个小示例: -``` +```js let s = "the cia and fbi"; console.log(s.replace(/\b(fbi|cia)\b/g, str => str.toUpperCase())); @@ -370,7 +384,7 @@ console.log(s.replace(/\b(fbi|cia)\b/g, 这里给出另一个值得讨论的示例: -``` +```js let stock = "1 lemon, 2 cabbages, and 101 eggs"; function minusOne(match, amount, unit) { amount = Number(amount) - 1; @@ -393,7 +407,7 @@ console.log(stock.replace(/(\d+) (\w+)/g, minusOne)); 使用`replace`编写一个函数移除 JavaScript 代码中的所有注释也是可能的。这里我们尝试一下: -``` +```js function stripComments(code) { return code.replace(/\/\/.*|\/\*[^]*\*\//g, ""); } @@ -417,7 +431,7 @@ console.log(stripComments("1 /* a */+/* b */ 1")); 而这便是我们想要的情况。通过让星号尽量少地匹配字符,我们可以匹配第一个`*/`,进而匹配一个块注释,而不会匹配过多内容。 -``` +```js function stripComments(code) { return code.replace(/\/\/.*|\/\*[^]*?\*\//g, ""); } @@ -435,7 +449,7 @@ console.log(stripComments("1 /* a */+/* b */ 1")); 这里给出一个示例。 -``` +```js let name = "harry"; let text = "Harry is a suspicious character."; let regexp = new RegExp("\\b(" + name + ")\\b", "gi"); @@ -449,7 +463,7 @@ console.log(text.replace(regexp, "_$1_")); 为了能够处理这种情况,我们可以在任何有特殊含义的字符前添加反斜杠。 -``` +```js let name = "dea+hl[]rd"; let text = "This dea+hl[]rd guy is super annoying."; let escaped = name.replace(/[^\w\s]/g, "\\$&"); @@ -464,7 +478,7 @@ console.log(text.replace(regexp, "_><_")); 但还有一个`search`方法,调用该方法时需要传递一个正则表达式。类似于`indexOf`,该方法会返回首先匹配的表达式的索引,若没有找到则返回 –1。 -``` +```js console.log(" word".search(/\S/)); // → 2 console.log(" ".search(/\S/)); @@ -481,7 +495,7 @@ console.log(" ".search(/\S/)); 所谓的极少数情况,指的是当正则表达式启用了全局(`g`)或者粘性(`y`),并且使用`exec`匹配模式的时候。此外,另一个解决方案应该是向`exec`传递的额外参数,但 JavaScript 的正则表达式接口能设计得如此合理才是怪事。 -``` +```js let pattern = /y/g; pattern.lastIndex = 3; let match = pattern.exec("xyzzy"); @@ -495,7 +509,7 @@ console.log(pattern.lastIndex); 全局和粘性选项之间的区别在于,启用粘性时,仅当匹配直接从`lastIndex`开始时,搜索才会成功,而全局搜索中,它会搜索匹配可能起始的所有位置。 -``` +```js let global = /abc/g; console.log(global.exec("xyz abc")); // → ["abc"] @@ -506,7 +520,7 @@ console.log(sticky.exec("xyz abc")); 对多个`exec`调用使用共享的正则表达式值时,这些`lastIndex`属性的自动更新可能会导致问题。 你的正则表达式可能意外地在之前的调用留下的索引处开始。 -``` +```js let digit = /\d/g; console.log(digit.exec("here it is: 1")); // → ["1"] @@ -516,7 +530,7 @@ console.log(digit.exec("and now: 1")); 全局选项还有一个值得深思的效果,它会改变`match`匹配字符串的工作方式。如果调用`match`时使用了全局表达式,不像`exec`返回的数组,`match`会找出所有匹配模式的字符串,并返回一个包含所有匹配字符串的数组。 -``` +```js console.log("Banana".match(/an/g)); // → ["an", "an"] ``` @@ -527,7 +541,7 @@ console.log("Banana".match(/an/g)); 一个常见的事情是,找出字符串中所有模式的出现位置,这种情况下,我们可以在循环中使用`lastIndex`和`exec`访问匹配的对象。 -``` +```js let input = "A string with 3 numbers in it... 42 and 88."; let number = /\b(\d+)\b/g; let match; @@ -576,7 +590,7 @@ outputdir=/home/marijn/enemies/davaeorn 由于我们需要逐行处理这种格式的文件,因此预处理时最好将文件分割成一行行文本。我们使用第 6 章中的`string.split("\n")`来分割文件内容。但是一些操作系统并非使用换行符来分隔行,而是使用回车符加换行符(`"\r\n"`)。考虑到这点,我们也可以使用正则表达式作为`split`方法的参数,我们使用类似于`/\r?\n/`的正则表达式,这样可以同时支持`"\n"`和`"\r\n"`两种分隔符。 -``` +```js function parseINI(string) { // Start with an object to hold the top-level fields let currentSection = {name: null, fields: []}; @@ -618,7 +632,7 @@ city=Tessaloniki`)); 另一个问题是,默认情况下,正则表达式使用代码单元,而不是实际的字符,正如第 5 章中所讨论的那样。 这意味着由两个代码单元组成的字符表现很奇怪。 -``` +```js console.log(/\ud83c\udf4e{3}/.test("\ud83c\udf4e\ud83c\udf4e\ud83c\udf4e")); // → false console.log(/<.>/.test("<\ud83c\udf39>")); @@ -633,7 +647,7 @@ console.log(/<.>/u.test("<\ud83c\udf39>")); 尽管这是刚刚标准化的,在撰写本文时尚未得到广泛支持,但可以在正则表达式中使用`\p`(必须启用 Unicode 选项)以匹配 Unicode 标准分配了给定属性的所有字符。 -``` +```js console.log(/\p{Script=Greek}/u.test("α")); // → true console.log(/\p{Script=Arabic}/u.test("α")); @@ -720,7 +734,7 @@ Code Golf 是一种游戏,尝试尽量用最少的字符来描述特定程序 需要帮助时,请参考本章总结中的表格。使用少量测试字符串来测试每个解决方案。 -``` +```js // Fill in the regular expressions verify(/.../, @@ -770,7 +784,7 @@ function verify(regexp, yes, no) { 思考一下可以区分这两种引号用法的模式,并手动调用`replace`方法进行正确替换。 -``` +```js let text = "'I'm the cook,' he said, 'it's my job.'"; // Change this call. console.log(text.replace(/A/g, "B")); @@ -781,7 +795,7 @@ console.log(text.replace(/A/g, "B")); 编写一个表达式,只匹配 JavaScript 风格的数字。支持数字前可选的正号与负号、十进制小数点、指数计数法(`5e-3`或`1E10`,指数前也需要支持可选的符号)。也请注意小数点前或小数点后的数字也是不必要的,但数字不能只有小数点。例如`.5`和`5.`都是合法的 JavaScript 数字,但单个点则不是。 -``` +```js // Fill in this regular expression. let number = /^...$/; diff --git a/img/9-0.jpg b/img/9-0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..efb3400153ac0c63f45f8da4329153c96bbc251d GIT binary patch literal 26842 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

M0Py2NaKEjYVMMcG_}Sz3>Z^FIoy?5E@*iVNu&} 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 + + + + + + + + + + + + + + + + + + + + + Created with Raphaël 2.1.0 + + + + " " + + + + + + + boundary + + + + boundary + + + + Group #1 + + + + "chicken" + + + + "cow" + + + + "pig" + + + + digit + + + + "s" + + + + + + diff --git a/img/9-2.svg b/img/9-2.svg new file mode 100644 index 0000000..a2fe450 --- /dev/null +++ b/img/9-2.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + Created with Snapword boundarygroup #1One of:01bOne of:digit-afhdigitword boundary diff --git a/img/9-3.svg b/img/9-3.svg new file mode 100644 index 0000000..493c9bc --- /dev/null +++ b/img/9-3.svg @@ -0,0 +1,175 @@ + + + + + + + + + + + + Created with Raphaël 2.1.0 + + + + "b" + + + + Group #1 + + + + One of: + + + + "1" + + + + "0" + + + + + +