• 发生时间:2022-07-11
  • 问题描述:
    • 运行某个 JS 仓库的测试用例,有 2 个失败。报错信息为 JSON.parse error: TypeError: Cannot read property '0' of null
  • 问题类别:软件测试
  • 原因分析:
    • 阅读源代码,定位到报错代码为 const content = frame.content.toString().match(/\{.*?\}/g)[0]
    • 根据报错信息,可以推断 match 的结果为 null,导致索引其第一个元素失败。
    • frame.content 的值为读取文件 tasks/test/assets/auto_detect_result.json 得到的字节流。
    • 分析至此,可以怀疑这里正则表达式的写法了。/\{.*?\}/g匹配的是以一对花括号为边界、中间为任意字符的字符串, 按道理应该能匹配 json 字符串才对,为啥匹配不到东西呢?
    • 查阅 MDN 「Regular_Expressions」 下面的 「Character classes」文档 发现, 点号无法匹配换行符! 而待匹配的字符串中含有很多换行符!这就是匹配不上的原因。
    • 另外,这里正则表达式中对问号的使用也是有风险的,应该删掉更合适。 因为问号代表「一旦匹配到就停止」(详见下文「JavaScript 正则表达式中的问号」), 比如对于字符串{{}},会匹配到{{}就停止,而我们期望匹配到{{}}
  • 解决方案:在 stack overflow 找到两种解决方案:
    • 方案一:使用 [^] 代替点号。这种方案只适用于 JavaScript。
    • 方案二:使用 [\s\S] 代替点号。这种方案可移植性更好,很多语言都适用,比如 JavaScript、Python 和 Golang。
  • 实施结果:采用方案一后,运行 ft4_tasks 的测试用例,全部 pass。
  • 经验总结:
    • 大多数编程语言的正则表达式中的点号默认不会匹配换行符。经过实测,JavaScript、Python 和 Golang 都这样。
    • 要想匹配包括换行符在内的任意字符,可以使用[\s\S]。经过实测,JavaScript、Python 和 Golang 都支持这样匹配。

各门编程语言的正则表达式的点号

JavaScript

以下是 MDN 文档 对 JavaScript 正则表达式里面的点号 . 的描述:

  • . Has one of the following meanings:
    • Matches any single character except line terminators: \n, \r, \u2028 or \u2029. For example, /.y/ matches “my” and “ay”, but not “yes”, in “yes make my day”.
    • Inside a character class, the dot loses its special meaning and matches a literal dot.
    • Note that the m multiline flag doesn’t change the dot behavior. So to match a pattern across multiple lines, the character class [^] can be used — it will match any character including newlines.
    • ES2018 added the s “dotAll” flag, which allows the dot to also match line terminators.

Python

以下是 Python 官方文档 对 Python 正则表达式里面的点号 . 的描述:

  • (Dot.) In the default mode, this matches any character except a newline.
  • If the DOTALL flag has been specified, this matches any character including a newline.

Golang

以下是 Go 官方文档 对 Golang 正则表达式里面的点号 . 的描述:

  • any character, possibly including newline (flag s=true)

JavaScript 正则表达式的问号

以下是 MDN 文档 对 JavaScript 正则表达式里面的问号 ? 的描述:

  • By default quantifiers like * and + are “greedy”, meaning that they try to match as much of the string as possible.

  • The ? character after the quantifier makes the quantifier “non-greedy”: meaning that it will stop as soon as it finds a match.

  • For example, given a string like some <foo> <bar> new </bar> </foo> thing:

    • /<.*>/ will match <foo> <bar> new </bar> </foo>
    • /<.*?>/ will match <foo>