场景

要将下面的字符串截取两个{title:***,url:***}来。

1
2
3
4
5
<script>
{title:'RegExp 对象参考手册',url:'http://sodino.com/regexp.asp'},
{title:'w3cSchool script', url:'http://www.w3cSchool.com'},
{}
</script>

正则表达式一

1
2
3
4
5
6
7
8
9
10
var string = '<script>'
+"{title:'RegExp 对象参考手册',url:'http://sodino.com/regexp.asp'},"
+"{title:'w3cSchool script', url:'http://www.w3cSchool.com'},"
+"{}"
+'</script>';

var reg = /\{.+\}/g; // 正则表达式

var result = string.match(reg);
console.log(result);

这个毫无疑问是全匹配,即输出为第一个{到最后一个}之间的全部内容:

1
2
D:\github\niVoVin>node test.js
[ '{title:\'RegExp 对象参考手册\',url:\'http://sodino.com/regexp.asp\'},{title:\'w3cSchool script\', url:\'http://www.w3cSchool.com\'},{}' ]

正则表达式二

书上说在表达式的后面加上?就是非贪婪模式了。即n?匹配包含零个或一个n的字符串。
那么目前想的是只匹配一个},那么将表达式修改为如下:

1
var reg = /\{.+\}?/g;

运行一下代码,发现输出是竟然是第一个{到字符串结尾:

1
2
D:\github\niVoVin>node test.js
[ '{title:\'RegExp 对象参考手册\',url:\'http://sodino.com/regexp.asp\'},{title:\'w3cSchool script\', url:\'http://www.w3cSchool.com\'},{}</script>' ]

?的作用竟然是忽略}吗?
陷入了深深的疑惑……


排查问题

当表达式一和表达式二对比时,直觉告诉你是由于引入?才导致的错误。
那么需要来验证下。

1
var reg = /script>?/g;

更换表达式,验证是否?一样会忽略掉>???
执行输出得到:

1
2
D:\github\niVoVin>node test.js
[ 'script>', 'script', 'script>' ]

所以?并不是忽略的作用。所以直觉不对。

再看表达式二的输出结果,结果是将第一个{后的内容全部输出,那就是说表达式二的结果是由于一直在匹配.+(任意数量的任意字符,除了换行和结束符),而没有匹配到截止符}?的原因。
而由于}?处于可匹配可不匹配的情况,解释器优先选择不匹配,导致截止符}是无效的。
参考链接正则基础之——贪婪与非贪婪模式

那么解决办法就变为截止符}不可设置为非贪婪,这样才有截止作用,而对应的是对.+(任意字符)执行非贪婪匹配,尽可能少的匹配{}之间的内容。


正则表达式三

1
var reg = /\{.+?\}/g;

输出结果:

1
2
3
D:\github\niVoVin>node test.js
[ '{title:\'RegExp 对象参考手册\',url:\'http://sodino.com/regexp.asp\'}',
'{title:\'w3cSchool script\', url:\'http://www.w3cSchool.com\'}' ]

很圆满。

由于.++表示最少出现一次.,所以第三行的空{}并没有被匹配成功。

结论

在表达式三中,.+表达任意字符串,}是截止符。
当使用n+ n* n?截取任意字符串时,如果待截取内容包括任意字符串,则非贪婪模式应尽可能少的匹配任中间的任意字符串,而非尽可能少的匹配截止符。


About Sodino