Regex
正则表达式基础
\b 匹配一个单词边界,也就是指单词和空格间的位置(即正则表达式的“匹配”有两种概念,一种是匹配字符,一种是匹配位置,这里的\b就是匹配位置的)。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。
\B 匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。
\d 匹配一个数字字符。等价于[0-9]。grep 要加上-P,perl正则支持
\D 匹配一个非数字字符。等价于[^0-9]。grep要加上-P,perl正则支持
(pattern) 匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“(”或“)”。
(?:pattern) 非获取匹配,匹配pattern但不获取匹配结果,不进行存储供以后使用。这在使用或字符“(|)”来组合一个模式的各个部分时很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。
(?=pattern) 非获取匹配,正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern) 非获取匹配,正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串,该匹配不需要获取供以后使用。例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”。
(?<=pattern) 非获取匹配,反向肯定预查,与正向肯定预查类似,只是方向相反。例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。
(?<!pattern) 非获取匹配,反向否定预查,与正向否定预查类似,只是方向相反。例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。这个地方不正确,有问题 此处用或任意一项都不能超过2位,如“(?<!95|98|NT|20)Windows正确,“(?<!95|980|NT|20)Windows 报错,若是单独使用则无限制,如(?<!2000)Windows 正确匹配
正则表达式中 ?= 和 ?: 的区别
Problem: read from split string 我们通过一个函数把一个 number 类型的数字转换成一个字符串,并且每三位给他加上一个 ‘,’: 1999 -> 1,999
Answer:
function groupByCommas(n) {
return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
console.log(groupByCommas(1234567)); //1,234,567
Change 1: 如果我们删掉(\d{3})后面的 ‘+’的话,全局标志依然还在,但是这个时候,就只能匹配字符串中的部分了。
function groupByCommas(n) {
return n.toString().replace(/\B(?=(\d{3})(?!\d))/g, ",");
}
console.log(groupByCommas(1234567)); //1234,567
Change 2: 最后如果我们把 ?= 换成 ?: 的话:
function groupByCommas(n) {
return n.toString().replace(/\B(?:(\d{3})+(?!\d))/g, ",");
}
console.log(groupByCommas(1234567)); //1,
Explain:
区别在于 ?= 是正向肯定 断言,进行的匹配是不占查询长度的;而 ?: 是非获取 匹配,进行的匹配是占据查询长度的。
题述的正则查询每一个非单词边界,然后对后面的一个或多个连续三组数字+一组非数字进行匹配。对于 1234567 而言,就会匹配到 1 和 2 中间的这个非单词边界,因为后面的 234567$ 满足正向肯定预查的 (\d{3})+(?!\d) 形式;之后会匹配到 4 和 5 中间的非单词边界,因为后面的 567$ 也满足上一形式。所以是正确的。
而你尝试将 + 去掉,使得断言只能匹配到 567$ 这样的形式——注意到你强调了 g 全局查询参数,但是我们要注意到 (?!\d) 的存在,这是一个正向否定断言,表示连续三个数字之后不能存在数字,所以 234 显然是不满足的,因为其后的 5 正是一个数字。假使你去掉了这个否定断言,那这个正则也不能工作——因为断言是 零宽 的,是不占据匹配长度的,查完 1 之后 234 满足,还会继续查 2,2 之后 345 也是满足的。因此结果就会变成 “1,2,3,4,567”。
最后你尝试使用了 ?: 这个非获取匹配实际上是占据匹配长度的,当执行了第一次匹配时,实际上就匹配到了行尾,直接将 234567 全替换成了 ,,然后完成了匹配。所以就出现了上面的结果。