javascript中正则的使用详解
来源:互联网 发布:悉尼科技大学 知乎 编辑:程序博客网 时间:2024/06/06 13:17
最近一直在啃Mastering Regular Expressions,3rd Edition(Jeffrey E.F.Friedl),相信很多朋友早已读过她了。非常遗憾,此书中虽然非常详尽的描述了很多主流编程语言如java,.net,perl,php等对于正则的支持及使用方法,但是,对于javascript却只字未提。或许是因为相对于其他语言,javascript对正则的功能支持不算丰富(比如:javascript不支持否定环视,不支持固化分组,不支持x修饰符……),以至于在javascript中使用正则要容易不少,所以导致作者没有将javascript的部分写进书中。话虽如此,要想把javascript的正则使用方法搞个透彻并非易事。趁元旦假期小弟特意整理了些资料,既是对自己近期学习的总结,也是希望给那些正好需要此类帮助的朋友们的一点参考吧。
这偏资料按使用对象的不同分类,最后加一篇总结
正则表达式对象
String对象
RegExp对象
总结
正则表达式对象
正则表达式对象只是一个正则表达式的描述,有两种方法实例化(1-1):
引用官方说明:
关于这个属性还有个非常有意思的地方,就是说它是可写的。也就是说我们可以任意改变它的值,而不是完全依赖匹配的结果(这个和后面介绍的RegExp对象的lastIndex有本质的区别),对上一个例子进行修改:(1-3)
结果表明:在第1次匹配后lastIndex原本为4,但我们将它改为6,这样第2次匹配就是从第2个book的b后面开始,所以第2次就匹配失败了,提前回到了位置0。(使用这个小技巧更有利于我们控制匹配流程)
对于lastIndex还要注意一点,如果只是匹配一个位置,lastIndex会被驱动机制强行向后移动一位(避免产生无限循环):(1-4)
如这个例子,匹配的是字符串开始位置,但lastIndex被强行移到了第1个a后面(看起来像匹配了第1个a)。
source
正则表达式对象的表达式文本,只读。此属性可以用于对外部传入的正则表达式的检测。(1-5)
正则表达式对象的方法:
test
指出在被查找的字符串中是否可以匹配至少1次,返回bool值。这是个比较常用的方法,用来仅检测是否匹配,而忽略具体的匹配结果。有一点需要特别注意:(1-6)
第2个打印的结果可能会不同于你期望的结果,原因在前文中已经提及过(1-2)。因为是g模式,正则表达式对象会记录下前一次匹配的结束位置,作为下次匹配的开始位置,即使被匹配的字符串是另一个。这里只要每次打印出lastIndex就能了解到每次的匹配起始位置:
第1次匹配后,lastIndex为9。第2次匹配时,即使字符串不是原先那个str1,但原先的lastIndex的值并没有改变,直接作为这次匹配的起始位置,也就是在u的前面,这样第2次当然就匹配失败了。
exec
用正则表达式在字符串中运行查找,并返回包含该查找结果的一个数组,如果匹配失败就返回null。
如果没有使用g模式,返回数组的0元素包含了完整的匹配,而第1到n元素中包含的是匹配中出现的一个子匹配(按()顺序排列):(1-7)
打印出的结果:
box 存在arr[0]中,是b/w/w的完整匹配
o 存在arr[1]中,是第1个/w的子匹配
x 存在arr[2]中,是第2个/w的子匹配
还不光如此,返回的数组arr还包含了3个属性:
input 被匹配的完整字符串(区别于被匹配到的字符串内容arr[0])
index 被匹配的字符串的起始位置
lastIndex 被匹配的字符串的结束位置(区别于正则表达式对象的lastIndex,虽然当前情况下值相同)
这个例子可以看到arr和re都有自身的lastIndex属性,千万不要忽略这点:(1-8)
第2种情况就是使用g模式,每次匹配都是从上次匹配的结束位置开始,从而可以寻找出字符串中的所有匹配。虽然功能更进了一步,但使用起来需更加小心:(1-9)
从上面这个例子可以看到,正则表达式的lastIndex可是控制匹配的进程,而返回的数组的lastIndex只是一个单纯存放信息的单元。
compile
把正则表达式编译为内部格式,从而执行得更快。关于这个方法我试了很多次,但发现是否把正则表达式预先编译速度好象都一样,暂时就先不给出说明了,等有好的例子再来补上。
String对象
字符串对象大家都很熟悉了,我这里只分析一下会用到正则表达式的4个方法。这些方法同样也会改变正则表达式对象的lastIndex属性。
search
返回第1次成功匹配的位置,如果匹配失败返回-1。
初看之下,这个方法和正则表达式对象的test方法差不多,只要一次成功的匹配就直接返回结果(只不过一个返回位置,一个返回bool),都不存放具体的匹配内容。但请看这个例子:(2-1)
第1次结果 result为4 ,匹配了12 ,正则表达式对象的lastIndex被改为6 ,这步没有问题。
第2次结果 result同样为4 ,正则表达式对象的lastIndex还是为6 。
第3次结果 result还是4 ,正则表达式对象的lastIndex却变成了8 。
第2次匹配的结果就展现了两种方法本质区别:虽然使用的是g模式,而且正则表达式对象的lastIndex也被改变了,但后续的search匹配并没有像test那样继lastIndex后面开始匹配(第3次匹配),而是直接从0开始。也就是说正则表达式对象的lastIndex对string的方法并没有任何影响,只对自身的方法起作用。
split
将一个字符串分割为子字符串,然后将结果作为字符串数组返回。这个方法使用起来比较简单但很实用:(2-2)
可见split方法也不受正则表达式的lastIndex的影响。
match
使用正则表达式模式对字符串执行查找,并将包含查找的结果作为数组返回,没有匹配则返回null。
这个方法的作用与正则表达式对像的exec相同,但使用起来有些区别,同样根据是否使用g模式分两中情况:
不使用g模式情况下,结果与不使用g模式的正则表达式对像的exec相同数组的0元素包含整个匹配,而第1到n元素包含了匹配中曾出现过的子匹配。
同样,返回的数组也包含了3个属性:
input 被匹配的完整字符串
index 被匹配的字符串的起始位置
lastIndex 被匹配的字符串的结束位置
如果前面的都理解了的话,这个例子就很清楚了:(2-3) 如果是g模式的话,情况就和g模式的正则表达式对象的exec结果有区别了。元素0到n中包含所有完整匹配(表达式中的子匹配就没意义了),请仔细看这个例子:(2-4)
可以惊奇的发现,match只执行了一次,但已经提取了所有的完整匹配(区别于正则表达式对象的exec方法需要外部使用循环方法依次获取)。有所得必有所失,用这种方法无法获取子匹配!还有一点需要说明,这种情况下的匹配开始位置是由系统内部决定的,我们无权干涉。
replace
返回根据正则表达式进行文字替换后的字符串的复制,如果匹配失败按原样返回字符串。这是个相当复杂的方法,但功能却很强大。让我们分别看看各种使用方法,且每种方法中是否使用g模式的结果都不同:
入门级的用法,使用固定的字符串替换掉被成功匹配的部分:(2-5)
这个例子中,非g模式下,每次只匹配一个且没有位置的继承效果。g模式下一次性替换掉所有符合的片段且有位置的继承效果,也就是说每次后续匹配都是从前一次结束位置开始(而不是将上次匹配后的结果字符串作为新的匹配样本),但这个位置我们同样无权控制:(2-6)
这个例子中,第1次匹配后结果为theeeeeeee(也就是少了一个e),如果后续匹配都是按照上一次结果为样本,那将再次匹配下去,最后结果为th 。很显然真实的结果表明并不是如此,而是在第1次匹配后的结束位置 3 开始第2次匹配,这样就直接匹配失败退出,re.lastIndex被定格在了3 。
然后就是中级用法,将本轮被匹配的子片段或相关片段直接运用到替换字符串中。这个我特别强调了“本轮”,就是说每次匹配的结果都可能不同,这直接影响大了替换字符串的内容。
调用被匹配的子片段或相关片段的替换符(必须直接写在""中,如果没有相应的匹配结果将直接作为字符显示):
$& 完整的被匹配的片段
$` 在被匹配的字符串中 $& 之前的片段
$' 在被匹配的字符串中 $& 之后的片段
$n/$nn 被匹配的第 n/nn 个片段(从1开始)
$$ $字符本身
下面的例子详细的说明了这些用法:(2-7)
怎么样,有了这些替换符就可以在匹配的过程中根据匹配的实际情况动态的生成替换字符串了,这个用处是相当大的!
最后讲解一下高级用法,每轮都使用回调函数来生成一个替换字符串,相比刚才的功能还停留在字符串的拼装层面上,现在更是将正则的功能发挥了淋漓尽致。
先说明一下回调函数的语法:(2-8)
官方语法我当时看的一头雾水,原因在与如果和刚才的替换符部分一起看的话,会产生疑问:$0是什么?因为替换符中并没有这个。而且这里的$1,$2和替换符号并不是一个概念!再看看我改写的这个语法结构,很清楚了吧?这个回调函数的参数名和它们对应的内容没有直接的联系,而是与它们的排列位置有关。也就是说其面说过的替换符$1无论在字符串的什么位置,它的内容总是第1个被匹配的子片段,而且不能用其他名字。而回调函数中的参数$1它只有在第2个参数的时候才表示第1个被匹配的子片段,如果把它和$0的位置交换,那$0就表示第1个被匹配的子片段了,可见任何第2个位置的参数都具有这个含义。所以我可以任意改变所有参数的名字,只要明确它们的位置。
理解了上面的说明后,我们来看一下具体每个参数是什么意思:
第1个参数(_whole) 本轮正则完整匹配的字符片段
第2-n个参数(_sub1 - _sub(n-1))本轮正则匹配的第(n-1)个子片段
第n+1个参数(_index) 本轮被成功匹配的字符片段的坐标
第n+2个参数(_input) 被匹配的原始字符串
可见回调函数的参数个数是不定的。还有一点要说明,所有回调函数内部要使用到的参数都必须定义在参数列表中正确的位置,而没有用到的参数不必定义。但是如果要用到后面的参数,前面的都必须被定义。
说了这么多,让我们来看一下一个官方的例子:(2-9)
怎么样,非常的强大吧!但要能熟练的使用这些高级的方法还是需要用心的去体会和长时间的练习的。
RegExp对象
RegExp对象是存放正则匹配结果信息的全局对象,不能实例化,只有属性(静态属性,都为只读),没有方法。不要认为下面的代码是RegExp对象的实例化(3-1):
随着正则表达式对象或String对象使用正则的相关方法,除了正则表达式对象的实例会记录下结果信息外,RegExp对象也会记录这些信息,只不过因为是全局对象,每次新的成功的匹配所产生的信息值都会刷新RegExp对象的相应属性值(因为每个信息的存放点都是唯一的)。这点很重要,理解了这点就能理解下面的这个例子(3-2):
为了方便分析,我将每次正则匹配都分开来:
第1次结果 0
第2次结果 2
第3次结果 2
第4次结果 0
第5次结果 2
可以看到第3次由于匹配失败,所以RegExp对象的index值没有被更新。所以验证了:只有在成功匹配的前提下,RegExp对象的属性值才会被改变!
RegExp对象每个属性都是被正则表达式对象或String对象所改变的,下面具体分析一下RegExp对象每个属性的含义及用法:
index
被匹配的字符串的起始位置,在还没有进行一次匹配的情况下值为-1,只读。
lastIndex
被匹配的字符串的的结束位置,在还没有进行一次匹配的情况下值为-1,只读。
这两个属性都是字符串中的位置坐标,最前端为0。这个被位置坐标描述了的范围是正则正确匹配了的范围,而不是匹配前的预定范围(3-3):
这点我觉得还是很有必要明确一下的:
第1次结果 2 4 ,对应str中的11,而不是预定范围 aa11bb22
第2次结果 6 8 ,对应str中的22,也不是预定范围 bb22
input
返回预被匹配的完整的字符串。只读。
lastMatch
返回最后被匹配的字符串片段,初始值为空字符串,只读。(虽然帮助文档上说可是使用替换符 $&,实际上却无法使用)
lastParen
如果有的话,返回最后被匹配的子匹配,初始值为空字符串,只读。(替换符 $+同样无法使用)
$1 - $9
如果有的话,返回九个最近保存的被匹配的子匹配,初始值为空字符串,只读。
leftContext
返回最后被匹配的字符串片段的左边所有字符串片段,初始值为空字符串,只读。
rightContext
返回最后被匹配的字符串片段的右边所有字符串片段,初始值为空字符串,只读。
这些属性同样都是被正则表达式对象或string对象直接影响,请不要把它们和string对象的replace中使用的替换符搞混,或许它们在功能上很相似,但本质是不同的。下面这个例子展现了这些属性的用法:(3-4)
这里要特别注意一下$1-$9,它们只有9个,如果多于9个的子匹配将无法存入(区别于string对象的replace中的$1-$99的替换符)。
总结
好了,写了一天终于快结束了,基本上在js中会用到正则的就是上面3个对象。因为有很多属性的命名和功能都差不多,但却存在细微的差别,导致经常会被搞晕或出现奇怪的问题。最后总结一下必须要搞明白的几点:
1.正则表达式对象和全局RegExp对象是完全两个对象。正则表达式对象(regular expression object);全局RegExp对象(global RegExp object)。打个比方:大家都喜欢玩电子游戏的吧,一个游戏有多个存档,一个存档就好比一个正则表达式对象,相互之间并没有联系且互不干涉。而我们读取了一个存档后系统就是按照你所选择的那个存档开始游戏,就是说你同一个时间只可能使用一个存档来进行游戏,这时游戏机的内存中就只有最新载入的那个存档,这些数据就是全局RegExp对象。所以你载入了哪个就直接影响了内存的内容,等你下次换了一个存档,内存的内容就相应的改变了。同理,你可以分别改变单个正则表达式对象,并刷新全局RegExp对象,但不同的正则表达式对象之间是不会有干扰的。
2.String对象如果使用了正则方法同样也会刷新全局RegExp对象,但是却不依赖正则表达式对象的lastIndex。
3.在通过读取全局RegExp对象的属性时一定要小心,因为可能此时它存放的内容已经被其他地方的程序修改了。
4.正则表达式对象的exec方法和String对象的match方法存在着细微的差别,特别是在g模式下。
5.String对象的replace中使用的替换符和全局RegExp对象属性中使用的替换符有细微的差别,特别是$1-$9。
这偏资料按使用对象的不同分类,最后加一篇总结
正则表达式对象
String对象
RegExp对象
总结
正则表达式对象
正则表达式对象只是一个正则表达式的描述,有两种方法实例化(1-1):
- //方法1
- var re1 = //d/d/g;
- //方法2
- var re2 = new RegExp("//d//d","g");
当预先知道查找字符串时用方法1;当查找字符串经常变动或不知道时用语法(比如由用户输入得到的字符串)时用方法2。注意一点,方法2的正则元字符/由于是写在""中,所以需要双写,否则会被编译为字符串元字符。
正则表达式对象有两个属性:
lastIndex
被匹配的字符串的的结束位置,初始值为0,匹配失败后也将还原为0。
为了方便分析,我打印出每次正则匹配的结果:(1-2)
- var re=/book/g; //注意是g模式,否则每次的匹配位置都是从字符起始位置^开始,后面的例子也是如此。
- var str="book book";
- alert("re.lastIndex:"+re.lastIndex);
- alert("test result:"+re.test(str)+"/nre.lastIndex:"+re.lastIndex);
- alert("test result:"+re.test(str)+"/nre.lastIndex:"+re.lastIndex);
- alert("test result:"+re.test(str)+"/nre.lastIndex:"+re.lastIndex);
- var re=/book/g;
- var str="book book";
- alert("re.lastIndex:"+re.lastIndex);
- alert("test result:"+re.test(str)+"/nre.lastIndex:"+re.lastIndex);
- re.lastIndex = 6; //手动改变了lastIndex
- alert("test result:"+re.test(str)+"/nre.lastIndex:"+re.lastIndex);
- alert("test result:"+re.test(str)+"/nre.lastIndex:"+re.lastIndex);
对于lastIndex还要注意一点,如果只是匹配一个位置,lastIndex会被驱动机制强行向后移动一位(避免产生无限循环):(1-4)
- var re=/c?/g;
- var str="aaa";
- re.test(str);
- alert("re.lastIndex:"+re.lastIndex);
source
正则表达式对象的表达式文本,只读。此属性可以用于对外部传入的正则表达式的检测。(1-5)
- var re=//d/g;
- alert("re.source:"+re.source);
test
指出在被查找的字符串中是否可以匹配至少1次,返回bool值。这是个比较常用的方法,用来仅检测是否匹配,而忽略具体的匹配结果。有一点需要特别注意:(1-6)
- var re = /a/g;
- var str1 ="This is a bus.";
- var str2 ="It is a bus.";
- alert("test result:"+re.test(str1));
- alert("test result:"+re.test(str2));
- alert("test result:"+re.test(str2));
第1次匹配后,lastIndex为9。第2次匹配时,即使字符串不是原先那个str1,但原先的lastIndex的值并没有改变,直接作为这次匹配的起始位置,也就是在u的前面,这样第2次当然就匹配失败了。
exec
用正则表达式在字符串中运行查找,并返回包含该查找结果的一个数组,如果匹配失败就返回null。
如果没有使用g模式,返回数组的0元素包含了完整的匹配,而第1到n元素中包含的是匹配中出现的一个子匹配(按()顺序排列):(1-7)
- var re = /b(/w)(/w)/;
- var str = "be box bus book";
- var arr = re.exec(str);
- for(var i=0;i<arr.length;i++){
- alert(arr[i]);
- }
box 存在arr[0]中,是b/w/w的完整匹配
o 存在arr[1]中,是第1个/w的子匹配
x 存在arr[2]中,是第2个/w的子匹配
还不光如此,返回的数组arr还包含了3个属性:
input 被匹配的完整字符串(区别于被匹配到的字符串内容arr[0])
index 被匹配的字符串的起始位置
lastIndex 被匹配的字符串的结束位置(区别于正则表达式对象的lastIndex,虽然当前情况下值相同)
这个例子可以看到arr和re都有自身的lastIndex属性,千万不要忽略这点:(1-8)
- var re = /b(/w)(/w)/;
- var str = "be box bus book";
- var arr = re.exec(str);
- alert("arr.input:"+arr.input+"/narr.index:"+arr.index+"/narr.lastIndex:"+arr.lastIndex+"/nre.lastIndex:"+re.lastIndex);
- var re = /b(/w)(/w)/g;
- var str = "be box bus book";
- var arr = re.exec(str);
- var result = "";
- for(var i in arr){
- result+=i+":"+arr[i]+"/n";
- }
- alert(result+"re.lastIndex:"+re.lastIndex);
- //手动改变re.lastIndex的值,将影响下次匹配的起始位置!
- //re.lastIndex = 8;
- arr = re.exec(str);
- result = "";
- for(var i in arr){
- result+=i+":"+arr[i]+"/n";
- }
- alert(result+"re.lastIndex:"+re.lastIndex);
- //虽然每次只能返回一个匹配项,但利用匹配失败后返回null的性质就可以获取所有可能的匹配项。
- var re = /b(/w)(/w)/g;
- var str = "be box bus book";
- while((arr = re.exec(str))!= null){
- var result = "";
- for(var i in arr){
- result+=i+":"+arr[i]+"/n";
- }
- alert(result+"re.lastIndex:"+re.lastIndex);
- }
compile
把正则表达式编译为内部格式,从而执行得更快。关于这个方法我试了很多次,但发现是否把正则表达式预先编译速度好象都一样,暂时就先不给出说明了,等有好的例子再来补上。
String对象
字符串对象大家都很熟悉了,我这里只分析一下会用到正则表达式的4个方法。这些方法同样也会改变正则表达式对象的lastIndex属性。
search
返回第1次成功匹配的位置,如果匹配失败返回-1。
初看之下,这个方法和正则表达式对象的test方法差不多,只要一次成功的匹配就直接返回结果(只不过一个返回位置,一个返回bool),都不存放具体的匹配内容。但请看这个例子:(2-1)
- var re = //d/d/g;
- var str="abcd1234";
- result = str.search(re);
- alert("result:"+result+"/nre.lastIndex:"+re.lastIndex);
- result = str.search(re);
- alert("result:"+result+"/nre.lastIndex:"+re.lastIndex);
- re.test(str);
- alert("result:"+result+"/nre.lastIndex:"+re.lastIndex);
第2次结果 result同样为4 ,正则表达式对象的lastIndex还是为6 。
第3次结果 result还是4 ,正则表达式对象的lastIndex却变成了8 。
第2次匹配的结果就展现了两种方法本质区别:虽然使用的是g模式,而且正则表达式对象的lastIndex也被改变了,但后续的search匹配并没有像test那样继lastIndex后面开始匹配(第3次匹配),而是直接从0开始。也就是说正则表达式对象的lastIndex对string的方法并没有任何影响,只对自身的方法起作用。
split
将一个字符串分割为子字符串,然后将结果作为字符串数组返回。这个方法使用起来比较简单但很实用:(2-2)
- var re = //s*;/s*/g; //是不是g模式结果都一样。
- var str="book a;book b ;book c ; book d";
- var arr = str.split(re);
- var result = "";
- re.lastIndex=10; //即使改变lastIndex也不影响结果。
- for(var i =0;i<arr.length;i++){
- result+=arr[i]+"/n";
- }
- alert(result);
match
使用正则表达式模式对字符串执行查找,并将包含查找的结果作为数组返回,没有匹配则返回null。
这个方法的作用与正则表达式对像的exec相同,但使用起来有些区别,同样根据是否使用g模式分两中情况:
不使用g模式情况下,结果与不使用g模式的正则表达式对像的exec相同数组的0元素包含整个匹配,而第1到n元素包含了匹配中曾出现过的子匹配。
同样,返回的数组也包含了3个属性:
input 被匹配的完整字符串
index 被匹配的字符串的起始位置
lastIndex 被匹配的字符串的结束位置
如果前面的都理解了的话,这个例子就很清楚了:(2-3)
- var re = /(.)a(.)/;
- re.lastIndex = 10; //再次强调,这样的改变不会影响结果!
- var str="The rain in Spain falls mainly in the plain";
- var arr = str.match(re);
- var result = "";
- for(var i in arr){
- result+=i+":"+arr[i]+"/n";
- }
- alert(result)
- //同理,每次都是从0位置开始,结果每次都相同。
- arr = str.match(re);
- result = "";
- for(var i in arr){
- result+=i+":"+arr[i]+"/n";
- }
- alert(result)
- var re = /(.)a(.)/g;
- re.lastIndex = 10; //再次强调,这样的改变不会影响结果!
- var str="The rain in Spain falls mainly in the plain";
- var arr = str.match(re);
- var result = "";
- for(var i in arr){
- result+=i+":"+arr[i]+"/n";
- }
- alert(result)
replace
返回根据正则表达式进行文字替换后的字符串的复制,如果匹配失败按原样返回字符串。这是个相当复杂的方法,但功能却很强大。让我们分别看看各种使用方法,且每种方法中是否使用g模式的结果都不同:
入门级的用法,使用固定的字符串替换掉被成功匹配的部分:(2-5)
- //非g模式
- var re = /(t)he/i;
- var str="The man hit the ball with the bat.";
- var result = str.replace(re,"a");
- alert(result)
- //连续使用replace也始终从字符串的起始位置开始匹配,完全符合通用规则。
- result = str.replace(re,"a");
- alert(result)
- //非g模式
- var re = /(t)he/ig;
- var str="The man hit the ball with the bat.";
- var result = str.replace(re,"a");
- alert(result);
- var re = /(t)he/ig;
- var str="theeeeeeeee";
- var result = str.replace(re,"th");
- alert("result:"+result+"/nre.lastIndex:"+re.lastIndex)
然后就是中级用法,将本轮被匹配的子片段或相关片段直接运用到替换字符串中。这个我特别强调了“本轮”,就是说每次匹配的结果都可能不同,这直接影响大了替换字符串的内容。
调用被匹配的子片段或相关片段的替换符(必须直接写在""中,如果没有相应的匹配结果将直接作为字符显示):
$& 完整的被匹配的片段
$` 在被匹配的字符串中 $& 之前的片段
$' 在被匹配的字符串中 $& 之后的片段
$n/$nn 被匹配的第 n/nn 个片段(从1开始)
$$ $字符本身
下面的例子详细的说明了这些用法:(2-7)
- //这样就可以保持原先t的大小写不变
- //非g模式
- var re = /the/i;
- var str="The man hit the ball with the bat.";
- var result = str.replace(re,"[$&]");
- alert(result)
- //g模式
- var re = /the/ig;
- var str="The man hit the ball with the bat.";
- var result = str.replace(re,"[$&]");
- alert(result)
- //下面展示一下所有替换符的效果
- var re = /(t)h(e)/i;
- var str="The man hit the ball with the bat.";
- var result = str.replace(re,"[$&]");//替换字符变为[The]
- alert(result)
- result = str.replace(re,"[$1]"); //替换字符变为[T]
- alert(result)
- result = str.replace(re,"[$2]"); //替换字符变为[e]
- alert(result)
- result = str.replace(re,"[$3]"); //由于没有第3个子匹配,$3直接作为字符,替换字符变为[$3]
- alert(result)
- result = str.replace(re,"[$`]"); //由于$&匹配The,$`接能匹配The左边的字符串起始位置,替换字符变为[]
- alert(result)
- result = str.replace(re,"[$']"); //由于$&匹配The,$'接能匹配The右边的字符串片段,替换字符变为[ man hit the ball with the bat.]
- alert(result)
- result = str.replace(re,"[$$]"); //$字符本身,替换字符变为[$]
- alert(result)
最后讲解一下高级用法,每轮都使用回调函数来生成一个替换字符串,相比刚才的功能还停留在字符串的拼装层面上,现在更是将正则的功能发挥了淋漓尽致。
先说明一下回调函数的语法:(2-8)
- //帮助文档上的例子是这样的
- function($0,$1,$2,...){return ...}
- //但我觉得这样的写法并不好,这个是我改写的
- function(_whole,_sub1,_sub2,...,_index,_input){return ...}
理解了上面的说明后,我们来看一下具体每个参数是什么意思:
第1个参数(_whole) 本轮正则完整匹配的字符片段
第2-n个参数(_sub1 - _sub(n-1))本轮正则匹配的第(n-1)个子片段
第n+1个参数(_index) 本轮被成功匹配的字符片段的坐标
第n+2个参数(_input) 被匹配的原始字符串
可见回调函数的参数个数是不定的。还有一点要说明,所有回调函数内部要使用到的参数都必须定义在参数列表中正确的位置,而没有用到的参数不必定义。但是如果要用到后面的参数,前面的都必须被定义。
说了这么多,让我们来看一下一个官方的例子:(2-9)
- //非g模式
- //只替换第1个被匹配的32F
- var re = /(/d+(/./d*)?)F/b/;
- var str= "Water freezes at 32F and boils at 212F.";
- var result = str.replace(re,function($whole,$sub1) { return((($sub1-32) * 5/9) + "C");});
- alert(result)
- //g模式
- var re = /(/d+(/./d*)?)F/b/g;
- var str= "Water freezes at 32F and boils at 212F.";
- var result = str.replace(re,function($whole,$sub1) { return((($sub1-32) * 5/9) + "C");});
- alert(result)
- //下面展示一下所有参数的功能
- var re = /(/d+(/./d*)?)F/b/g;
- var str= "Water freezes at 32.1F and boils at 212F.";
- var result = str.replace(re,function($whole,$sub1,$sub2,$index,$input) { return("[$whole:"+$whole+"][$sub1:"+$sub1+"][$sub2:"+$sub2+"][$index:"+$index+"][$input:"+$input+"]");});
- alert(result)
RegExp对象
RegExp对象是存放正则匹配结果信息的全局对象,不能实例化,只有属性(静态属性,都为只读),没有方法。不要认为下面的代码是RegExp对象的实例化(3-1):
- var re = new RegExp("/d",g);
- //re是一个正则表达式对象的实例,不是RegExp对象的实例,后面说到这两个对象的区别。
- var re=//d./g;
- var str="1234";
- alert("test result:"+re.test(str)+"/nRegExp.index:"+RegExp.index);
- alert("test result:"+re.test(str)+"/nRegExp.index:"+RegExp.index);
- alert("test result:"+re.test(str)+"/nRegExp.index:"+RegExp.index);
- alert("test result:"+re.test(str)+"/nRegExp.index:"+RegExp.index);
- alert("test result:"+re.test(str)+"/nRegExp.index:"+RegExp.index);
第1次结果 0
第2次结果 2
第3次结果 2
第4次结果 0
第5次结果 2
可以看到第3次由于匹配失败,所以RegExp对象的index值没有被更新。所以验证了:只有在成功匹配的前提下,RegExp对象的属性值才会被改变!
RegExp对象每个属性都是被正则表达式对象或String对象所改变的,下面具体分析一下RegExp对象每个属性的含义及用法:
index
被匹配的字符串的起始位置,在还没有进行一次匹配的情况下值为-1,只读。
lastIndex
被匹配的字符串的的结束位置,在还没有进行一次匹配的情况下值为-1,只读。
这两个属性都是字符串中的位置坐标,最前端为0。这个被位置坐标描述了的范围是正则正确匹配了的范围,而不是匹配前的预定范围(3-3):
- var re=//d./g;
- var str="aa11bb22";
- re.test(str);
- alert("RegExp.index:"+RegExp.index+"/nRegExp.lastIndex:"+RegExp.lastIndex);
- re.test(str);
- alert("RegExp.index:"+RegExp.index+"/nRegExp.lastIndex:"+RegExp.lastIndex);
第1次结果 2 4 ,对应str中的11,而不是预定范围 aa11bb22
第2次结果 6 8 ,对应str中的22,也不是预定范围 bb22
input
返回预被匹配的完整的字符串。只读。
lastMatch
返回最后被匹配的字符串片段,初始值为空字符串,只读。(虽然帮助文档上说可是使用替换符 $&,实际上却无法使用)
lastParen
如果有的话,返回最后被匹配的子匹配,初始值为空字符串,只读。(替换符 $+同样无法使用)
$1 - $9
如果有的话,返回九个最近保存的被匹配的子匹配,初始值为空字符串,只读。
leftContext
返回最后被匹配的字符串片段的左边所有字符串片段,初始值为空字符串,只读。
rightContext
返回最后被匹配的字符串片段的右边所有字符串片段,初始值为空字符串,只读。
这些属性同样都是被正则表达式对象或string对象直接影响,请不要把它们和string对象的replace中使用的替换符搞混,或许它们在功能上很相似,但本质是不同的。下面这个例子展现了这些属性的用法:(3-4)
- var re = new RegExp("d(b+)(d)","ig");
- var str = "cdbBdbsbdbdz";
- var arr = re.exec(str);
- var result= "RegExp.input:" + RegExp.input + "/n";
- result += "RegExp.lastMatch: " + RegExp.lastMatch + "/n"; //第1轮 d(b+)(d) 匹配了dbBd,所以RegExp.lastMatch就是dbBd
- result += "RegExp.lastParen " + RegExp.lastParen + "/n"; //虽然匹配了两个子片段,但RegExp.lastParen只保留最后一个(d)匹配的d
- result += "RegExp.leftContext " + RegExp.leftContext + "/n"; //dbBd左边的字符片段
- result += "RegExp.rightContext " + RegExp.rightContext + "/n"; //dbBd右边的字符片段
- result += "RegExp.$1 " + RegExp.$1 + "/n"; //存放第1个子匹配bB
- result += "RegExp.$2 " + RegExp.$2 + "/n"; //存放第2个子匹配d
- result += "RegExp.$3 " + RegExp.$3 + "/n"; //第3个子匹配为空
- alert(result);
- arr = re.exec(str);
- result= "RegExp.input:" + RegExp.input + "/n";
- result += "RegExp.lastMatch: " + RegExp.lastMatch + "/n"; //第2轮 d(b+)(d) 匹配了dbd,所以RegExp.lastMatch就是dbd
- result += "RegExp.lastParen " + RegExp.lastParen + "/n"; //同理,RegExp.lastParen保留dbd中的d
- result += "RegExp.leftContext " + RegExp.leftContext + "/n"; //dbd左边的字符片段
- result += "RegExp.rightContext " + RegExp.rightContext + "/n"; //dbd右边的字符片段
- result += "RegExp.$1 " + RegExp.$1 + "/n"; //刷新后,第1个子匹配被更新为b
- result += "RegExp.$2 " + RegExp.$2 + "/n"; //刷新后,第2个子匹配被更新为d
- result += "RegExp.$3 " + RegExp.$3 + "/n"; //还是为空
- alert(result);
总结
好了,写了一天终于快结束了,基本上在js中会用到正则的就是上面3个对象。因为有很多属性的命名和功能都差不多,但却存在细微的差别,导致经常会被搞晕或出现奇怪的问题。最后总结一下必须要搞明白的几点:
1.正则表达式对象和全局RegExp对象是完全两个对象。正则表达式对象(regular expression object);全局RegExp对象(global RegExp object)。打个比方:大家都喜欢玩电子游戏的吧,一个游戏有多个存档,一个存档就好比一个正则表达式对象,相互之间并没有联系且互不干涉。而我们读取了一个存档后系统就是按照你所选择的那个存档开始游戏,就是说你同一个时间只可能使用一个存档来进行游戏,这时游戏机的内存中就只有最新载入的那个存档,这些数据就是全局RegExp对象。所以你载入了哪个就直接影响了内存的内容,等你下次换了一个存档,内存的内容就相应的改变了。同理,你可以分别改变单个正则表达式对象,并刷新全局RegExp对象,但不同的正则表达式对象之间是不会有干扰的。
2.String对象如果使用了正则方法同样也会刷新全局RegExp对象,但是却不依赖正则表达式对象的lastIndex。
3.在通过读取全局RegExp对象的属性时一定要小心,因为可能此时它存放的内容已经被其他地方的程序修改了。
4.正则表达式对象的exec方法和String对象的match方法存在着细微的差别,特别是在g模式下。
5.String对象的replace中使用的替换符和全局RegExp对象属性中使用的替换符有细微的差别,特别是$1-$9。
- javascript中正则的使用详解
- 详解Javascript中正则表达式的使用
- 详解Javascript中正则表达式的使用
- 详解Javascript中正则表达式的使用
- 详解Javascript中正则表达式的使用
- javascript中正则表达式相关的方法使用详解
- Javascript中正则 表达式的使用
- JAVASCRIPT中 正则表达式的使用
- Javascript中正则表达式的使用介绍
- javascript中正则表达式详解
- javascript正则表达式使用详解
- javascript正则表达式使用详解
- javascript正则表达式使用详解
- javascript正则表达式使用详解
- JavaScript正则表达式使用详解
- Linux中正则表达式的使用详解
- javascript中使用正则表达式
- javascript中使用正则表达式
- 历史不会忘记人民不会忘记。。。满洲里红军烈士公园
- 香港一日游
- 2008年终总结--总结过去,把握现在,展望未来
- 使用 inotify 监控 Linux 文件系统事件
- JS日历
- javascript中正则的使用详解
- 三等功奖章背后的故事
- 跟踪NtReadFile系统服务的执行过程
- String看堆栈
- 关于#pragma warning
- 几个免费的方案(转自云舒大大)
- Java在WEB中如何统计在线人数
- 制作iis自动安装包
- paradox数据库sum后的排序