一个正则表达式引发的空白

最近在开发内部的一个前端异常监控系统,在 Web 端会展示异常上报的来源 IP 以及所在地,大概是这个样子的:

由于页面空间有限且几乎所有的异常都来自国内,因此做了一个简单的优化,将所在地字符串开头处的“中国”二字移除掉,写个很简单的正则表达式即可:

result.replace(/^中国/, "")

改完以后,效果还不错:

直到有一天小伙伴反馈问题,有的 IP 的地理位置展示了空白,像这样:

简单调查了一下发现该 IP 的查询结果只有“中国”二字,于是经过前面的处理逻辑后就只剩下空字符串了。

解决方案

最直接的想法就是增加一个 if / else 判断,如果查询结果是“中国”二字,则跳过正则处理。

虽然直接,但是感觉不够简洁。能否在原来的正则表达式上优化一把来实现我们要的效果呢?使用今天要说的 lookahead 断言就可以。

lookahead 断言

在说 lookahead 断言前我们先看看另一个写法:

result.replace(/^中国./, "")

这种写法在“中国”二字后多匹配了一个字符,这样写可以避免处理仅有“中国”二字的情况,但带来一个新的问题:在多个字的情况下,多删掉了一个字符。

我们想要的只是一个 0 长度的断言而不是真正匹配什么内容,就像 \b^$ 那样,只表示某个条件是否达成,但并不匹配字符串中的内容。

这就是 lookahead 的意义,我们只需将正则改写成:

result.replace(/^中国(?=.)/, "")

就可以表示只匹配之后还有其它字符的“中国”二字的含义。

lookahead 断言的写法就是 (?=(regex)) 其中(regex) 就是你想断言的条件。

只需要将 = 改成 ! 你就得到了一个否定形式的 lookahead 断言,例如 /^中国(?!.)/ 就表示之后没有任何其它字符的“中国”二字

lookbehind 断言

与 lookahead 对应的,还有 lookbehind 断言以及它的否定形式:

/(?<=(regex))/
/(?<!(regex))/

具体逻辑不再赘述。

延伸阅读