JavaScript 对象与继承教程之内置对象(下)
5、 RegExp类
现在要介绍的内容不完全与RegExp类有关,他几乎包含了正则表达式的大部分的基本知识点和含义,但是我无意扩展出来说,如果对此感兴趣,推荐些教程和好书。以下推荐绝非随机、随意。
《精通正则表达式》,该书的作者和译者都对正则表达式有十分深厚的功底和理解,同时该书也是我目前为止见到的最好的教材,而且不用担心你的英文不好,该书的中文版翻译质量绝对一流。不过,这本书正如书名一样,是给对正则有兴趣,且真心的希望有所精通的人看的。
blog.csdn.net/lxcnn。该博客绝对是当今中国数一数二的关于正则方面的精品,博主的造诣绝不含糊,且文笔和内容也十分值得大家一看的。
《正则表达式傻瓜书》,该书的作者即《精通》一书的译者,有幸翻阅了该书的样章,书中的内容深浅合宜,语言精炼,加之作者深厚的功底,书的质量不言而喻。虽然该书尚未出版,但是绝对的值得期待。
《正则表达式必知必会》,该书短小易懂,适合不大希望精通于此,仅限于能用即可的朋友阅读,该书中有许多实例,很多内容大家甚至可以拿来就用,只是受制于其篇幅,该书不大适合进阶阅读。
如果你对以上的珍品不感兴趣,那么下面的内容希望你能耐心的看下去,我会尽我所能来让你有所收获。
正则表达式通常用于查找、验证、替换字符串内容。其便捷和高效致使它渐渐的为大家所接收和喜爱,我想很多看到这里的朋友可能会有这样的感受:“每次用到的时候我都会翻一遍正则的简明教程或手册,但是总是记不住,而且当有一天翻自己所写的正则时,会看不懂。”。那么没关系,记不住是因为你没有深入的去了解和思考它,我不会穷举每一个元字符和语法结构,尽量让它变得有那么一点意思。
案例一、验证一个字符串由8到16位数字、英文字母、下划线组成。
这个验证是最基本且最常用的,验证的首要条件是限制死首和尾,即正则必须匹配整个字符串,从起始字符一直到结尾字符。先看正:
/ ^ [ \d a-zA-Z_] {8,16} $ /
这个正则中没有任何的空白,两个/之间的内容就是正则表达式,在JavaScript中我们有两个方式创建正则表达式对象,上面的是字面量对象,另外一个是构造函数方法:
var reg = new RegExp("^[\\da-zA-Z_]{8,16}$","mode");
var reg = new RegExp("^[\\da-zA-Z_]{8,16}$","mode");
关于模式,有g、i、m三个,i表示忽略大小写,g和m会在后面深入解析,因为简单的说个一两句不会太容易理解。
两者效果几乎等效,而字面量方式简洁的许多,细心的你一定看到后一种方法中\d前多了一个\。原因是字符串中\x会被当作转义字符进行转义。
接下来看蓝色部分,^和$符号分别代表字符串的开始和字符串的结尾,但是在m模式的时候他们的含义会有不同,在m模式下,他们分别代表行首和行尾。
[]之间的是字符集合,这相当于字符的switch,[^...]则是匹配非集合。这个很好理解,在你看过的任何一篇教程中都不会漏下它,不过你一定想知道匹配不等于某个字符串的字符串该怎么办,答案是否定环视(断言、预查,以下统一称为环视),一会在介绍环视时,我们再来介绍。
{8,16}表示一个范围量词,他表示匹配之前的分组或字符重复多少次。与之相对应的还有+,*,?,{m,},{,n}等。
案例二、验证一个有数字、字母组成的8-12位字符串。必须包含至少一个数字和字母。
这个与案例一有点区别,那就是最后一句的限制,所以我们需要引入一个判断逻辑,那就是环视,先引入环视的概念和解释吧^_^。
环视从方向来说分为逆序和顺序,从逻辑来说分为肯定和否定。不过JavaScript正则表达式不支持逆序的环视,只支持顺序肯定环视、顺序否定环视。环视有个特点就是不占据所匹配字符,所以他会预先进行一次匹配,然后匹配完了之后会回溯会环视匹配之前的位置。让我们看刚才的案例吧。首先进行2次否定环视,分别排除掉纯粹由数字或由字母组成的字符串。然后再把案例一的正则表达式放进来就可以了。如:
/^(?!\d+$)(?!\D+$)[a-zA-Z\d]{8,12}$/
假设字符串为abcdefg123。那么匹配的过程如下。首先^匹配字符串开始,接着是第一个否定环视,(?!\d+$)他从'a'开始匹配,\d+即不能匹配,匹配失败,否定环视成功,接下来匹配(?!\D+$),\D表示非数字,那么从a-g都可以匹配,接下来是1,\D+匹配完成,如果没有$那么这个匹配其实也算成功,但是\D+匹配完成之后有一个$,那么$匹配1失败。否定环视成功,此时字符串位置回到a之前。开始后面的匹配。[a-zA-Z\d]{8,12}匹配abcdefg123成功,具体过程忽略,然后把句柄交给$,匹配成功。此时整个匹配成功。因为篇幅和表述能力问题,关于环视的解释不够清晰,如果至此你依然没有弄清。那么请访问过客的相关博客,真的很棒( blog.csdn.net/lxcnn )。
好吧,我们现在倒回来看一些日常应用中的一些误解导致的错误应用。
1、 包含字母数字的字符串判断使用\w是不对的。因为\w<==>[a-zA-Z0-9_],所以使用时请注意
2、 JavaScript中不支持逆序环视。无论是肯定还是否定,因此如果其他语言的程序员在使用js正则的时候千万要注意
3、 .元字符在字符组中是普通字符,而不解释为任意字符的元字符,同时.元字符不匹配\n(换行符),JavaScript中没有单行模式以使.匹配\n。
4、 在使用test、exec时一定要弄清楚g模式之后再使用。因为一旦使用了g模式,那么会保存全局的lastIndex,在你下一次进行匹配时是从lastIndex开始,而非字符串首。(感谢过客之前对此的解释和指导。有兴趣的可以看这个帖子,看过客的解释 http://topic.csdn.net/u/20110117/18/e2423564-856b-4f46-8879-0c382a3e1c7a.html?93335 )。在这里我把其中的例子发出来,让大家有个了解,请看:
var arr = [ 1 , 2 , 3 , 4 , 5 ];
var reg = /^\d+$/g ;
for ( var i = 0 ; i < arr . length ; i ++){
alert ( reg . test ( arr [ i ]));
}
var arr = [ 1 , 2 , 3 , 4 , 5 ];
var reg = /^\d+$/g ;
for ( var i = 0 ; i < arr . length ; i ++){
alert ( reg . test ( arr [ i ]));
}
5、 js正则不支持命名分组,所以后向引用的时候要格外注意分组的序号。
6、 js正则不支持固话分组和占有优先量词。所以如果可以的话,[^xx]+取代.+?来使用,一方面可以提高效率,另一方面可以减少错误的匹配结果。
7、 正则效率上,new RegExp()与字面量方式创建没有区别,而使用变量保存一个正则对象和循环中使用同一个正则表达式字面量效率上也没区别(至少在大部分的浏览器上是这样的)。
8、 在使用new RegExp()创建正则的时候,切记转义问题。new RegExp("\\d")才等价于/\d/,使用new RegExp("\d")就会得到错误的正则。
9、 回溯和环视是2个非常重要的概念,同时也要弄清楚位置和字符的区别。时间和能力的原因,推荐移步过客的博客( http://blog.csdn.net/lxcnn/archive/2009/06/28/4304754.aspx 和 http://blog.csdn.net/lxcnn/archive/2009/06/28/4304651.aspx )
关于正则目前仅仅只提这些,因为后面准备了一个实战博客,那个时候会详细介绍几个实用的实例以及推导过程。下面看看在介绍字符串的时候落下的几个方法。replace/match/search,OK,我们来一个一个看吧
replace方法是提供强大的替换功能,他不仅能够替换简单的文本。还能使用一个替换函数来进行更复杂的替换,请看下面的代码:
var str = "dream on, dream on, dream on,dream yourself a dream come true.dream on, dream on, dream on," ;
var reg = /dream/g ;
var i = 0 ;
alert ( str . replace ( reg , function ( m ){
return m + ( i ++). toString ();
}));
var str = "dream on, dream on, dream on,dream yourself a dream come true.dream on, dream on, dream on," ;
var reg = /dream/g ;
var i = 0 ;
alert ( str . replace ( reg , function ( m ){
return m + ( i ++). toString ();
}));
该方法通过一个匿名方法,将查找到的dream单词进行替换,该替换会给后面的dream单词添加一个序列。你可能会想到下面的方法:
var str = "dream on, dream on, dream on,dream yourself a dream come true.dream on, dream on, dream on," ;
var reg = /(dream)/g ;
var i = 0 ;
alert ( str . replace ( reg , "$1" + i ++));
var str = "dream on, dream on, dream on,dream yourself a dream come true.dream on, dream on, dream on," ;
var reg = /(dream)/g ;
var i = 0 ;
alert ( str . replace ( reg , "$1" + i ++));
可惜很遗憾你运行一下就会发现每次都是0。这点在任何可以使用委托方法(匿名方法、函数指针==)进行替换的语言中都是一样的,这个和替换机制是有关的。
replace的时候非正则替换有个缺陷,就是只会替换第一个。相当于没有加上g模式的正则。所以使用的时候一定要注意,当然在替换的时候不要忘了g模式哟。
match则是查找匹配的方法,他同样支持正则表达式作为查找模式,不过有一点需要注意,如果使用全局检索(g模式)的时候,返回的是一个数组,该数组不包括分组信息。而如果不使用全局检索(g模式)的时候,返回的是第一个匹配的文本,以及各分组的匹配内容。比如下面的代码:
var str = "dream on, dream on, dream on,dream yourself a dream come true.dream on, dream on, dream on," ;
var reg = /(dream)[^,]+/ ;
alert ( str . match ( reg )); //返回数组,数组内容为 dream on,dream2 个元素。
reg = /(dream)[^,]+/g ;
alert ( str . match ( reg )); //返回数组,数组内容为 dream on,dream on,dream on,dream yourself a dream come true.dream on, dream on,dream on 几个元素。
var str = "dream on, dream on, dream on,dream yourself a dream come true.dream on, dream on, dream on," ;
var reg = /(dream)[^,]+/ ;
alert ( str . match ( reg )); //返回数组,数组内容为 dream on,dream2 个元素。
reg = /(dream)[^,]+/g ;
alert ( str . match ( reg )); //返回数组,数组内容为 dream on,dream on,dream on,dream yourself a dream come true.dream on, dream on,dream on 几个元素。
最后一个是search,他接收一个正则对象,他返回search到的第一个匹配文本的起始位置,该方法不受g模式的影响。
var str = "dream on, dream on, dream on,dream yourself a dream come true.dream on, dream on, dream on," ;
reg = /dream/g ;
alert ( str . search ( reg ));
alert ( str . search ( reg ));
alert ( str . search ( reg ));
var str = "dream on, dream on, dream on,dream yourself a dream come true.dream on, dream on, dream on," ;
reg = /dream/g ;
alert ( str . search ( reg ));
alert ( str . search ( reg ));
alert ( str . search ( reg ));
该方法三次alert出的都是0。其实还有一个split方法也支持正则。不过它很简单,在这里不再提了。
RegExp有以下的属性需要记住,lastIndex是最后一次匹配的位置(还记得g模式么),source是正则表达式的源文本,而$1、$+、$$等意味著什么请可查看下手册,这里就不一一列举了。
6、 Global对象
这里没有用类这个名词,是因为连这个关键字实际上都不存在,在JavaScript中所有的函数都是对象的方法,所以你看到的所有内置的“函数”其实都是全局对象的方法。
编码、解码方法,对uri进行编码是为了使防乱码的一个有效措施,很多后台语言也都有相对应的解码、编码方法。 encodeURI/encodeURIComponent/decodeURI/decodeURIComponent。
如果不能区分其中的区别,那么用后者代替前者吧。
escape/unescape基本上不推荐使用,用上面的方法来取代吧。
类型转换的方法也是比较常用的方法。比如Number、String方法,但是看似简单的2个方法还有真点需要补充的。Number方法不能取代parseInt和parseFloat方法,因为Number方法只能把真正的数字转换为数字(该方法用来验证一个输入是否是数字很好吧)。先看这4句代码。
alert ( Number ( '01x' )); //NaN
alert ( Number ( '011' )); //11
alert ( String ( 0X11 )); //17
alert ( String ( 011 )); //9
alert ( Number ( '01x' )); //NaN
alert ( Number ( '011' )); //11
alert ( String ( 0X11 )); //17
alert ( String ( 011 )); //9
其实关于这点我在之前的博客提到过,现在再拿出来解释下。第一个如果用parseInt会得到1,而用Number强转将返回NaN。千万要使用isNaN来判断是否成功转换,而不是Number('01x').constructor == Number。因为NaN是个数字类型,但是他的意思是Not a Number。第二个返回的是11,看上去很正常啊?可是011往往可能意味着是8进制的数哦。所以字符串形式的011和数字型的011可不是一个意思哦。所以在使用数字字面量的时候注意前置0的使用。第三行返回的是17,为什么?刚才提到过0X11是16进制的数字,那么他翻译为17就意味着转换为string类型前会强转为10进制数,第三行同理。那么第三行想输出11怎么办呢,使用它:(0x11).toString('16'),8进制也一样哦。所以转换的时候要了解清楚先。
最后一个要说的是最有争议的eval方法。他的功能十分强大,但是缺点也异常明显,代码不易读、性能低下、安全性降低。所以一般情况下可以用setTimeout、setInterval、数组等方法来替代。注意在处理json的时候要把json包括在一个括号里哦:eval('(' + jsondata + ')');
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/cj205/archive/2011/01/24/6161968.aspx