技术控

    今日:96| 主题:49531
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] 写正则不要再瞎转义了

[复制链接]
南国未暖 发表于 2016-11-28 15:50:04
98 0

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
在 JavaScript 中,有两个地方用到了反斜杠转义序列,一个是在字符串字面量里,一个是在正则字面量里。其中字符串字面量里的反斜杠转义序列又分为下面几种形式:
  1. \ 后面跟着单引号(')、双引号(")、反斜杠自己(\)、b、f、n、r、t、v 其中的一个
  2. \ 后面跟着某个行终止符序列,常见的行终止符序列有三种:回车、换行、回车+换行
  3. \ 后面跟着 0
  4. \ 后面跟着 1 到 3 个八进制数字
  5. \ 后面跟着 x 再跟着两个 16 进制数字
  6. \ 后面跟着 u 再跟着 4 个 16 进制数字
  7. \ 后面跟着 u 再跟着 {1个到任意多个的 16 进制数字}
  8. \ 后面跟着某个不满足上面所有这些条件的单个字符
  前 7 种本文不予讨论,这第 8 种转义形式其实就是无效的转义。举个例子,比如 \o 就是这样的转义,在一些编译语言里,这样的转义会直接报编译错误,比如在 Java 里:

  1. System.out.println("\o"); // error: illegal escape character
复制代码
另外在 JSON 里也会报错:

  1. JSON.parse(String.raw`"\o"`) // SyntaxError: Unexpected token o in JSON at position 2
复制代码
在脚本语言里通常都不会报错,它们有两种选择,要么不把 \ 看成转义字符,而是看成普通的反斜杠字面量,像 Python:

  1. >>> "\o"
  2. '\\o' # len("\o") 为 2
复制代码
要么把 \ 丢弃掉,只留下后面被转义的那个字符,像 JavaScript:

  1. js> "\o"
  2. "o" // "\o".length 为 1
复制代码
哪种做法好呢?我知道一个保留反斜杠的好处是和正则相关的:在一些没有正则字面量的语言里,或者是有些人不会用正则字面量,又或者有时需要从字符串变量动态生成正则的时候,有些人会忘了要双写反斜杠,比如:

  1. "www\.taobao\.com" // 在不保留反斜杠的语言比如 JavaScript 里,这个字符串生成的正则可以错误的匹配到别的域名,比如 "wwwataobao.com"
复制代码
还有些语言会对这个坑会发出警告:

  1. $ awk -F 'www\.taobao\.com' ''
  2. awk: warning: escape sequence `\.' treated as plain `.'
复制代码
丢弃反斜杠在其它语言里有什么好处我不清楚,但在 JavaScript 里,我还真知道一个,那就是在一个内联的 <script> 标签里书写一个包含有 </script> 字样的字符串时:

  1. <script>
  2. document.wirte("<script src=foo.js><\/script>") // \/ 其实是无效的转义,但没有 \ 的话,这个 </script> 会被 HTML 解析器错误的当成结束标签
  3. </script>
复制代码
下面才开始本文的正文,正则字面量中的反斜杠转义序列比字符串字面量中的更复杂些,分为下面这么多种形式:
  1. \ 后面跟着 /
  2. \ 后面跟着 ^、$、\、.、*、+、?、(、)、[、]、{、}、| 其中的一个
  3. \ 后面跟着 c 再跟任意一个字母
  4. \ 不存在于 [] 中,后面跟着 b 或者 B
  5. \ 存在于于 [] 中,后面跟着 - 或者 b
  6. \ 后面跟着 d、D、s、S、w、W 其中的一个
  7. \ 后面跟着 f、n、r、t、v 其中的一个
  8. \ 后面跟着 0
  9. \ 不存在于 [] 中,后面跟着 1 到 3 个十进制数字
  10. \ 后面跟着 x 再跟着两个 16 进制数字
  11. \ 后面跟着 u 再跟着 4 个 16 进制数字
  12. \ 后面跟着 u 再跟着 {1个到任意多个的 16 进制数字}
  13. \ 后面跟着某个不满足上面所有这些条件的单个字符
  上面这些转义形式有些和字符串字面量中的相同,有些则不同,甚至有些虽然外表看起来相同,功能却不同。我们还是重点关注最后一种情况,也就是无效转义的情况。很多人记不住在正则里哪些符号应该转义,哪些不该转义,比如双引号 " 在正则里是不需要转义的,如果你写了 /\"/,通常情况下,JavaScript 的正则引擎会帮你把 \ 去掉:

  1. /^"$/.test('"') // true
复制代码
但如果这个正则开启了 Unicode 模式,则这样的写法会导致语法错误:

  1. /"/u // SyntaxError: Invalid escape
复制代码
那你可能会问,为什么要开启 Unicode 模式呢?这是因为 Unicode 模式对 BMP 之外的字符支持更友好,比如:

  1. "?野家".match(/./g) // ["�", "�", "野", "家"]
  2. "?野家".match(/./ug) // ["?", "野", "家"]
复制代码
具体的优点可以看    这篇文章的总结,总之,如果不考虑兼容性的话,默认加上 /u 总是最佳做法。  
  还有一个经常被错误转义的字符,那就是连字符 -,连字符只在中括号里面才是元字符,在中括号里面需要转义,但如果你在中括号外面转义它的话,同样在 Unicode 模式下会报错:

  1. JSON.parse(String.raw`"\o"`) // SyntaxError: Unexpected token o in JSON at position 2
  2. 0
复制代码
另外,从 Firefox 46 和 Chrome 53 开始,在 HTML 表单的 pattern 属性中填写的正则开始强制使用 Unicode 模式,比如下面这个 input 的 pattern 属性就是无效的。打开下面这个 demo,然后打开开发者工具,然后把鼠标指针移动到 input 上,就能看到开发者工具的控制台出现了报错信息:

  1. JSON.parse(String.raw`"\o"`) // SyntaxError: Unexpected token o in JSON at position 2
  2. 1
复制代码
因为这个改动是不向后兼容的,所以一些开发者们发现自己以前运行的好好的代码突然报错了:
  转义了 @ 和 %    http://stackoverflow.com/questions/36953775/firefox-error-unable-to-check-input-because-the-pattern-is-not-a-valid-regexp  
  转义了 !    https://input.mozilla.org/en-US/dashboard/response/5898357  
  转义了 -    http://stackoverflow.com/questions/39895209/html-input-pattern-not-working  
  转义了 '    https://bugs.chromium.org/p/chromium/issues/detail?id=667713  
  可以看见,只要是标点符号,就有人想转义,因为他们对正则不熟悉,不知道哪些符号是元字符,以前这样做没事,但从现在开始,不行了。
  Unicode 模式就像是正则里的严格模式,禁止了很多不好的、容易导致 bug 的写法,下面再举一个 /u 禁止了的、和 \ 转义相关的写法,那就是 \ 后面跟非 0 十进制数字的情况:
  在非 Unicode 模式下,当 \ 后面是一个非 0 的十进制数字时,如果这个数字对应的捕获分组刚好存在,则该转义序列表示反向引用那个分组:

  1. JSON.parse(String.raw`"\o"`) // SyntaxError: Unexpected token o in JSON at position 2
  2. 2
复制代码
如果对应的捕获分组不存在,且数字 < 8 的话,则该序列会被当做八进制转义序列看待:

  1. JSON.parse(String.raw`"\o"`) // SyntaxError: Unexpected token o in JSON at position 2
  2. 3
复制代码
如果对应的捕获分组不存在,且数字 >= 8 的话,反斜杠会被丢弃,只留下数字:

  1. JSON.parse(String.raw`"\o"`) // SyntaxError: Unexpected token o in JSON at position 2
  2. 4
复制代码
也就是说,一样的转义写法可能有三种不同的解释,稍不留神就会导致 bug,代码也不好读,因此 Unicode 模式禁用了后两种情况,\ 后跟非 0 十进制数字只能表示捕获分组的反向引用,只要对应的捕获分组不存在,就报语法错误:

  1. JSON.parse(String.raw`"\o"`) // SyntaxError: Unexpected token o in JSON at position 2
  2. 5
复制代码
总结:本文列举了几个在正则的 Unicode 模式下不正确的转义形式,告诫大家以后在写正则的时候不能看到标点符号就想转义,对待知识要一丝不苟。
  更高要求:其实正则的 Unicode 模式并没有我希望的那么严格,比如正则里的大多数元字符,实际上在中括号里并不是元字符,是不需要转义的,但即便 Unicode 模式下也并不会禁止这样的写法:

  1. JSON.parse(String.raw`"\o"`) // SyntaxError: Unexpected token o in JSON at position 2
  2. 6
复制代码
友荐云推荐




上一篇:Node 性能优化
下一篇:用Python侦测比特币交易的网络可视化分析
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表