以前发过一些文章:
正则表达式的Javascript应用
Extjs 正则表达式的优雅应用 -1
近来再看mastering regular expressions 想到的一些有趣的正则表达式细节问题,首先看
1.一段程序 :
var regs=[/x*/g,/x*$/g,/^x*/g,/^x*$/g,/x*?/g,/^x*?/g,/x*?$/g,/^x*?$/g]; for(var i=0,reg=null;reg=regs[i++];){ console.log("*************reg :" +reg.source); console.log("xx".replace(reg,"y")); console.log("xx".match(reg)); var m; var j=0; while((m=reg.exec("xx"))&&++j<5){ console.log(m[0]+" - "+reg.lastIndex) //reg.lastIndex+=1 } }
可以先想一下结果 ,下面为运行输出:
*************reg :x* yy ["xx", ""] xx - 2 - 2 - 2 - 2 *************reg :x*$ yy ["xx", ""] xx - 2 - 2 - 2 - 2 *************reg :^x* y ["xx"] xx - 2 *************reg :^x*$ y ["xx"] xx - 2 *************reg :x*? yxyxy ["", "", ""] - 0 - 0 - 0 - 0 *************reg :^x*? yxx [""] - 0 - 0 - 0 - 0 *************reg :x*?$ yy ["xx", ""] xx - 2 - 2 - 2 - 2 *************reg :^x*?$ y ["xx"] xx - 2
其中涉及到了:
-1. Backtracking(回朔的概念): 对每个量词(quantifier)以及或(||),正则引擎必须做出一个决定,对于量词(*,+,?,{2,}),正则引擎必须决定是否要多匹配一些字符,而对于或(字符类[a-z]或简写\s,.除外)必须决定选择哪个分支。
每次引擎作出了一个选择,它都会记住其他可能的选项,如果这次匹配失败则会退回到上次做选择的量词或位置,并选择其他的选项继续进行匹配。
举例:(From High performance javascript)
0. * 量词可以匹配空,zero match
1.匹配分为位置(开头,字符间,结尾)与字符 两类,match 的所有匹配结果会多一些
2.replace g 时,当遇到空匹配 时在当前位置(开头,字符间,结尾)匹配成功,正则引擎会强行bump-along到下一匹配处 (防止空匹配死循环匹配)
这解释了 /x*?/g 的交替情况
3.为了达到正则表达式整体匹配,后面的表达式会驱动前面的表达式回朔 ,对于前面为x*
x* 会迫使量词匹配次减 1 ,直到为空匹配则这时一定成功
这解释了 /x*/ ,/x*$/ 一次匹配 xx ,二次到结尾位置空匹配,但是注意 /x*/ 并不包括开头位置^匹配,x*为贪婪匹配,开头位置不能满足,但是bump后,第一个x可以满足,所以无须匹配开头,直接匹配到 xx全部,但是第二次只剩下空字符串,为了匹配成功,才选择了空匹配,可见:
"xx".replace(/y*/g,"y")
对于前面为 x*?
x*? 会迫使量词增1,直到当前字符不能匹配x位置
这解释了 /x*?$/为了取得 在第一个 x处,为了达到匹配,扩展到 匹配 xx的情况,在第二次空字符串时,就默认0匹配成功了。
5.Regexp.lastIndex 为 the position of the first character after the match,但是使用 exec 时遇到结尾空匹配 并不会像 replace一样会强行驱动 ,对*量词连续使用 exec 会造成死循环 ,同java不同,java 连续find遇到结尾空匹配会自动bump的:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Test {
public static void main(String[] args) throws Exception {
Pattern p = Pattern.compile("x*");
Matcher m = p.matcher("xx");
while (m.find()) {
System.out.println("match : "+m.group());
}
System.out.println("xx".replaceAll("x*","y"));
}
}
2. 多行模式问题:
"1\n\n2\n3".replace(/^.*$/mg,"m"); // m\nm\nm\nm\n
^ : 匹配于当前整串头位置,或者 \n 后一个位置,相当于(?<=\n),js不支持
$ : 匹配于整串结尾位置,或者 \n 前一个位置,相当于(?=\n)
. : javascript中不会匹配 \n ,不可设置多行模式,则要匹配到 换行则要手动匹配
若要匹配任何字符则要使用:[\d\D],[\s\S],[\w\W],[\0-\uffff] 以及(?:.|\r|\n|\u2028|\u2029) 尽量用 character class少用 alteration
行缩减应用:
"1\n\n2\n3".replace(/^.*$\n?/mg,"m"); //mmmm
2010-09-20 update :
备忘,强大的 jquery 正则式整理报告