正则表达式可以表示简单的模式,当然也可以表达复杂的模式。复杂的模式不仅由字符类和量词组成,也可以由分组、反向引用、前瞻和其他一些强大的正则表达式功能组成。
(一) 分组
分组是通过用一系列括号包围一系列字符、字符类以及量词来使用的。例如,假设想匹配字符串“dogdog”。使用目前获得的知识,可能估计表达式应该类似:
var reDogDog=/dogdog/g;
尽管这是可以的,但有是点儿浪费,如果不知道dogh在字符串中到底出现几次时该怎么办?如果dog重复次数过多呢?你可以使用分组来重写这个表达式,如下:
var reDogDog=/(dog){2}/g;
表达式中的括号的意思是字符序列“dog”将在一行上连续出现两次。但是不并不限制在分组后使用花托号,可以使用任意量词:
var re1=/(dog)?/; //匹配出现零次或一次dog
var re2=/(dog)*/; //匹配出现零次或任意次dog
var re3=/(dog)+/; //匹配出现一次或多次dog
通过混合使用字符、字符类和量词,甚至可以实现一些相当复杂的分组:
var re=/[(bd)ad?]*/; //匹配出现零次或多次”ba”,”da”,”bad”,”dad”
同时也不介意将分组放在分组中间:
var re=/(mom)( and dad)?)/; //匹配”mom”或者”mon and dad”
这个表达式要求”mon”是必要的,但是字符串” and dad”可以出现零次或一次。分组还可以用来弥补一些JavaScript所缺乏的一些语言功能。
(二) 反向引用
反向引用是按照从左到右遇到的左括号字符的顺序进行创建和编号的。例如,表达式(A?(B?(C?)))将产生编号从1~3的三个反向引用:
(1)(A?(B?(C?)))
(2)(B?(C?))
(3)(C?)
反向可以有几种不同的使用方法。
使用正则表达式对象的test()、match()或search()方法后,反向引用的值可以从RegExp构造函数中获得。例如:
var sToMatch=”#123456789”;
var reNumbers=/#(\d+)/;
reNumbers.test(sToMatch);
alert(RegExp.$1); //outpus “123456789”
在test()方法后,所有的反向引用都被保存在RegExp构造函数中,从RegExp.$1(它保存了第一个反向引用)开始,如果还有第二个反向引用,就是RegExp.$2,如果第三个反向引用存在,就是RegExp.$3,依此类推。因为该组匹配了”123456789”,所以RegExp.$1中就存储了这个字符串。还可以直接在定义分组的表达式中包含反向引用。这可以通过使用特殊转义字符序列如\1、\2等等实现。例如:
var sToMatch=”dogdog”;
var reDogDog=/(dog)\1/;
alert(reDogDog.test(sToMatch)); //outpus “true”
正则表达式reDogDog首先创建单词dog的组,然后又被特殊转义序列\1引用,使得这个正则表达式等于/dogdog/。
第三,反向引用可以用在String对象的replace()方法中,这通过使用特殊字符序列$1、
$2等等来实现。描述这种功能的最佳例子是调换字符串中的两个单词的顺序。假设想将字符串”1234 5678”变成”5678 1234”。可以通过下面的代码来实现
var sToMatch=”1234 5678”;
var rematch=/(\d{4}) (\d{4})/;
var sNes=sToMatch.replace(rematch,($2 $1));
alert(sNew); //outpus “5678 1234”;
在这个例子中,正则表达式有两个分组,每一个分组有四个数字。在replace()方法的第二个参数中,$2等同于”5678”,而$1等同于”1234”,对应于它们在表达式中出现的顺序。
(三) 候选
如果要对同一个表达式同时匹配”red”和”black”时要怎么做呢?这些单词完全没有相同的字符,这样就要写两个不同的正则表达式,并分别对两个字符串进行匹配,像这样:
var sToMatch1=”red”;
var sToMatch2=”black”;
var reRed=/red/;
var reBlack=/black/;
alert(reRed.test(sToMatch1) || reBlack.test(sToMatch1)); //outpus “true”;
alert(reRed.test(sToMatch2) || reBlack.test(sToMatch2)); //outpus “true”;
这虽然完成了任务,但是十分冗长。还有另一种方式就是使用正则表达式的候选操作符。
候选操作符和ECMAScript的二进制异或一样,是一个管道符(|),它放在两个单独的柜式之间。正如下面的例子:
var sToMatch2=”black”;
var reRedOrBlack=/(red|black)/;
alert(reRedOrBlack.test(sToMatch1)); //outpus “true”;
alert(reRedOrBlack.test(sToMatch2)); //outpus “true”;
在这里,reRedOrBlack匹配”red”或者”black”,同时测试每个字符串都返回”true”。因为两个备选项存放在一个分组中,不管哪个被匹配了,都会存在RegExp.$1中以备将来使用(同时也可以在表达式中使用\1)。在第一个测试中,RegExp.$1等于”red”,在第二个中,它等于”blue”。
OR模式在实践中以一种通常的用法是从用户输入中删除不合适的单词,这对于在线论坛来说是非常重要的。通过针对这些敏感单词使用OR模式和replace()方法,则可以很方便地在帖子发布之前去掉敏感内容。
var reBaWords=/badword|anotherbadword/gi;
var sUserInput=”This is a string using badword and anotherbadword”;
var sFinalText=sUserInput.replace(reBadWords,’******’);
alert(sFinalText); //outpus “This is a string using ****** and ******”
也可以用星号替换敏感词中每一个字母,也就是说最后出现的文本中星号的数量和敏感词中的字符数量是一样的,使用函数来作为replace()方法的第二个参数,就可以达到这个目的:
var reBaWords=/badword|anotherbadword/gi;
var sUserInput=”This is a string using badword and anotherbadword”;
return sMatch.replace(/./g,”*”);
});
alert(sFinalText);
(四)非捕获性分组
在较长的正则表达式中,存储反向引用会降低匹配速度。通过使用非捕获性分组,仍然可以拥有与匹配字符串序列同样的能力,而无需存储结果的开销。
如果一创建一个非捕获性分组,只要在左括号的后面加上一个问号和一个紧跟的冒号
var sToMatch=”#123456789”;
var reNumbers=/#(?:\d+)/;
reNumbers.test(sToMatch);
alert(RegExp.$1) //outpus “”
这个例子的最后一行代码输出一个空字符,因为该分组是非捕获性的。正因如此,replace()方法就不能通过RegExp.$x变量为使用任何反向引用,或在正则表达式中使用它。看看运行下面的代码会怎样:
alert(sToMatch.replace(renumbers,”abcd$1”)); //outpus “abcd$1”
这段代码输出”abcd$1”而不是”abcd123456789”,因为”$1”在这里并不被看成是一个反向引用,而被直接翻译成字符。
正则表达式又一个十分常用的方式是去掉文本中所有的HTML标签,尤其是在论坛和BBS上,这可以防止游客在他们的发帖中插入恶意或是无意错误的HTML。删除HTML标记的正则表达式很简单,只要用一个简单的表达式:
var retag=/<(?:.|\s)*?>/g;
这个表达式匹配一个小括号(<)后面跟着任何文本,然后跟着一个大于号(>),这有效地匹配了所有的HTML标签,这里使用非捕获性分组是因为在小于号和大于号之间出现的内容并不重要(这些都是要删除的)。
String.prototype.stripHTML=function(){
var retag=/<(?:.|\s)*?>/g;
return this.replace(retag,””);
}
var sTest=”<b>This would be bold</b>”;
alert(sTest.stripHTML()); //outpus “This would be bold”;
(五)前瞻
有时候,可能希望,当某个特定的字符分组出现在另一个字符串之前时,才去捕获它。
前瞻就是告诉正则表达式运算器向前看一些字符而不移动其位置。有正向前瞻和负向前瞻。正向前瞻检查的是接下来的出现的是不是某个特定字符集。而负向前瞻则是检查接下来的不应该出现的特定字符集。
创建正向前瞻是要将模式放在(?=和)之间。注意这不是分组,虽然它也用到括号。事实上,分组不会考虑前瞻的存在(无论是正向的还是负向的)。
var sToMatch1=”bedroom”;
var sToMatch2=”bedding”;
var reBed=/bed(?=room))/;
alert(reRed.test(sToMatch1)); //outpus “true”
alert(RegExp.$1) //outpus “bed”
alert(redBed.test(sToMatch2)) //outpus “false”
在这个例子中,reBed只匹配后面跟着”room”的”bed”。因此,它能匹配sToMatch1而不能匹配sToMatch2。在用表达式测试sToMatch1后,这段代码输出RegExp.$1的内容,是”bed”,而不是”bedroom”。模式的”room”的部分是包含在前瞻中的,所以没有作为分组的一部分返回。
创建负向前瞻是要将模式放在(?!和)之间。
var sToMatch2=”bedding”;
alert(reRed.test(sToMatch1)); //outpus “false”
alert(redBed.test(sToMatch2)) //outpus “true”
这里,表达式变成只匹配后面不跟着”room”的”bed”,所以模式匹配”bedding”而不是”bedroom”。在测试sToMatch2,RegExp.$1还是包含”bed”,而不是”bedding”。
尽管JavaScript支持正则表达式前瞻,但它不支持后瞻。后瞻可以匹配这种模式:“匹配b当且仅汉它前面没有a“。
(六)边界
边界用于正则表达式中表示模式的位置。^(行开头)、$(行结尾)、\b(单词的边界)、\B(非单词的边界)。
假设想查找一个单词,但要它只出现在行尾,则可以使用美元符号($)表示它:
var sToMatch=”Important word is the last one.”;
var reLastWord=/(\w+)\.$/;
reLastWord.test(sToMatch);
alert(RegExp.$1); //outpus “one”
例子中的正则表达式查找的是,在一行结束之前出现的跟着句号的一个或多个单词字符。当用这个表达式测试sToMatch时,它返回“one“。还可以很容易地获取一行的第一个单词,只要使用脱字符(^)。
var reLastWord=/^(\w+)/;
alert(RegExp.$1); //outpus “Important”
在这个例子中,正则表达式查找行起始位置后一个或多个单词字符。如果遇到非单词字符,匹配停止。返回“Important“。这个例子也可以用单词边界实现:
var sToMatch=”Important word is the last one.”;
var reLastWord=/^(.+?)\b/;
这里,正则表达式用惰性来制定在单词边界之前可以出现任何字符,且可出现一次或多次(如果使用贪婪性量词,表达式就匹配整个字符串)。
使用单词边界可以方便地从字符串中抽取单词:
var sToMatch=”First second third fourth fifth sixth”;
var rewords=/\b(\S+?)\b/g;
var arrWords=sToMatch.match(rewords);
正则表达式reWords使用单词边界(\b)和非空白字符类(\S)从句子中抽取单词。执行后,arrWords数组将会包含”First”,”second”,”third”,”fourth”,”fifth”,”sisth”。注意行的开始和行的结束,通常由^和$表示的位置,对应地认为是单词的边界。
(七)多行模式
要指定多选模式,只要在正则表达式后面添加一个m选项。这会让$边界匹配换行符(\n)以及字符串真正的结尾。例如:
var sToMatch=”First second\nthird fourch\nfifth sixth”;
var reLastWordLine=/(\w+)$/gm
var arrWords=sToMatch.match(reLastWordOnLine);
多行模式同样会改变^边界的行为,这时它会匹配换行之后的位置。例如,前面的例子中,要从字符串获取字符”First”,”third”,”fifth”,则可以这么做:
var sToMatch=”First second\nthird fourch\nfifth sixth”;
var reLastWordLine=/^(\w+)/gm
var arrWords=sToMatch.match(reLastWordOnLine);
若不指定多行模式,表达式将只返回”First”。
(一) 分组
分组是通过用一系列括号包围一系列字符、字符类以及量词来使用的。例如,假设想匹配字符串“dogdog”。使用目前获得的知识,可能估计表达式应该类似:
var reDogDog=/dogdog/g;
尽管这是可以的,但有是点儿浪费,如果不知道dogh在字符串中到底出现几次时该怎么办?如果dog重复次数过多呢?你可以使用分组来重写这个表达式,如下:
var reDogDog=/(dog){2}/g;
表达式中的括号的意思是字符序列“dog”将在一行上连续出现两次。但是不并不限制在分组后使用花托号,可以使用任意量词:
var re1=/(dog)?/; //匹配出现零次或一次dog
var re2=/(dog)*/; //匹配出现零次或任意次dog
var re3=/(dog)+/; //匹配出现一次或多次dog
通过混合使用字符、字符类和量词,甚至可以实现一些相当复杂的分组:
var re=/[(bd)ad?]*/; //匹配出现零次或多次”ba”,”da”,”bad”,”dad”
同时也不介意将分组放在分组中间:
var re=/(mom)( and dad)?)/; //匹配”mom”或者”mon and dad”
这个表达式要求”mon”是必要的,但是字符串” and dad”可以出现零次或一次。分组还可以用来弥补一些JavaScript所缺乏的一些语言功能。
(二) 反向引用
反向引用是按照从左到右遇到的左括号字符的顺序进行创建和编号的。例如,表达式(A?(B?(C?)))将产生编号从1~3的三个反向引用:
(1)(A?(B?(C?)))
(2)(B?(C?))
(3)(C?)
反向可以有几种不同的使用方法。
使用正则表达式对象的test()、match()或search()方法后,反向引用的值可以从RegExp构造函数中获得。例如:
var sToMatch=”#123456789”;
var reNumbers=/#(\d+)/;
reNumbers.test(sToMatch);
alert(RegExp.$1); //outpus “123456789”
在test()方法后,所有的反向引用都被保存在RegExp构造函数中,从RegExp.$1(它保存了第一个反向引用)开始,如果还有第二个反向引用,就是RegExp.$2,如果第三个反向引用存在,就是RegExp.$3,依此类推。因为该组匹配了”123456789”,所以RegExp.$1中就存储了这个字符串。还可以直接在定义分组的表达式中包含反向引用。这可以通过使用特殊转义字符序列如\1、\2等等实现。例如:
var sToMatch=”dogdog”;
var reDogDog=/(dog)\1/;
alert(reDogDog.test(sToMatch)); //outpus “true”
正则表达式reDogDog首先创建单词dog的组,然后又被特殊转义序列\1引用,使得这个正则表达式等于/dogdog/。
第三,反向引用可以用在String对象的replace()方法中,这通过使用特殊字符序列$1、
$2等等来实现。描述这种功能的最佳例子是调换字符串中的两个单词的顺序。假设想将字符串”1234 5678”变成”5678 1234”。可以通过下面的代码来实现
var sToMatch=”1234 5678”;
var rematch=/(\d{4}) (\d{4})/;
var sNes=sToMatch.replace(rematch,($2 $1));
alert(sNew); //outpus “5678 1234”;
在这个例子中,正则表达式有两个分组,每一个分组有四个数字。在replace()方法的第二个参数中,$2等同于”5678”,而$1等同于”1234”,对应于它们在表达式中出现的顺序。
(三) 候选
如果要对同一个表达式同时匹配”red”和”black”时要怎么做呢?这些单词完全没有相同的字符,这样就要写两个不同的正则表达式,并分别对两个字符串进行匹配,像这样:
var sToMatch1=”red”;
var sToMatch2=”black”;
var reRed=/red/;
var reBlack=/black/;
alert(reRed.test(sToMatch1) || reBlack.test(sToMatch1)); //outpus “true”;
alert(reRed.test(sToMatch2) || reBlack.test(sToMatch2)); //outpus “true”;
这虽然完成了任务,但是十分冗长。还有另一种方式就是使用正则表达式的候选操作符。
候选操作符和ECMAScript的二进制异或一样,是一个管道符(|),它放在两个单独的柜式之间。正如下面的例子:
var sToMatch2=”black”;
var reRedOrBlack=/(red|black)/;
alert(reRedOrBlack.test(sToMatch1)); //outpus “true”;
alert(reRedOrBlack.test(sToMatch2)); //outpus “true”;
在这里,reRedOrBlack匹配”red”或者”black”,同时测试每个字符串都返回”true”。因为两个备选项存放在一个分组中,不管哪个被匹配了,都会存在RegExp.$1中以备将来使用(同时也可以在表达式中使用\1)。在第一个测试中,RegExp.$1等于”red”,在第二个中,它等于”blue”。
OR模式在实践中以一种通常的用法是从用户输入中删除不合适的单词,这对于在线论坛来说是非常重要的。通过针对这些敏感单词使用OR模式和replace()方法,则可以很方便地在帖子发布之前去掉敏感内容。
var reBaWords=/badword|anotherbadword/gi;
var sUserInput=”This is a string using badword and anotherbadword”;
var sFinalText=sUserInput.replace(reBadWords,’******’);
alert(sFinalText); //outpus “This is a string using ****** and ******”
也可以用星号替换敏感词中每一个字母,也就是说最后出现的文本中星号的数量和敏感词中的字符数量是一样的,使用函数来作为replace()方法的第二个参数,就可以达到这个目的:
var reBaWords=/badword|anotherbadword/gi;
var sUserInput=”This is a string using badword and anotherbadword”;
return sMatch.replace(/./g,”*”);
});
alert(sFinalText);
(四)非捕获性分组
在较长的正则表达式中,存储反向引用会降低匹配速度。通过使用非捕获性分组,仍然可以拥有与匹配字符串序列同样的能力,而无需存储结果的开销。
如果一创建一个非捕获性分组,只要在左括号的后面加上一个问号和一个紧跟的冒号
var sToMatch=”#123456789”;
var reNumbers=/#(?:\d+)/;
reNumbers.test(sToMatch);
alert(RegExp.$1) //outpus “”
这个例子的最后一行代码输出一个空字符,因为该分组是非捕获性的。正因如此,replace()方法就不能通过RegExp.$x变量为使用任何反向引用,或在正则表达式中使用它。看看运行下面的代码会怎样:
alert(sToMatch.replace(renumbers,”abcd$1”)); //outpus “abcd$1”
这段代码输出”abcd$1”而不是”abcd123456789”,因为”$1”在这里并不被看成是一个反向引用,而被直接翻译成字符。
正则表达式又一个十分常用的方式是去掉文本中所有的HTML标签,尤其是在论坛和BBS上,这可以防止游客在他们的发帖中插入恶意或是无意错误的HTML。删除HTML标记的正则表达式很简单,只要用一个简单的表达式:
var retag=/<(?:.|\s)*?>/g;
这个表达式匹配一个小括号(<)后面跟着任何文本,然后跟着一个大于号(>),这有效地匹配了所有的HTML标签,这里使用非捕获性分组是因为在小于号和大于号之间出现的内容并不重要(这些都是要删除的)。
String.prototype.stripHTML=function(){
var retag=/<(?:.|\s)*?>/g;
return this.replace(retag,””);
}
var sTest=”<b>This would be bold</b>”;
alert(sTest.stripHTML()); //outpus “This would be bold”;
(五)前瞻
有时候,可能希望,当某个特定的字符分组出现在另一个字符串之前时,才去捕获它。
前瞻就是告诉正则表达式运算器向前看一些字符而不移动其位置。有正向前瞻和负向前瞻。正向前瞻检查的是接下来的出现的是不是某个特定字符集。而负向前瞻则是检查接下来的不应该出现的特定字符集。
创建正向前瞻是要将模式放在(?=和)之间。注意这不是分组,虽然它也用到括号。事实上,分组不会考虑前瞻的存在(无论是正向的还是负向的)。
var sToMatch1=”bedroom”;
var sToMatch2=”bedding”;
var reBed=/bed(?=room))/;
alert(reRed.test(sToMatch1)); //outpus “true”
alert(RegExp.$1) //outpus “bed”
alert(redBed.test(sToMatch2)) //outpus “false”
在这个例子中,reBed只匹配后面跟着”room”的”bed”。因此,它能匹配sToMatch1而不能匹配sToMatch2。在用表达式测试sToMatch1后,这段代码输出RegExp.$1的内容,是”bed”,而不是”bedroom”。模式的”room”的部分是包含在前瞻中的,所以没有作为分组的一部分返回。
创建负向前瞻是要将模式放在(?!和)之间。
var sToMatch2=”bedding”;
alert(reRed.test(sToMatch1)); //outpus “false”
alert(redBed.test(sToMatch2)) //outpus “true”
这里,表达式变成只匹配后面不跟着”room”的”bed”,所以模式匹配”bedding”而不是”bedroom”。在测试sToMatch2,RegExp.$1还是包含”bed”,而不是”bedding”。
尽管JavaScript支持正则表达式前瞻,但它不支持后瞻。后瞻可以匹配这种模式:“匹配b当且仅汉它前面没有a“。
(六)边界
边界用于正则表达式中表示模式的位置。^(行开头)、$(行结尾)、\b(单词的边界)、\B(非单词的边界)。
假设想查找一个单词,但要它只出现在行尾,则可以使用美元符号($)表示它:
var sToMatch=”Important word is the last one.”;
var reLastWord=/(\w+)\.$/;
reLastWord.test(sToMatch);
alert(RegExp.$1); //outpus “one”
例子中的正则表达式查找的是,在一行结束之前出现的跟着句号的一个或多个单词字符。当用这个表达式测试sToMatch时,它返回“one“。还可以很容易地获取一行的第一个单词,只要使用脱字符(^)。
var reLastWord=/^(\w+)/;
alert(RegExp.$1); //outpus “Important”
在这个例子中,正则表达式查找行起始位置后一个或多个单词字符。如果遇到非单词字符,匹配停止。返回“Important“。这个例子也可以用单词边界实现:
var sToMatch=”Important word is the last one.”;
var reLastWord=/^(.+?)\b/;
这里,正则表达式用惰性来制定在单词边界之前可以出现任何字符,且可出现一次或多次(如果使用贪婪性量词,表达式就匹配整个字符串)。
使用单词边界可以方便地从字符串中抽取单词:
var sToMatch=”First second third fourth fifth sixth”;
var rewords=/\b(\S+?)\b/g;
var arrWords=sToMatch.match(rewords);
正则表达式reWords使用单词边界(\b)和非空白字符类(\S)从句子中抽取单词。执行后,arrWords数组将会包含”First”,”second”,”third”,”fourth”,”fifth”,”sisth”。注意行的开始和行的结束,通常由^和$表示的位置,对应地认为是单词的边界。
(七)多行模式
要指定多选模式,只要在正则表达式后面添加一个m选项。这会让$边界匹配换行符(\n)以及字符串真正的结尾。例如:
var sToMatch=”First second\nthird fourch\nfifth sixth”;
var reLastWordLine=/(\w+)$/gm
var arrWords=sToMatch.match(reLastWordOnLine);
多行模式同样会改变^边界的行为,这时它会匹配换行之后的位置。例如,前面的例子中,要从字符串获取字符”First”,”third”,”fifth”,则可以这么做:
var sToMatch=”First second\nthird fourch\nfifth sixth”;
var reLastWordLine=/^(\w+)/gm
var arrWords=sToMatch.match(reLastWordOnLine);
若不指定多行模式,表达式将只返回”First”。