Sivo 悉见

正则指引勘误表

匿名 · 更新于 2015/8/24

页数 页内位置 修改前 修改后 说明
3 例1-3中Python例子的注释 能匹配则返回RegexObject 能匹配则返回Match Object  
5 第3段第3行 请参考第241页 请参考第243页  
5 倒数第2段 判断返回值是否为None 判断返回值是否不为None “为”改为“不为”
10 例1-11,第1行 re.search(r"^[012]345]$", "2345") != null re.search(r"^[012]345]$", "2345]") != null 后一个2345改为2345]
10 例1-11,第3行 re.search(r"^[012\]345]$", "2345") != null re.search(r"^[012\]345]$", "2345]") != null 后一个2345改为2345]
10 例1-12,第2行 re.search(r"^[012\\]345]$", "3") != null re.search(r"^[012\]345]$", "3") != null 删去多余的反斜线
14 例1-20 四个例子的True/False全部标注反了 将四个例子的True/False取反,原来标为True的改为False,原来标为False的改为True  
20 表2-3最后一行 <[^>/]+/> <[^>]+/> 方括号内多了一个/
20 表2-3第1行 <[^/>][^>]*> <[^/][^>]*> 第一个方括号内多了一个>
22 例2-9第10行 re.findall(r"<[^/>][^>]*[^/>]>", re.findall(r"<[^/][^>]*[^/>]>", 第一个方括号内多了一个>
25 例3-4第3行 re.search(r"^ab+$","abab")!=None #=> True re.search(r"^ab+$","abab")!=None #=> False  
29 第4段 表格的tag是<tag> 表格的tag是<table> 格式不变
29 第2段第1行 之前匹配JavaScript的表达式是<script langu 之前匹配JavaScript的表达式是<script type="text/javascript language改为type
29 脚注第2行 <script <script> 少了结尾的>,而且阴影标错了;建议脚注里去掉所有阴影标注
31 图3-1“可选出现”上面的表达式 [^/]*[^/] [^>]*[^/]  
34 例3-3“应该匹配的”第二行代码 re.search(idCardRegex, "1101018001017016") != None re.search(idCardRegex, "110101800101701") != None 参数结尾多了一个6
38 第2段末尾 [\w.]{0,64} [\w.]{1,64},请注意用户名不能为空,最短应该包含1个字符 把表达式中的0改为1,最后新增文字描述
38 第2段 现在有些邮件服务商也容许用户名中出现点号等字符了,这种情况复杂些,此处不做考虑 现在有些邮件服务商也容许用户名中出现点号等字符了,情况比较复杂,暂时只考虑点号的情况  
38 最后一段表达式的开头 [-\w.]{0,64} [\w.]{1,64} 把表达式中的0改为1,同时去掉方括号内的横线
39 例3-8第一行代码的开头 r"^[-\w.]{0,64} r"^[\w.]{1,64} 把表达式中的0改为1,同时去掉方括号内的横线
40 第1行 re.search(idCard, “1101018001017016″) ! re.search(idCard, “110101800101701″) != None 参数结尾多了一个6
40 第4段(表格上面)倒数第2行 正则表达式虽然直接表示“匹配一段数值在0~255之间的文本” 正则表达式要匹配的虽然是“数值在0~255之间的文本”  
40 第4段 再将它转换为整数类型的变x 再将它转换为一个整型变量X  
41 中间表格最下面一行,匹配分钟数的表达式 (0?[1-9]|[0-5]\d|60) (0?[1-9]|[1-5]\d|60)  
41 最后一段文字 仔细分析 tag 中可能出现 > 它只可能 分析 tag 中可能出现的 >, 它只可能 去掉“仔细”,新增一个“的”字和一个逗号,保持原字数不变
43 例3-5上面一行 正则表达式是(jeff|jefferey)还是(Jeffrey|j 正则表达式是(jeff|jefferey)还是(jeffrey|jeff) 第2个jeffery的j大小写错误
43 最后一行 针对多选结构(option1|regex2) 针对多选结构(option1|option2)  
48 例3-23最后一行 2012-12-22 [2012-12-22]  
48 例3-23第1行代码 "\\0" "[\\0]"  
48 例3-23第2行代码 r"\0" r"[\0]"  
49 倒数第2行第一个表达式末尾部分 <\1> </\1> 少了一个/
52 例3-30第一个运行结果 9   应当为空
52 例3-31第一行代码 replaceAll("^(\\d)…… replaceAll("^(\\d)(\\d)…… 少了一个(\\d)
53 图3-5最下面一行 dat day 在“分组名”这一列
55 3.4最后一段 在本书中,为了使代码简洁和易于 在本书中,为保证代码简洁,易于理解  
56 例3-36第3行 去掉末尾的# => True   去掉末尾的# => True
56 倒数第3行末尾 ([0-9]{2) ([0-9]{2}) {2之后少了右花括号
60 例4-2结果 tomorrow I will wear a brown sweater standing in row <span class="h1">10</span> next to the rowdy guy tomorrow I will wear in brown sweater standing in <span class="h1">row</span> 10 next to the rowdy guy  
63 例4-4第1行末尾 \r\nast line" \r\nlast line last少了开头的l
64 例4-6下面的正文 \Z和\的主要差别在于 \Z和\z的主要差别在于 补上的z应当与之前的\保持同一格式
64 上面的表格 左边那列的两个$的格式与其它表格不统一 左边那列的两个$的格式改为与其它表格中正则表达式字符的格式相同  
64 下面的表格 最后一行最后一列\z能匹配的位置 箭头应该指向字符串结尾的换行符之后,也就是挪到带框的NL右侧  
71 正文倒数第4段第1行 其中(?!\一种组合 其中(?!…)嵌套在(?=…)中,(?=…)又与(?<=…)并列 请注意(?!…)之类表达式应使用正则表达式的格式,也就是与修改之前的(?!\格式相同
72 例4-19最下面一行 中英文混排的文本 中英文混排 应该去掉“的文本”
73 例4-20上面的表达式 (?=[-a-zA-Z0-9.]{0,255}(?![-a-zA-Z0-9.]))((?!-)[-a-zA-Z0-9]{1,63}\.)*((?!-)[-a-zA-Z0-9]){1,63}  (?=[-a-zA-Z0-9.]{0,255}(?![-a-zA-Z0-9.]))((?!-)[-a-zA-Z0-9]{1,63}\.)*(?!-)[-a-zA-Z0-9]{1,63}  最后一部分多了一对括号,应去掉
73 例4-20 hostnameRegex = r"^(?=[-a-zA-Z0-9.]{0,255}(?![-a-zA-Z0-9.]))((?!-)[-a-zA-Z0-9]{1,63}\.)*((?!-)[-a-zA-Z0-9]){1,63}$" hostnameRegex = r"^(?=[-a-zA-Z0-9.]{0,255}(?![-a-zA-Z0-9.]))((?!-)[-a-zA-Z0-9]{1,63}\.)*(?!-)[-a-zA-Z0-9]{1,63}$" 最后一部分多了一对括号,应去掉
75 例4-21中的表达式 r"(?!=ab)(cd)" r"(?<=ab)(cd)" 感叹号!改为尖括号<,两行代码都要修改
78 中间的代码段     缺少了标题,应补上:例4-23 并列多个环视
79 例4-23标题 例4-23 例4-24 因为之前漏标了例4-23,所以这里应该改为例4-24,才能与第一段末尾的文字对应
79 例4-24标题 例4-24 例4-25 因为之前漏标了例4-23,所以这里应该改为例4-25,才能与最后一段的文字对应
80 章末 补充一个小节 4.4.6 环视的陷阱

前面讲过,环视可以避免“牵一发而动全身”,集中关注局部,同时不干扰其它部分。大多数时候这是一个好的特性,但也有些时候会造成非常费解的问题。
比如,要改造匹配文本段落的正则表达式<p>.*?</p>,限制<p>和</p>中不能出现abc,似乎很容易想到用环视,把正则表达式改为<p>(?!.*abc).*?</p>。看起来似乎非常简单:环视(?!.*abc)限制了不容许出现包含abc的字符串,再用.*?真正匹配两个tag之间的文本。但是,此处使用环视的情况并没有想象的那么简单,因为环视(?!.*abc)并不会受到之后的</p>的限制,换句话说,如果</p>之后出现了abc,也会影响到环视的判断,导致整个匹配失败。

例4-25

re.search(r"<p>(?!.*abc).*?</p>", "<p>babc</p>") != None # => False
re.search(r"<p>(?!.*abc).*?</p>", "<p>bbbc</p>") != None # => True
re.search(r"<p>(?!.*abc).*?</p>", "<p>bbbc</p>abc") != None # => False

要解决这个问题,必须修改用于匹配<p>和</p>之间内容的元素,将最开始的.*?的“任意字符”改为“之后不能为abc的任意字符”,也就是把整个表达式改为<p>((?!abc).)*?</p>。

例4-26

re.search(r"<p>((?!abc).)*?</p>", "<p>babc</p>") != None # => False
re.search(r"<p>((?!abc).)*?</p>", "<p>bbbc</p>") != None # => True
re.search(r"<p>((?!abc).)*?</p>", "<p>bbbc</p>abc") != None # => True
这样不影响排版和页码,但请更新目录
87 第2段第2行的表达式 ^\d.*?$ ^\d.* 去掉最后的?$,格式不变
87 例5-6第3行的注释 #start of whoe regex #start of whole regex whole少了l
88 例5-6倒数第2行的注释 #end of whoe regex #end of whole regex whole少了l
88 5.4倒数第3行 例5-6同时制定了两种模式 例5-5同时指定了两种模式  
90 例5-7最后一行运行结果 ["feeBAR", "ZEEBBAR", "zeeBBAR"] ["feeBAR", "ZEEBAR", "zeeBAR"] 后2个单词都多了一个B
91 例5-8下面第1段第2行 \1不在区分大小写模式 \1不在不区分大小写模式 少了一个“不”字
91 例5-8下面第1段第2行 \1处在区分大小写模式 \1处在不区分大小写模式 少了一个“不”字
98 最上面表格第2行 \(…|…) \(…\|…\) 添加两个反斜线
98 表6-7下面一段的最后一行 (ab|c\)) (ab|b\)) c改为b
98 表6-7下面一段的最后一行 (ab|c)) (ab|b)) c改为b
101 例6-9最后一行 re.search(r"[()", "(") != None re.search(r"[(]", "(") != None 圆括号改为方括号
101 例6-9下面一行的倒数第3行 它可以匹配除^、a、b之外的任何字符 它可以匹配的字符^、a、b中任何一个  
102 6.2.2上方代码的最后一行 preg_replace("\\d+" preg_replace("/\\d+/" \\d+两端各少了一个/
107 表6-10(不算表头)第3行第1列 (ab)+ a+(bc)  
110 注1 因为同一种unicode字符在不同的编码(或者叫转换格式,比如UTF-8、UTF-16)中有不同的码值 在Unicode字符集中,同一个字符的码值(“代号”)是不变的,但是,在依照不同的“文字编码方式”(Character Encoding Scheme,或者叫“转换格式”,比如UTF-8、UTF-16)映射到实现中若干字节(octet strings)的时候,这一串octet的值会依据文字编码方式的不同而变化。关于编码的详细说明,可以参考《松本行弘的程序世界》第7章(人民邮电出版社,2011年8月)。  
113 最后一段第2行 [收发] 格式错误,应当标注为正则表达式格式 格式错误,正确格式可参考115页第1行的[收发]
113 倒数第2段 现在再来解释,虽然GBK编码也是多字节编码,为什么不推荐使用:常见的中文字符编码有GBK和Unicode两种,GBK编码使用确实也很多(尤其是在Windows平台上),但如果需要使用正则表达式处理中文,我强烈推荐使用Unicode字符。这样选择的最重要原因是,正则表达式处理程序一般只能准确识别Unicode字符的边界。 现在来解释,虽然GBK编码也是多字节编码,为什么不推荐使用:GBK编码作为常见的中文编码,使用确实很多(尤其是在Windows平台上),但在GBK编码环境下使用正则表达式,却可能遇到非常奇怪的现象,因为正则表达式处理程序一般只能准确识别Unicode字符的边界。 原来加粗的部分仍然需要加粗
113 倒数第2行 ca、d5和b7、a2 ca d5和b7 a2 去掉顿号
116 表7-2上方一段最后一行 \s匹配\S不能匹配的字符 \S匹配\s不能匹配的字符 调换\s和\S,但格式应保留
118 例7-12下方一段第2行 \b\regex\b \bregex\b 去掉regex之前的\
119 倒数第2段的参考页码 231 253  
119 倒数第1段的参考页码 213 234  
120 第1段的参考页码 105 6  
121 图7-3 代码点在哪个区间 码值在哪个区间 “码值”是书中统一的名称
122 倒数第2段代码 Regex.IsMatch("我", "\\p{IsCJKUnifiedIdeographs}");// => True Regex.IsMatch("我", @"\p{IsCJKUnifiedIdeographs}");// => True 遵循书中的惯例,.NET的正则表达式都用@原生字符串
135 脚注1的参考页 错误!未定义书签 144页  
136 最后一行 需要关注只是 需要关注的是  
137 倒数第2段末尾 末尾增加 这一点务必要熟记:使用多选结构进行正则表达式操作时,很可能因为多选结构的顺序问题得到不同的结果,具体情况在下一章介绍。  
140 9.1.2最后一段 综合这些情况,最终得到的表达式就是(+?(\d+ 综合这些情况,最终得到的表达式就是(+?(\d+|\.\d+|\d+\.\d+)|-?(\d+|\d+\.\d+))。 正则表达式去掉了中间的空格
140 表格下第一段 段尾补充内容 需要补充的是:前一章讲过,传统型NFA的匹配结果与多选分支的顺序有关,它会优先选择最左侧的多选分支。所以如果你用这个表达式来提取(而不是验证)3.2之类的数字,只能提取到3,如果要完整提取出3.2,必须改换多选分支的顺序,将匹配长度最长的分支移到左边,也就是[-+]?(\d+\.\d+|\.\d+|\d+)。这个问题先不多说,在9.2节还会详细讲到。  
144 第3段第2行 (?!<\.) (?<!\.) 调换!和<的顺序
144 第3段第3行表达式开头 (?!<\.) (?<!\.) 调换!和<的顺序
147 表9-2第6行第2列 re.find(pattern, string) re.findall(pattern, string) 应保持原有格式
147 表9-2第6行第3列 逐步进行 一次性进行  
148 例9-3的标题 面向对象式处理中 函数式处理中  
148 9.2.2上面一段 单独补充一段 提取操作还有一点容易忽视,就是多选分支的顺序。因为大多数语言和工具都使用传统型NFA引擎(详见第6章),多个匹配分支都能匹配时,优先选择左侧的分支。所以如果多个分支的匹配存在重叠,提取时一定要把能匹配更长文本的分支放在左侧。比如正则表达式\d+|\d+\.\d+,按照预期,\d+匹配24之类的数字,\d+\.\d+匹配3.14之类的数字,如果字符串是2.72,“应当”可以全部提取出来,但是因为\d+在左侧,优先得到匹配,所以提取的结果是2,而不是2.72。 这里增加的内容较多,会不会影响整个排版?如果不方便可以把9.2.2节(150页)出现三次的“5点要求”压缩,行距紧密一些(类似130页最下的4个条件那样排版),可否?
151 最后 单独补充一段 需要补充的是:提取操作时应当注意多选分支的顺序,否则会导致匹配不完整的情况,验证时则不需要考虑这一点。仍然举正则表达式\d+|\d+\.\d+,字符串3.14为例,虽然\d+可以匹配3,但最终验证的是3.14是不是能够由表达式完整匹配,这个判断不受多选分支顺序的影响。  
163 例9-14第2行的表达式 r"\A.[0-9A-Za-z-]*\Z" r"\A[0-9A-Za-z-]*\Z" \A之后多了一个.,应当去掉
165 例9-16 代码第3行 int(str) % 400 != 0 int(str) % 400 == 0  
165 例9-16 运行结果最下面两行 validateLeapYear("2000") #=> False
validateLeapYear("2100") #=> True
validateLeapYear("2000") #=> True
validateLeapYear("2100") #=> False
把两行的运行结果调换过来
208 最下面一段代码 the price is 12.99.replace(/\d+\.\d{0,2}/, "$$$0"); the price is 12.99.replace(/\d+\.\d{0,2}/, "$$$&");  
208 倒数第二段文字 在最后补充 请注意JavaScript不支持$0,必须写成$&。  
209 表12-3 新增一行 左侧:$&;右侧:整个表达式匹配的文本  
239 倒数第2行的第2个表达式 (?!<…) (?<!…) 格式应保留
245 14.3.6上方的示例代码 re.search re.match 全部re.search都应当改为re.match
247 倒数第2段代码的执行结果 One TWO THREE One Two Three 大小写弄混了
IV 第1个例子 re.search(ch, "[0-9]") != None re.search("[0-9]", ch) != None 两个参数写反了
IV 第2个例子 re.search(str, "[1-9][0-9]{6,7}") != None re.search("[1-9][0-9]{6,7}", str) != None 两个参数写反了
IV 第3个例子 re.search(str, '(?<![0-9])[1-9][0-9]{6,7}(?![0-9])') != None re.search('(?<![0-9])[1-9][0-9]{6,7}(?![0-9])', str) != None 两个参数写反了,且单引号应该改为双引号
X “致谢”第二段 整段修改 首先要感谢的是李笑来老师、周筠老师、徐定翔和卢鸫翔两位编辑。在我翻译完《精通》之后,李笑来老师三番五次地鼓励我写一本关于正则表达式的书,并且打消了我的很多顾虑;周筠老师、徐定翔和卢鸫翔两位编辑在我写作的最初阶段做了大量细致耐心的工作。可以说,没有他们,我就不会有写作这本书的念头,也不会有坚持完成的动力。  
勒口 个人简介第一段 现任华南某电商网站技术部总监 现任华南某电商网站技术总监 去掉了“部”