一、概述
正则表达式是一个对象,用来描述字符串的模式。JavaScript用RegExp类表示正则表达式,String和RegExp两个类都定义了一些方法来使用正则表达式,完成一些基于文本的模式匹配、查找与替换等强大功能。JavaScript的正则表达式语法完全是Perl5正则表达式语法的一个子集。本文先介绍正则表达式的语法,然后介绍String和RegExp类使用正则表达式的一些方法。
二、定义正则表达式
在JavaScript中,正则表达式是用RegExp类表示的,所以我们当然可以使用RegExp的构造函数来定义正则表达式对象;但是就像字符串一样,我们也可以使用字面量来定义正则表达式,只不过字符串是使用一对引号包含一串字符,而正则表达式是使用一对斜杠包含("/")一串字符。
例子:使用RegExp:var pattern=new RegExp("s$");
上面的例子等价于:var pattern = /s$/; 他们都匹配一个s字符结尾的字符串。
1、字符字面量
(1)正则表达式中的字母和数字字符,都匹配他们本身。
(2)正则表达式使用反斜杠加一个特殊字符,可以表示非字母和数字的字符。有:
(换行),v (vertical tab), (nul字符),f(form feed), (Tab), (回车), uxxxx(4位16进制数字xxxx表示的unicode字符), xnn(2位16进制数字nn表示的拉丁字符),cX(X表示的控制字符)
(3)标点符号:^ $ . * ! + ? = : | / ( ) [ ] { }18个字符在正则表达式中有特殊含义,如果要匹配这些字符本身,需要在前面加个反斜杠。不过其他的标点比如@符号,引号等不需要加反斜杠就表示他们本身。
如果在匹配的时候不记得哪些需要加反斜杠,哪些不需要,一个安全的做法就是在所有需要匹配标点符号本身的情况下,都在标点符号前加上反斜杠。
2、字符组
多个单个字符合并一起放到一个中括号中,表示一个字符组,字符组匹配在字符组中的任意一个字符。如:/[abc]/ 匹配a,b,c中的任意一个。
在字符组的左括号后加上一个插入符表示反字符组,反字符组匹配不在字符组中的任意一个字符,如:/[^abc]/匹配除了a,b,c的任意字符。
字符组中可以使用连字符表示一个字符范围,如:/[a-zA-Z0-9/匹配26个大小写字母和0到9的数字中的任何一个。
由于字符组在正则表达式中经常使用,所以JavaScript在正则表达式的语法中定义了一些特殊的转义字符表示这些字符组。对字符组归纳一下:
[...] --匹配括号中任意单个字符
[^...] --匹配非括号中的任意单个字符,即上面的反字符组
. --单个句点匹配除了换行以及其他Unicode的行结束符外的任意字符
/w --匹配任意Ascii字符,等价于[a-zA-Z0-9_]
/W --匹配任意非Ascii字符,等价于[^a-zA-Z0-9_],即上面的反字符组
/s --匹配任意Unicode空字符
/S --匹配任意非Unicode空字符,即上面的反字符组
/d --匹配任意数字,等价于[0-9]
/D --匹配非数字,等价于[^0-9],即上面的反字符组
[] --匹配单个退格字符,这个特殊情况是因为在正则表达式中有其他的意义。
3、重复
重复表示上一个模式可以重复的次数,重复的语法有:
{n,m} --上一个模式至少出现n次,但是不超过m次
{n,} --上一个模式至少出现n次,没有上限
{n} --上一个模式只能出现n次
? --上一个模式出现0次或者1次,等价于{0,1}
+ --上一个模式至少出现1次,等价于{1,}
* --上一个模式出现0次或者多次,等价于{0,}
(1)非贪婪重复
上面的重复语法表示的都是贪婪重复,贪婪重复表示在匹配过程中,在模式的其他部分仍然可以匹配的情况下,尽可能多地去匹配。
我们可以在重复语法的后面加个问号,表示非贪婪匹配,非贪婪匹配是只要匹配到了,就不继续往下匹配了。
比如有字符串”aaa“,用/a+/可以匹配到整个字符串”aaa“,而用/a+?/仅仅匹配了字符串的第一个字符”a“
使用非贪婪重复,不一定能产生期望的效果,比如字符串"aaab",用/a+b/可以匹配整个字符串,如果你使用/a+?b/,可能是想匹配字符串的后两位,但是实际上该匹配返回的还是整个字符串。
4、可选、组合、引用
| --用来分割可选的匹配模式,如/ab|cd|ef/ 匹配ab,或者cd,或者ef,有多个可选方案时,采用从左到右的匹配顺序,找到匹配结果就停止,即使后面有更优的匹配。比如/a|ab/ 匹配字符串ab的时候,结果为a。
() --圆括号表示一个组合,组合里的模式当做一个整体看待,比如/java(script)?/,匹配java或者javascript。
正则表达式中的圆括号的另一个用途就是,用作子模式。比如/[a-z]+d+/,匹配小写字母开头,数字结尾的字符串。如果你只关心每个匹配中结尾的数字,可以使用/[a-z]+(d+)/。
有了子模式,我们还可以通过一个反斜杠加一个数字对子模式进行引用,数字是子模式在整个模式中的索引(第几个左括号)。不过引用的不是子模式本身,而是子模式的匹配结果。
比如:/['"][^'"]['"]/匹配的是用引号引起来的字符串,但是左右的单双引号可能不对称。如果使用子模式就可以解决这个问题:/(['"])[^'"]*1/,其中1表示对第一个子模式匹配结果的引用。
注意,对子模式的引用,不能放到字符组里去,比如不能这样:/(['"])[^1]*1/。
这种子模式及其引用,使用JavaScript有了强大的能力对字符串进行查找和替换。
另外如果不想对子模式产生一个引用的索引,可以把圆括号变成(?:...),即在左括号后面加上?:,如/([jJ]ava(?:[sS]cript)?)siss(funw*)/中,2表示的是(funw*)匹配的结果
总结一下:
| --可选项,表示匹配左边或者右边
(...) --组合,把括号中的字符组合成一个单独的单元,可以和? * + | 等一起使用。组合匹配的结果可以被记住,并在之后进行引用。
(?:...) --单纯的组合,不能记住匹配的结果用来对其进行引用。
--第n个匹配子模式的引用,n是通过从左到右数左括号的个数确定的,不过(?:除外。
5、定义匹配的位置(锚点)
正则表达式中的很多元素都表示匹配一个实际的字符,有一些特别的元素表示匹配字符之间的位置。比如匹配的是单词之间的边界,或者单词与字符串首尾的边界。
像这种元素,没有确定在匹配结果中的任何字符,然而他们确定了一个匹配结果应该发生的合法位置,我们把这样的元素称为正则表达式的锚点(regular-expression anchors)。
正则表达式锚点总结
^ --字符串的起始位置,在多行查找中,匹配每一行的开始
$ --字符串的结束位置,在多行查找中,匹配每一行的结束
--匹配单词的边界,即:w与W之间的位置,或者w与字符串起始与结尾之间的位置。注意:要匹配一个退格符,使用[]
B --匹配非单词的边界,与正好相反。
(?=p) --匹配一个位置,该位置上接下来的字符符合模式p,匹配结果中不包含p匹配的结果。如/Java(?=:)/匹配"Java: program language"中的Java,但是不匹配"Java program language"
(?!p) --匹配一个位置,该位置上接下来的字符必须不符合模式p,匹配结果也不含p匹配的结果,与上面刚好相反。比如/Java(?!Script)([A-Z]w*)/匹配Java开头,后面跟一个大写字母以及任意个其他单词,但是Java后面不能跟Script。所以这个正则表达式匹配:JavaScrpt,JavaBean,但是不匹配Javabean,JavaScript,JavaScripter
6、标识
正则表达式最后一个语法元素是标识,标识定义个更高级别的匹配规则,标识没有定义在双斜杠之间,而是定义在第二个斜杠后面。JavaScript支持3个标识:
i --表示匹配是非大小写敏感的
g --表示匹配是全局的,在查找字符串中,所有的匹配项都必须找到
m --表示多行查找匹配模式,该模式下,^ $除了匹配整个查找字符串的首位外,还匹配每一行的首位位置。
三、与模式匹配相关的String方法
String对象支持4个方法使用正则表达式,search
1、search:使用一个正则表达式参数,返回第一个匹配的位置,没有匹配返回-1,如:"JavaScript".search(/script/i)返回4,如果参数不是正则表达式,使用RegExp构造函数把它转成正则表达式,search不支持全局标识g,如果包含的话,会被忽略掉。
2、replace:使用两个参数,第一个为正则表达式,第二个为替换的字符串。如果第一个参数是字符串,该函数不会像search一样把它先转成正则表达式,而是直接搜索该字符串。该函数支持全局标识g,如果使用g,则会把所有匹配的结果用第二个参数替换,否则只替换第一个匹配结果。如:text.replace(/javacript/ig,"JavaScript"),把text中的所有javascript替换成正确的大小写形式。
如果第二个参数中包含$加一个数字的话,则替换的字符串会用匹配的结果替换掉这两个字符,然后再作为新的替换字符串去替换原来的字符串。比如包英文的引号换成中文的引号:text.replace(/"([^"]*)"/g,'”$1“');
3、match:使用一个正则表达式参数,不是正则表达式,先转为正则表达式。返回一个字符串数组。如果正则表达式是全局模式,返回的是所以匹配结果的数组。如果正则表达式是非全局模式的,返回的也是一个数组,数组第一个元素为匹配结果,其他元素是正则表达式中子模式的匹配结果。
4、split:使用一个正则表达式参数,匹配结果作为分隔符,把字符串分隔成字符串数组,如果参数是字符串而不是正则表达式,则直接用字符串作为分隔符。如:"123,456,234".split(",")返回["123","456","234"]
"1, 3, 4 , 5".split(/s*,s*/)返回["1","3","4","5"]
四、RegExp对象
RegExp提供了一个构造函数,3个方法,以及5个属性来定义和使用正则表达式对象。
1、构造函数:构造函数使用1个或者2个字符串参数,第一个参数定义表达式实体,第二个参数定义表达式标识,即i,g,m及其组合。如:var zipcode=new RegExp("\d{5}","g");
2、5个属性:
source --只读属性,包含正则表达式文本的字符串
global --只读属性,是否全局模式,true/false
ignoreCase --只读属性,是否忽略大小写模式,true/false
multiline --只读属性,是否多行模式,true/false
lastIndex --读写属性,整型,对于g模式下,存储下一次搜索的起始位置,方法test()和exec()会使用到。
3、2个方法
exec(),一个字符串参数,如果没有匹配,返回null,如果有匹配,返回匹配的字符串数组结果,类似String方法match的数组结果,数组第一个元素表示匹配结果,接下来的元素表示每一个子模式的匹配结果。在返回的结果数组对象上,新增了2个属性:index和input,index表示匹配在字符串中发生的位置,input表示查找字符串。如果正则表达式是全局模式,那么执行后会修改正则表达式对象上的lastIndex属性,表示下次搜索的起始位置。非全局模型下,lastIndex始终为0.
如: var pattern=/Java/g;
var text="JavaScript is more fun than Java!";
var result;
while((result=pattern.exec(text))!=null){
alert(result[0] + result.index + pattern.lastIndex);
}
test(),一个字符串参数,如果匹配成功,返回true,否则,返回false,该方法和exec()类似,只是返回值不同。全局模式下,执行后也会修改正则表达式的lastIndex属性,可以在一个字符串上多次调用。