这里是对正则表达式的思考,源自一个题目:

完成一个 extractStr 函数,可以把一个字符串中所有的 : 到 . 的子串解析出来并且存放到一个数组当中,例如:
extractStr('My name is:Jerry. My age is:12.') // => ['Jerry', '12']
注意,: 和 . 之间不包含 : 和 .。也即是说,如果 ::abc..,则返回 [‘abc’]。

他给出的答案是

1
2
3
4
const extractStr = (str) => {
const ret = str.match(/:([^:\.])*?\./g) || []
return ret.map((subStr) => subStr.replace(/[:\.]/g, ''))
}

首先正则表达式匹配到的结果是[‘:Jerry.’,’:12.’],然后再对数组进行操作,去掉 :.
这样做不就多了一步了嘛,其实我们完全可以用正则来一步解决

1
2
3
4
5
6
7
const extractStr = (str) => {
const reg = /(?<=:)(\w*)(?=\.)/g;
const ret = str.match(reg) || []
return ret;
}
var str = 'My name is:Jerry. My age is:12.'
extractStr(str);

这样一步,就能得到我们的结果['Jerry','12'],而且有多余的 : 或者 . 也可以匹配到正确的结果。接下来解释一下这个正则表达式。
/(?<=:)(\w*)(?=\.)/g;
我们先看大部分正则都有的部分(\w*)\w*就是匹配任意字符串0个或多个,()的意思则是捕获,即记住这个捕获的内容。所以合起来就是匹配任意字符串0个或多个并且记住匹配项。
然后是正则表达式 /.../g 后面的’g’,这是正则表达式标志:

标志 描述
g 全局搜索。
i 不区分大小写搜索。
m 多行搜索。
y 执行“粘性”搜索,匹配从目标字符串的当前位置开始,可以使用y标志。

使用 g 之后,不会因为匹配到一个匹配项就停下,而是继续搜索。
接下来就是两个不常见的 (?<=:)(?=\.)
首先解释一下名字 (?<=:) 的意思是反向肯定查找,而(?=\.)的意思就是正向肯定查找。我们分两部分,(?<=:)(\w*)(\w*)(?=\.)。这样就清楚了,正向肯定查找会匹配到 Jerry 仅仅当它后面跟着 .,且不会把 . 当做匹配的内容。反向肯定查找会匹配到 Jerry 仅仅当它前面跟着:,且不会把 : 当做匹配的内容。两个结合起来就是,正则表达式会匹配到任意字符串仅仅当它的前面跟着 :,且仅仅当它后面跟着 .

注意:因为 . 在正则表达式里面代表除换行符之外的任何单个字符,所以需要转义才能匹配到 .,即 \.