`
jickcai
  • 浏览: 246789 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

javascript 学习笔记五 第七章 正则表达式

阅读更多
第七章 正则表达式
JavaScript对正则表达式的支持是通过ECMAScript中的RegExp类实现的。RegExp对象的构造函数可以带一个参数或两个参数。
第一个参数是描述需要进行匹配的模式字符串,如果还有第二个参数,这个参数则制定了额外的处理指令.
最基本的正则表达式即使普通的字符串。例如,要匹配单词"cat",可以这样定义正则表达式
var reCat=new RegExp("cat");
这个正则表达式只会匹配字符串中出现的第一个单词"cat",而且,它是区分大小写的.如果要让这个正则表达式匹配所有出现的
"cat",需要给构造函数添加第二个参数.
var reCat=new RegExp("cat","g");
在这一行代码里,第二个参数"g"是global的缩写,表示要搜索字符串中出现的全部"cat",而不是在找到第一个匹配后就停止。
如果还要让模式不区分大小写,可以给第二个参数添加字符"i"("i"是case-insensitive中insensitive的缩写)
var reCat=new RegExp("cat","gi");
有些正则表达式字面量使用Perl风格的语法:
var reCat=/cat/gi;
正则表达式字面量由一条斜线开始,跟着是字符串模式,然后是另一条斜线。如果还要指定格外的处理指令,如"g"和"i",直接跟在
第二个斜线的后面.
7.1.1 使用RegExp对象
创建一个RegExp对象后,把它应用到字符串上。RegExp和String的一些方法都可使用。
首先用正则表达式要做的可能判断某个字符串是否匹配指定的模式。最简单的情况,RegExp有个test()方法,如果给定字符串
(只有一个参数)匹配这个模式,它就返回true,否则返回false.
var sToMatch="cat";
var reCat=/cat/;
alert(reCat.test(sToMatch)); //outputs "true"
在这个例子中,警告对话框输出"true",因为模式匹配字符串。即使模式只在字符串中出现一次,也认为是一个匹配,test()将
返回真。但如果想访问模式的每一次出现呢?可以使用exec()方法。
RegExp的exec()方法,有一个字符串参数,返回一个数组。数组中的每一个条目是第一个匹配,其他的是反向引用.
var sToMatch="a bat,a cat,a fat bat,a fat cat";
var reAt=/at/;
var arrMatches=reAt.exec(sToMatch);
这里,arrMatches只包含一个条目:第一个"at"的实例.但是为什么这样只能返回包含一个条目的数组呢?假设想获得
某个模式所有出现呢?这里需要用到另外一种方法。
String对象有个match()方法,它会返回一个包含在字符串中的所有匹配的数组。这个方法调用string对象,同时传给它
一个RegExp对象:
var sToMatch="a bat,a Cat,a fAt baT,a faT cat";
var rcAt=/at/gi;
var arrMatches=sToMatch.match(rcAt);
alert(arrMatches.length);//outputs "6"
for(var i=0;i<arrMatches.length;i++){
alert(arrMatches[i]);
}//outputs at,at,At,aT,aT,at
另一个叫做search()的字符串方法的行为与indexOf()有些类似,但是它使用一个RegExp对象而非仅仅一个子字符串。
Search()方法返回在字符串中出现的一个匹配的位置。
var sToMatch="a bat,a Cat,a fAt baT,a faT cat";
var rcAt=/at/gi;
alert(sToMatch.search(rcAt));//outputs "3"
在这个例子中,警告对话框输出"3",因为"at"第一次在字符串中出现的位置为3.值得注意的是,全局匹配正规表达式
(带参数g)在使用search()时不起作用.
7.1.2 扩展的字符串方法
var sToChange="The sky is red.";
var reRed=/red/;
alert(sToChange.replace(reRed,"blue"));//outputs "The sky is blue."
也可以指定一个函数作为replace()的第二个参数。这个函数可以接受一个参数,即匹配了的文本,并返回应当进行替换的文本。
var sToChange="The sky is red.";
var reRed=/red/;
var sResultText=sToChange.replace(reRed,function(sMatch){
return "blue";
});
alert(sResultText);
在这个例子中,在函数中的sMatch的值总为"red"(因为这是唯一匹配的模式)."red"的首次出现被替换为函数的返回值"blue".
函数加上正则表达式处理文本替换的能力是非常强大的,可让你使用所用JavaScript的功能来决定什么才是替换文本。
注意:
注意在前面的三个例子中,都只是替换给定字符串中的第一个出现的"red".如果要替换"red"的所有出现,必须指明
表达式/red/g.

var sColor="red,blue,yellow,green";
var reComma=/\,/;
var arrColors=sColor.split(reComma); //split at each comma
alert(arrColors.length);//outputs "4"
7.2 简单模式
原字符、字符类和量词等都是正则表达式语法中非常重要的组成部分,适当应用可以取得很好的效果。
7.2.1 元字符
在前一节总发现,逗号必须进行转义(在前面加上反斜杠)才能正确匹配.这是因为逗号是一个元字符,元字符
是正则表达式语法的一部分。
元字符: ([{\^$|)?*+.
任何时候要在正则表达式中使用这些元字符,都必须对它们进行转义。因此,要匹配一个问号,正则表达式应该
这样表示: var reQMark=/\?/; 或 var reQMark=new RegExp("\\?");
注意到第二行中的两个反斜杠了吗?这是需要大家去理解的重要概念。当正则表达是以这种形式(第二行的,非字面量)
表示时,所有的反斜杠都必须用两个反斜杠来替换。因为JavaScript字符串解析器会按照翻译\n的方式尝试翻译\?.为了保证不会
出现这个问题,在元字符的前面加上两个反斜杠(我们称之为双重转义).这个小小的gotcha就是多数开发者更偏好使用字面量语法的
原因。
7.2.2 使用特殊字符
你可以通过直接使用字符来表示它们本身,也可以使用它们的ASCII代码或者Unicode代码指定字符。要使用ASCII来表示一个字符,
则必须指定一个两位的十六进制代码,并在前面加上\x.例如,字符b的ASCII码为98,等于十六进制的62,因此,表示字符b可以使用\x62:
var sColor="blue";
var reB=/\x62/;
alert(reB.test(sColor));//outputs "true"
使用八进制数值:
var sColor="blue";
var reB=/\142/;
alert(reB.test(sColor));//outputs "true"
使用Unicode来表示字符,必须指定字符串的四位十六进制表示形式。因此b应该是\u0062:
var sColor="blue";
var reB=/\u0062/;
alert(reB.test(sColor));//outputs "true"
注意这种方式如果使用RegExp构造函数来表示字符,则仍需要使用两个反斜杠:
var sColor="blue";
var reB=new RegExp("\\u0062");
alert(reB.test(sColor)); //outputs "true"
另外,还有其他一些预定义的特殊字符,如下表所列:
----------------------------------------------------
字符 描述
----------------------------------------------------
\t 制表符
\n 换行符
\r 回车符
\f 换页符
\a alert字符
\e escape字符
\cX 与X相对应的控制字符
\b 回退字符
\v 垂直制表符
\0 空字符
----------------------------------------------------
如果通过RegExp构造函数来使用它们,则都必须进行双重转义

假设想删除字符串中所有的换行符(常用于处理用户输入的文本).可以这样做:
var sNewString=sStringWidthNewLines.replace(/\n/g,"");
7.2.3 字符类
字符类是用于测试的字符的组合.通过将一些字符放入括号中,可以很有效地告诉正则表达式取匹配第
一个字符、第二个字符等等。例如要匹配字符a,b和c,字符类应该是[abc].这个称之为简单类(simple class),因为它确切地
指定了要查找的那些字符。
1.简单类
var sToMatch="a bat,a Cat,a fAt baT,a faT cat";
var reBatCatRat=/[bcf]at/gi;
//var reBatCatRat=/[\u0062cf]at/gi; 使用Unicode形式
var arrMatches=sToMatch.match(reBatCatRat);
document.write(arrMatches.join(","));//outputs "bat,Cat,fAt,baT,faT,cat"
2.负向类
使用负向类(negation class),它可以指定要排除的字符。例如,要匹配除了a和b的所有字符,那么这个字
符类就是[^ab].脱字符号(^)告诉正则表达式字符不能匹配后面跟着的字符。
var sToMatch="a bat,a Cat,a fAt baT,a faT cat";
var reBatCatRat=/[^bc]at/gi;
var arrMatches=sToMatch.match(reBatCatRat);
document.write(arrMatches.join(","));//outputs "fAt,faT"
3.范围类
指定从a到z之间的范围:[a-z].
var sToMatch="num1,num2,num3,num4,num5,num6,num7,num8,num9";
var reOneToFour=/num[1-4]/gi;
var arrMatches=sToMatch.match(reOneToFour);
document.write(arrMatches.join(","));//outputs "num1,num2,num3,num4"
4.组合类
组合类(combination class)是由几种其他的类组合而成的字符类.
假如要匹配所有的从a-m的字母以及从1-4的数字,以及一个换行符,那么所用到的类应该是这样:
[a-m1-4\n]
注意在内部的类之间不要有空格.
JavaScript/ECMAScript不支持某些其他正则表达式实现中的联合类(union class)和交叉类(intersection calss).
这意味着你不能有类似[a-m[p-z]]或者[a-m[^b-e]]之类的模式出现。
5.预定义类
由于某些模式会反复用到,所以可以使用一组预定义字符类以让我们更方便地指定复杂类。
-------------------------------------------------------------------------------------------------
代码 等同于 匹配
-------------------------------------------------------------------------------------------------
。 [^\n\r] 除了换行和回车之外的任意字符
\d [0-9] 数字
\D [^0-9] 非数字字符
\s [ \t\n\x0B\f\r] 空白字符
\S [^ \t\n\x0B\f\r] 非空白字符
\w [a-zA-Z_0-9] 单词字符(所有的字符、数字和下划线)
\W [^a-zA-Z_0-9] 非单词字符
-------------------------------------------------------------------------------------------------
使用预定义字符可以明显地使模式匹配变得简单。例如,假设想匹配3个数字,如果不用\d的话,代码会类似这样:
var sToMatch="567 9838 abc";
var reThreeNums=/[0-9][0-9][0-9]/;
//var reThreeNums=/\d\d\d/;
alert(reThreeNums.test(sToMatch)); //outputs "true"
7.2.4 量词
量词(quantifier)可以指定某个特定模式出现的次数。当指定某个模式应当出现的次数时,可以指定硬性数量,也可以指定
软性数量.
1.简单量词
-----------------------------------------------------------------------
代码 描述
-----------------------------------------------------------------------
? 出现零次或一次
* 出现零次或多次(任意次)
+ 出现一次或多次(至少出现一次)
{n} 一定出现n次
{n,m} 至少出现n次但不超过m次
{n,} 至少出现n次
---------------------------------------------------------------------
例如,假设想匹配单词bread,read或red.使用问号量词,则可以只要使用一个表达式就可以匹配这三个:
var reBreadReadOrRed=/b?rea?d/;
可以这样理解这个正则表达式:b出现零次或一次,跟着r,跟着e,跟着出现零次或一次的a,跟着d.前面的正则表达式
和下面的这个正则表达式是一样的:
var reBreadReadOrRed=/b{0,1}rea{0,1}d/;
在这个表达式中,问号被花括号代替。在花括号中的数字0和1,前者表示出现次数的最小值,后者表示最大值。这个
表达式理解上和前面的是一样的,仅是表现形式不同而已。两个表达式都是正确的.
-------------------------------------------------------------------------
正则表达式 匹配
-------------------------------------------------------------------------
ba?d "bd","bad"
ba*d "bd","bad","baad"
ba+d "bad","baad","baaad"
ba{0,1}d "bd","bad"
ba{0,}d "bd","bad","baad"
ba{1,}d "bad","baad","baaad"
-------------------------------------------------------------------------
2.贪婪的、懒惰的和支配性的量词
贪婪量词先看整个的字符串是否匹配。如果没有发现匹配,它去掉该字符串中的最后一个字符,并再次尝试。
如果还是没有发现匹配,那么再次去掉最后一个字符,这个过程会一直重复直至到发现一个匹配或者字符串不剩任何字符。
到目前为止讨论的所有量词都是贪婪的。
懒性量词先看字符串中的第一个字母是否匹配。如果单独这个字符还不够,就读入下一个字符,组成两个字符的
字符串。如果还是没有发现匹配,惰性量词继续从字符串中添加字符直到发现匹配或者整个字符串都检查过也没有匹配。
惰性量词和贪婪量词的工作方式恰好相反。
支配量词只尝试匹配整个字符串。如果整个字符串不能产生匹配,不做进一步尝试。支配量词其实简单的说,就是一
刀切。
怎样表示贪婪、惰性和支配量词呢?正是完全使用星号、加号和问号。例如,单独一个问号(?)是贪婪的,但一个问号后
面再跟一个问号(??)就是惰性的。要使问号成为支配量词,在后面加上一个加号(?+).下面的表格列出了所有学过的贪婪、惰性
和支配的量词。
-----------------------------------------------------------------------------------------------------
贪婪 惰性 支配 描述
-----------------------------------------------------------------------------------------------------
? ?? ?+ 零次或一次出现
* *? *+ 零次或多次出现
+ +? ++ 一次或多次出现
{n} {n}? {n}+ 恰好n次出现
{n,m} {n,m}? {n,m}+ 至少n次至多m次出现
{n,} {n,}? {n,}+ 至少n次出现
------------------------------------------------------------------------------------------------------
要说明这三种不同的量词之间的区别,请看下面的例子:
var sToMatch=""abbbaabbbaaabbb1234;
var re1=/.*bbb/g; //greedy
var re2=/.*?bbb/g; //reluctant
var re3=/.*+bbb/g;//possessive
想匹配跟着bbb的任意字符。最后,想要获得的是匹配"abbb"、"aabbb"和"aaabbb".然而,这三个表达式只有一个能正确
地返回这个结果,你能猜出是哪个吗?
如果你猜的是re2,恭喜你答对了!你已经理解了贪婪、惰性和支配量词之间的差别。第一个表达式re1,是贪婪的,所以
首先看整个字符串。这样,下面正是匹配的过程:
re1.test("abbbaabbbaaabbb1234");//false-no match
re1.test("abbbaabbbaaabbb123"); //false-no match
re1.test("abbbaabbbaaabbb12"); //false-no match
re1.test("abbbaabbbaaabbb1");   //false-no match
re1.test("abbbaabbbaaabbb");    //true-match!
所以re1返回的唯一结果是"abbbaabbbaaabbb".记住,点代表任意字符,b也包括在内,因此"abbbaabbbaaa"匹配表达式
.*部分,"bbb"匹配表达式中bbb的部分。
对于第二个表达式re2,匹配过程如下:
re2.test("a"); //false-no match
re2.test("ab"); //false-no match
re2.test("abb"); //false-no match
re2.test("abbb"); //true-match!
//store this result and start with next letter

re2.test("a"); //false-no match
re2.test("aa"); //false-no match
re2.test("aab"); //false-no match
re2.test("aabb"); //false-no match
re2.test("aabbb"); //false-no match!
//store this result and start with next letter

re2.test("a"); //false-no match
re2.test("aa"); //false-no match
re2.test("aaa"); //false-no match
re2.test("aaab"); //false-no match
re2.test("aaabb"); //false-no match
re2.test("aaabbb"); //true match
//store this resutl and start with next letter

re2.test("1"); //false-no match
re2.test("2"); //false-no match
re2.test("3"); //false-no match
re2.test("4"); //false-no match
由于re2包含一个惰性量词,所以它能够按照预期的返回"abbb","aabbb"和"aaabbb".
最后一个正则表达式re3,其实没有返回结果,因为它是支配性的。这里是匹配过程:

re3.test("abbbaabbbaaabbb1234"); //false-no match
注意:
浏览器多支配量词的支持还很不完善。IE和Opera不支持支配量词,如果要用它,就会抛出一个错误。
Mozzilla不会产生错误,但是它会将支配量词看作是贪婪的。
7.3 复杂模式
如前面几节讨论的那样,正则表达式可以表示简单的模式,当然也可以表达复杂的模式。复杂的模式不仅仅由
字符类和量次组成,也可以由分组、反向引用、前瞻和其他一些强大的正则表达式功能组成。这一节将介绍这些概念,
这样就可以方便地使用正则表达式进行复杂的字符串操作。
7.3.1 分组
分作是通过用一系列的括号包围一系列字符、字符类以及量词来使用的。假如,假设想匹配字符串"dogdog".使用目前
获得的知识,可能估计表达式应该类似:
var reDogDog=/dogdog/g;
var reDogDog=/(dog)(2)/g;//表示字符序列"dog"将在一行上连续出现两次.
表达式中的括号的意思是字符序列"dog"将在一行上连续出现两次。但是并不限制在分组后使用花括号,可以使用任意量词:
var re1=/(dog)?/; //match zero or one occurences of "dog"
var re2=/(dog)*/; //match zero or more occurences of "dog"
var re3=/(dog)+/; //match one or more occurences of "dog"
通过混合使用字符、字符类和量词,甚至可以实现一些相当复杂的分组:
var re=/([bd]ab?)*/; //match zero or more occurrences of "ba","da","bad",or "dad"
同时也不介意将分组放在分组中间:
var re=/(mom(and dad)?)/; //match "mom" or "mom and dad"
String.prototype.trim=function(){
var reExtratSpace=/^\s+(.*?)\s+$/;
return this.replace(reExtratSpace,"$1");
};

var sTest="  this si a test   ";
alert(sTest.trim());
7.3.2 反向引用
那么,在表达式计算完后还可以怎样利用分组?每个分组都被存放在一个特殊的地方以备将来使用。
这些存储在分组中的特殊值,我们称之为反向引用(backreference).
反向引用是按照从左到右遇到的左括号字符的顺序进行创建和编号的。例如,表达式(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);//outputs "123456789"
这个例子尝试匹配后面跟着几个或多个数字的镑符号(the pound sign).并对数字进行分组以存储他们。在调用
test()方法后,所有的反向引用都被保存在RegExp构造函数中,从RegExp.$1(它保存了第一个反向引用)开始,
如果还有第二个引用,就是RegExp.$2,如果第三个反向引用存在,就是RegExp.$3,依次类推。因为该组匹配了
"123456789",所以RegExp.$1中就存储了这个字符串。
然后,还可以直接在定义分组的表达式中包含反向引用。这可以通过使用特殊转义字符序列如\1,
\2等等实现.例如:
var sToMatch="dogdog";
var reDogDog=/(dog)\1/;
alert(reDogDog.test(sToMatch));//outputs "true"
正则表达式reDogDog首先创建单词dog的组,然后又被特殊转义字符序列\1引用,使得这个正则表达式等同于
/dogdog/。
第三,方向引用可以用在String对象的replace()方法中,这通过使用特殊字符序列$1,$2等等来实现。描述这种
功能的最佳例子是调换字符串中的两个单词的顺序。假设想将字符串"1234 5678"变成"5678 1234".可以通过下面的代码实现
var sToChange="1234 5678";
var reMatch=/(\d{4}) (\d{4})/;
var sNew=sToChange.replace(reMatch,"$2 $1");
alert(sNew);//outputs "5678 1234"
在这个例子中,正则表达式有两个分组,每一个分组有四个数字。在replace()方法的第二个参数中,$2等同于"5678",
而$1等同于"1234",对应于它们在表达式中出现的顺序。

7.3.3 候选
有时候要构建一个能够正确匹配想得所有肯能性的模式是十分困难的。如果要对同一个表达式同时匹配"red"和"black"时要怎么做呢?
这些单词完全没有相同的字符,这样就要写两个不同的正则表达式,并分别对两字符串进行匹配。
例:
var sToMatch1="red";
var sToMatch2="black";
var reRed=/red/;
var reBlack=/black/;
alert(reRed.test(sToMatch1)|| reBalck.test(sToMatch1));//outputs "true"
alert(reRed.test(sToMatch2)||reBlack.test(sToMatch2));//outputs "true"
在这里,reRedOrBlack匹配"red"或者"black",同时测试每个字符串都返回"true".因为两个备选项存放在一个分组中的,
不管哪个被匹配了,都会存在RegExp.$1中以备将来使用(同时也可以在表达式中使用\1).在第一个测试中,RegExp.$1等于
"red",在第二个中,它等于"blue".
也可以指定任何数目候选项,只要你能想到,只要在之间加入候选操作符:
var sToMatch1="red";
var sToMatch2="black";
var sToMatch3="green";
var reRedOrBlack=/(red|blue|green)/;
alert(reRedOrBlack.test(sToMatch1));//outputs "true"
alert(reRedOrBlack.test(sToMatch2));//outputs "true"
alert(reRedOrBlack.test(sToMatch3));//outputs "true"
OR模式在实践中一种通常的用法是从用户输入中删除不合适的单词,这对于在线论坛来说是非常重要的。通过针对这些
敏感单词使用OR模式和replace()方法,则可以很方便地在帖子发布之前去掉敏感内容:
var reBadWords=/badword|anotherbadword/gi;
var sUserInput="This is a string using badword1 and badword2";
var sFinalTest=sUserInput.replace(reBadWords,"****");
alert(sFinalTest);//outputs "This is a string using ****1 and ****2"
也可以用星号替换敏感词中的每一个字母,也就是说最后出现的文本中星号的数量和敏感词中的字符数量是一样的。
使用函数作为replace()方法的第二个参数,就可以达到这个目的。
var reBadWords=/badword|anotherbadword/gi;
var sUserInput="This is a string using badword1 and badword2";
var sFinalText=sUserInput.replace(reBadWords,function(sMatch){
return sMatch.replace(/./g,"*");
});
alert(sFinalText);//outputs "This is a string using *******1 and *******2"
在这段代码中,作为第二个参数传给replace()方法的函数其实还使用了另外一个正则表达式。当执行函数之后,
sMatch包含了敏感词汇中的一个。最方便快捷的将每个字符替换成星号的方法是,对sMatch使用replace()方法,
指定一个匹配任意字符的模式(也就是句号)并将其替换成一个星号(注意设置全局标志).这种技巧可以保证,不合适
的评论不会出现在你的在线论坛或BBS上.
7.3.4 非捕获性分组
创建反向引用的分组,我们称之为捕获性分组。同时还有一个非捕获性分组,它不会创建反向引用。在较长的正则表达式中,
存储反向引用会降低匹配速度。通过使用非捕获性分组,任然可以拥有与匹配字符串序列同样的能力,而无需存储结果的开销。

如果要创建一个非捕获性分组,只要在左括号的后面加上一个问号和一个紧跟的冒号:
var sToMatch="#123456789";
var reNumbers=/#(?:\d+)/;
reNumbers.test(sToMatch);
alert(RegExp.$1);//outputs ""

例:
var sToMatch="#123456789";
var reNumbers=/#(?:\d+)/;
alert(sToMatch.replace(reNumbers,"abcd$1"));//outputs "abcd$1"
因为该分组是非捕获性的。所以replace()方法不能通过RegExp.$x变量来使用任何反向引用,或在正则表达式中使用它。

去掉文本中的HTML标签
var reTag=/<(?:.|\s)*?>/;
例:
String.prototype.stringHTML=function(){
var reTag=/<(?:.|\s)*?>/gi;
return this.replace(reTag,"");
};

var sTest="<b>This would be bold</b>";
alert(sTest);
alert(sTest.stringHTML());//outputs "This would be bold"
7.3.5 前瞻
有时候,可能希望,当某个特定的字符分组出现在另一个字符串之前时,才去捕获它。如果使用"前瞻"就可以
让这个过程变得十分简单.
前瞻(lookahead)就和它的名字一样,它告诉正则表达式运算器向前看一些字符而不移动其位置。同样存在正
向前瞻和负向前瞻。正向前瞻检查的是接下来出现的是不是某个特定字符集。而负向前瞻则是检查接下来的不应该出现的
特定字符集。
创建正向前瞻要将模式放在(?=和)之间。注意这不是分组,虽然它也用括号。事实上,分组不会考虑前瞻的存在(
无论正向的还是负向的)。考虑下列代码:
var sToMatch1="bedroom";
var sToMatch2="bedding";
var reBed=/(bed(?=room))/;
alert(reBed.test(sToMatch1));//outputs "true"
alert(RegExp.$1);//outputs "bed"
alert(reBed.test(sToMatch2));//outputs "false"
在这个例子中,reBed只匹配后面跟着"room"的"bed".因此,它能匹配sToMatch1而不能匹配sToMatch2.
在用表达式测试sToMatch1后,这段代码输出RegExp.$1的内容是"bed",而不是"bedroom".模式的"room"的部分是
包含在前瞻中的,所以没有作为分组的一部分返回。
天平的另一端是负向前瞻,要创建它,则要将模式放在(?!和)之间。前面得例子可以改成用负向前瞻匹配
"bedding"而不是"bedroom".
var sToMatch1="redroom";
var sToMatch2="bedding";
var reBed=/(bed(?!room))/;
alert(reBed.test(sToMatch1));//outputs "false"
alert(reBed.test(sToMatch2));//outptus "true"
alert(RegExp.$1);//outputs "bed"
注意:
尽管JavaScript支持正则表达式前瞻,但它不支持后瞻。后瞻可以匹配这种模式:"匹配b当且仅当它前面没有a".
7.3.6 边界
边界(bounday)用于正则表达式中表示模式的位置。下表列出了几种可能的边界:
-----------------------------------------------------------------------
边界 描述
-----------------------------------------------------------------------
^ 行开头
$ 行结尾
\b 单词的边界
\B 非单词的边界
-----------------------------------------------------------------------
假设想查找一个单词,但要它只出现在行尾,则可以使用美元符号($)来表示它:
var sToMatch="Important word is the last one.";
var reLastWord=/(\w+)\.$/;
reLastWord.test(sToMatch);
alert(RegExp.$1);//outputs "one"
例子中的正则表达式查找的是,在一行结束之间出现的跟着句号的一个或多个单词字符。当用这个表达式测试sToMatch时,
它返回"one".还可以很容易地获取一行的第一个单词,只要使用脱字符号(^):
var sToMatch="Important word is the last one.";
var reFirstWord=/^(\w+)/;
reFirstWord.test(sToMatch);
alert(RegExp.$1);//outputs "Important"
在这个例子中,正则表达式查找行开始位置后的一个或多个单词字符。如果遇到非单词字符,匹配停止,返回"Important".
这个例子也可以用单词边界实现。
var sToMatch="Important word is the last one.";
var reFirstWord=/^(.+?)\b/;
reFirstWord.test(sToMatch);
alert(RegExp.$1);//outpus "Important"
这里,正则表达式用惰性量词来制定在单词边界之前可以出现任何字符,且可出现一次或多次(如果使用贪婪量词,表达式就
匹配整个字符串).

使用单词边界可以方便地从字符串中抽取单词:
var sToMatch="First second third fourth fifth sixth";
var reWords=/\b(\S+?)\b/g;
var arrWords=sToMatch.match(reWords);
document.write(arrWords.join(","));//outputs "First,second,third,fourth,fifth,sixth"
正则表达式reWords使用单词边界(\b)和非空白字符(\S)从句子中抽取单词。执行完后,arrWords数组将会
包含"First,second,third,fourth,fifth,sixth".注意行的开始和行的结束,通常由^和$表示的位置,对
应地也认为是单词的边界,所以"First"和"sixth"也在结果中。当然这并不是唯一的一种获取句子中所有单词
的方法。
事实上,更加简单的方法是使用单词字符类(\w):
var sToMatch="First second third fourth fifth sixth";
var reWords=/(\w+)/g;
var arrWords=sToMatch.match(reWords);
document.write(arrWords.join(","));//outputs "First,second,third,fourth,fifth,sixth"
7.3.7 多行模式
上节中,学过了行边界的开始和结束的匹配。如果字符串只有单独一行,那么显而易见。但如果字符串中包含
很多行呢?当然可以使用split()方法将字符串分割成行与行的数组,但就得对每一行单独进行正则表达式测试。
var sToMatch="First second\nthrid fourth\nfifth sixth";
var reLastWordOnLine=/(\w+)$/g;
var arrWords=sToMatch.match(reLastWordOnLine);
document.write(arrWords.join(","));//outputs "sixth"
要制定多行模式,只要在正则表达式后面添加一个m选项。这就让$边界匹配换行符(\n)以及字符串真正的结尾。
如果添加了这个选项,前面的例子将返回"second","fourth"和"sixth":
var sToMatch="First second\nthrid fourth\nfifth sixth";
var reLastWordOnLine=/(\w+)$/gm;
var arrWords=sToMatch.match(reLastWordOnLine);
document.write(arrWords.join(","));//outputs "second,fourth,sixth "
多行模式同样也会改变^边界,这时它会匹配换行符之后的位置。例如,前面的例子中,要从字符串中获取字符
"First","third"和"fifth",则可以这么做:
var sToMatch="First second\nthrid fourth\nfifth sixth";
var reLastWordOnLine=/^(\w+)/gm;
var arrWords=sToMatch.match(reLastWordOnLine);
document.write(arrWords.join(","));//outputs "First,thrid,fifth"
7.4 理解RegExp对象
JavaScript中的每个正则表达式都是一个对象,同其他的对象一样。你已经知道正则表达式使用RegExp对象来表示的,
还知道它拥有的方法,但RegExp对象还有一些属性,RegExp实例和构造函数都有属性。两者的属性在创建模式和进行测试的时候
都会发生改变。
7.4.1 实例属性
RegExp的实例有一些开发人员可以使用的属性:
global-------Boolean值,表示g(全局选项)是否已设置。
ignoreCase---Boolean值,表示i(忽略大小写选项)是否已设置。
lastIndex----整数,代表下次匹配将会从哪个字符位置开始(只有当使用exec()或test()函数才会填入,否则为0).
multiline----Boolean值,表示m(多行模式选项)是否已设置.
source------正则表达式的源字符串形式。例如,表达式/[ba]*/的source将返回"[ba]*".
一般不会使用global,ignoreCase,multiline和source属性,因为一般之前就已知道了这些数据:
var reTest=/[ba]*/i;
alert(reTest.global);//outputs "false"
alert(reTest.ignoreCase);//ouputs "true"
alert(reTest.multiline);//outputs "false"
alert(reTest.source);//outputs "[ba]*"
真正有用的属性是lastIndex,它可以告诉你正则表达式在某个字符串中停止之前,查找了多远:
var sToMatch="bbq is short for barbecue";
var reB=/b/g;
reB.exec(sToMatch);
alert(reB.lastIndex);//outputs "1"
reB.exec(sToMatch);
alert(reB.lastIndex);//outputs "2"
reB.exec(sToMatch);
alert(reB.lastIndex);//outputs "18"
reB.exec(sToMatch);
alert(reB.lastIndex);//outputs "21"
在这个例子中,正则表达式reB查找的是b.当它首次检测sToMatch时,它发现在第一个位置--也就是位置0--是b;
因此,lastIndex属性就被置成1,而再次调用exec()时就从这个地方开始执行。再次调用exec(),表达是在位置1又
发现了b,于是将lastIndex设置成为2.如此继续。
如果想从头开始匹配,则可以将lastIndex设为0.
var sToMatch="bbq is short for barbecue";
var reB=/b/g;
reB.exec(sToMatch);
alert(reB.lastIndex);//outputs "1"
reB.lastIndex=0;
reB.exec(sToMatch);
alert(reB.lastIndex);//outputs "1"
7.4.2 静态属性
静态的RegExp属性对所有的正则表达是都有效。这些属性也都与众不同的,因为它们都有两个名字:一个复杂名字和一个以美元符号
开头的简短名字。下表中列出这些属性:
-----------------------------------------------------------------------------------------------------
长名 短名 描述
-----------------------------------------------------------------------------------------------------
input $_ 最后用于匹配的字符串(传递给exec()或test()的字符串)
lastMatch $& 最后匹配的字符
lastParen $+ 最后匹配的分组
leftContext $` 在上次匹配的前面的字串
multiline $* 用于指定是否所有的表达式都是用多行模式的布尔值
rightContext $' 在上次匹配之后的字串
----------------------------------------------------------------------------------------------------
这些属性可以告诉你,关于使用exec()或test()完成的匹配的一些特定信息。例如:
var sToMatch="this has been a short,short summer";
var reShort=/(s)hort/g;
reShort.test(sToMatch);
alert(RegExp.input);//outputs "this has been a short,short summer"
alert(RegExp.leftContext);//outputs "this has been a "
alert(RegExp.rightContext);//outputs ",short summer"
alert(RegExp.lastMatch);//outputs "short"
alert(RegExp.lastParen);//outputs "s"
这一例子描述了几个不同的属性应该如何使用:
input 属性总是等于测试用的字符串。
RegExp.leftContext 包含第一个实例"short"之前的所有字符,同时RegExp.rightContext包含第一个实例"short"
之后的所有字符。
lastMatch的属性包含最后匹配整个正则表达式的字符串,也就是"short".
lastParen属性包含最后匹配的分组,这里就是"s".
也可以使用这些属性的短名字,不过对其中的大部分名字必须使用方括号标记,因为有些名字在ECMAScript的语法中是不合法的。
var sToMatch="this has been a short,short summer";
var reShort=/(s)hort/g;
reShort.test(sToMatch);
alert(RegExp.$_);//outputs "this has been a short,short summer"
alert(RegExp.["$`"]);//outputs "this has been a "
alert(RegExp.["$'"]);//outputs ",short summer"
alert(RegExp.["$&"]);//outputs "short"
alert(RegExp.["$+"]);//outputs "s"
记住每次执行exec()或test()时,所有的属性(除multiline外)都会被重新设置.
而multilinde属性在这里不同于其他属性,因为它不依赖最后一次执行的匹配。相反,它可以设置所有的正则表达式的m选项.
var sToMatch="Fisrt second\nthird fourth\nfifth sixth";
var reLastWordOnLine=/(\w+)$/g;
RegExp.multiline=true;
var arrWords=sToMatch.match(reLastWordOnLine);
alert(arrWords.join(","));//outputs "second,fourth,sixth"
注意: IE和Opera并不支持RegExp.multiline,所以最好单独的对每个表达式设置m选项而不要直接设置则个标记。
7.5 常用模式
验证日期dd/mm/yyyy格式
function isValidDate(sText){
var reDate=/(?:0[1-9]|[12][0-9]|3[01])\/(?:0[1-9]|1[0-2])\/(?:19|20\d{2})/;
return reDate.test(sText);
}

alert(isValidDate("5/5/2004")); //outputs "true"
alert(isValidDate("10/12/2004")); //outputs "true"
alert(isValidDate("6/13/2004")); //outputs "false"
7.5.2 验证信用卡号
MasterCard的信用卡号:
MasterCard必须包含16位数字。在这16个数字中,前两个数字必须是51-55之间的数字,并且在每四个数码之间可以有
空格或短横.例如 5555-5555-5555-5555

function isValidMasterCard(sText){
var reMasterCard=/^(5[1-5]\d{2})[\s\-]?(\d{4})[\s\-]?(\d{4})[\s\-]?(\d{4})$/;
if(reMasterCard.test(sText)){
var sCardNum=RegExp.$1+RegExp.$2+RegExp.$3+RegExp.$4;
return sCardNum;
}else{
return false;
}
}

Luhn算法有四步.第一步是从卡号的最后一个数字开始,并逆向地逐个将奇数位置(1、3等等)的数字相加。要判断当前
的这个数字是不是在偶数位置上,可以使用Boolean标志(叫做bIsOdd).起始时,标记为true,应为最后一个位置是15.
在一个单独的函数中定义luhn算法是很有效的,因为这样其他的函数就可以很方便地调用它:
function luhnCheckSum(sCardNum){
var iOddSum=0;
var bIsOdd=true;
for(var i=sCardNum.length-1;i>=0;i--){
var iNum=parseInt(sCardNum.charAt(i));
if(bIsOdd){
iOddSum+=iNum
}
bIsOdd=!bIsOdd;
}
}

下一步是将偶数位置的数字相加,但这个有些麻烦事:必须先将数字乘以2,然后,如果结果是两位数,必须将两个位上
的数相加,然后将结果加入到总和中。这话有点唠叨,那么考虑一下信用卡号"5432-1234-5678-9012".现在已经将奇数位置的
数字相加,等于4+2+2+4+6+8+0+2=28.下面这一步,要将偶数位置的数字乘以2,就是5,3,1,3,5,7,9,1都要乘以2,得
10,6,2,6,10,14,18,2。因为10,10,14,18都是有两位的,必须将这几个数字两位相加,最后得到1,6,2,6,1,5,9,2.
并将结果存储,得32.
将这个算法加入到代码中,得到:
function luhnCheckSum(sCardNum){
var iOddSum=0;
var iEvenSum=0;
var bIsOdd=true;
for(var i=sCardNum.length-1;i>=0;i--){
var iNum=parseInt(sCardNum.charAt(i));
if(bIsOdd){
iOddSum+=iNum
}else{
iNum=iNum*2;
if(iNum>9){
iNum=eval(iNum.toString().split("").join("+"));
}
iEvenSum+=iNum;
}
bIsOdd=!bIsOdd;
}
}
将else语句加入到if(bIsOdd)中就完成了偶数位置相加的任务。如果数字大于9(表示这个数有两位),就对数字进行转化:
1.用toString()方法将数字转化成子符串。
2.用split()将字符串分割成两个数字的数组。
3.用join()并用加号将数组组合起来
4.将最后的结果字符串传给eval(),它可以将字符串翻译为代码并执行。
现在,最后一步是将两个总和(从奇数部分和偶数部分得来的)相加并对结果进行取模操作,如果数字是合法的,那么结果应该
可以被10整除。
function luhnCheckSum(sCardNum){
var iOddSum=0;
var iEvenSum=0;
var bIsOdd=true;
for(var i=sCardNum.length-1;i>=0;i--){
var iNum=parseInt(sCardNum.charAt(i));
if(bIsOdd){
iOddSum+=iNum
}else{
iNum=iNum*2;
if(iNum>9){
iNum=eval(iNum.toString().split("").join("+"));
}
iEvenSum+=iNum;
}
bIsOdd=!bIsOdd;
}
return ((iEvenSum+iOddSum)%10==0);
}
//把这个方法加入到isValidMasterCard()中,就完成了验证:
function isValidMasterCard(sText){
var reMasterCard=/^(5[1-5]\d{2})[\s\-]?(\d{4})[\s\-]?(\d{4})[\s\-]?(\d{4})$/;
if(reMasterCard.test(sText)){
var sCardNum=RegExp.$1+RegExp.$2+RegExp.$3+RegExp.$4;
return luhnCheckSum(sCardNum);
}else{
return false;
}
}
现在可以用这样的方法传入MasterCard号:
alert(isValidMasterCard("5432 1234 5678 9012"));//outputs "true"
至于其他类的信用卡号,则必须了解管理这些卡号的规则.
Visa卡号可能有13位或16位。且首位数字总是为4,因此,匹配Visa卡号(不带空格)的模式应该是:
var reVisa=/^(4\d{12}(?:\d{3})?)$/;
关于这个模式还有一些注意事项:
1.一个非捕获性分组包含卡号最后三位数字,是应为这三个数字单独使用的不多。
2.在最后的非捕获性分组后的问号,表示要么出现三个数字,要么就没有数字。
有了完整的正则表达式,只要抽取数字并将其应用于Luhn算法:
function isValidVisa(sText){
var reVisa=/^(4\d{12}(?:\d{3})?)$/;
if(reVisa.test(sText)){
return luhnCheckSum(RegExp.$1);
}else{
return false;
}
}
注意: 关于更多信用卡号模式和使用Luhn算法的信息,可以查看http://www.beachnet.com/~hstiles/cardtype.html
7.5.3 验证电子邮件地址
function isValidEmail(sText){
var reEmail=/^(?:\w+\.?)*\w+@(?:\w+\.?)*\w+$/;
return reEmail.test(sText);
}

alert(isValidEmail("john.doe@somewhere.com"));//outputs "true"
alert(isValidEmail("john.doe@somewhere.com"));//outputs "false"
分享到:
评论

相关推荐

    正则表达式基础学习笔记

    不同的编程语言和环境可能使用不同的正则表达式引擎,如Perl兼容正则表达式(PCRE)、JavaScript的正则表达式等,它们在语法细节和功能上可能存在差异。 七、实例应用 1. 邮箱验证:`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-...

    JS正则表达式入门笔记实例

    这篇入门笔记实例将带你深入了解正则表达式的使用。 1. **正则表达式基础** - **模式定义**:正则表达式由特殊字符(元字符)和普通字符组成,用于描述文本模式。 - **创建方式**:可以使用`/pattern/flags`或`...

    Javascript正则表达式校验共7页.pdf.zip

    在这个名为“Javascript正则表达式校验共7页.pdf.zip”的压缩包中,很可能是提供了一份详细的教程或笔记,涵盖了JavaScript正则表达式的基础到高级用法,共计7页内容。 正则表达式(Regular Expression)是由字符、...

    正则表达式30.zip

    这份教程可能涵盖了30个关键概念或技巧,帮助学习者深入理解和掌握正则表达式。 1. **基础符号**:正则表达式的核心是基础符号,如`.`代表任意字符,`*`表示前面的字符可以出现零次或多次,`+`表示至少一次,`?`...

    正则表达式的笔记记录呢

    正则表达式是一个深奥且强大的工具,学习和理解其原理和技巧对于处理文本数据至关重要。熟练掌握正则表达式可以大大提高编程效率和代码质量。通过不断练习和使用,你可以更好地理解和运用这些概念来解决各种文本处理...

    正则表达式及常用匹配表达式

    ### 正则表达式及常用匹配表达式 ...通过以上内容的学习,我们可以了解到正则表达式不仅可以用于简单的文本搜索,还可以实现复杂的模式匹配和文本提取。这些技巧和例子对于日常开发工作中处理各种文本数据非常有用。

    小菜升级笔记之手撸正则表达式——Python & JavaScript

    最后,学习正则表达式需要时间和实践。理解元字符、边界定位符、预查以及如何在Python和JavaScript中使用这些工具,是提升数据处理能力的关键。通过不断地练习和应用,你可以逐步掌握这项强大的技能。

    javascript学习笔记

    ### JavaScript 学习笔记 #### 一、JavaScript 概述 **JavaScript** 是一种轻量级的编程语言,主要用于网页的交互式开发。它是一种解释型的语言,可以在客户端(通常是浏览器)运行,也可以在服务器端运行(如 ...

    javascript学习笔记2

    ### JavaScript 学习笔记2:字符串与事件对象总结 #### 字符串操作方法总结 在JavaScript中,字符串是一系列字符的有序集合。JavaScript提供了一系列内置的方法来帮助我们处理字符串,这些方法可以用来检索、替换...

    Python第四章.zip

    在本压缩包“Python第四章.zip”中,包含的是关于Python编程语言第四章的学习资料,主要聚焦于字符串处理和正则表达式这两个重要主题。学习这些内容对于掌握Python的高级特性,尤其是数据处理和文本分析方面,至关...

    js正则案例及笔记.rar

    通过学习JavaScript正则表达式,你可以更高效地处理文本数据,比如验证用户输入、提取特定信息、替换文本等。结合笔记和示例代码(JavaScript正则、note),你可以深入理解并熟练运用这些概念和技巧。在实际开发中,...

    Javascript权威指南学习笔记一

    JavaScript权威指南的学习笔记为我们深入理解这门语言的基础和特性提供了宝贵的资源。在第一部分中,主要探讨了JavaScript的核心概念和基本语法。 首先,JavaScript有两个主要的解释器:C语言编写的SpiderMonkey和...

    JavaScript忍者秘籍(第2版)笔记、习题与解析

    7. **正则表达式**:正则表达式在JavaScript中用于文本匹配和替换,书中详细介绍了各种正则语法,如字符类、量词、分组、预查等。 8. **错误处理**:JavaScript中的异常处理通过`try...catch...finally`语句实现。...

    韩顺平javascript课堂笔记完整版

    理解这些基础知识是掌握JavaScript的第一步,随着学习的深入,还将涉及DOM操作、事件处理、函数、对象、数组、正则表达式、闭包、异步编程等更多概念。JavaScript的灵活性和广泛应用使其成为Web开发不可或缺的一部分...

    Ruffy javascript 学习笔记

    ### Ruffy JavaScript 学习笔记知识点详解 #### 1. JavaScript 的原型模型 JavaScript 的原型模型是实现面向对象编程的基础之一。在这个模型中,构造函数用于定义对象的成员,而方法则是通过构造函数的 `prototype...

    Javascript-the-Good-Parts-notes, 关于seminal的优秀部分,注释.zip

    通过学习《JavaScript的精华部分笔记》,开发者可以深入了解JavaScript的核心概念,提高编程技能,并更好地利用这门语言来构建高效、健壮的Web应用。这份开源资料为开发者提供了丰富的注解和实例,有助于理解和应用...

    ES6 学习笔记.pdf

    ### ES6 学习笔记知识点详解 #### 一、ECMAScript 概述 - **ECMA**(European Computer Manufacturers Association):欧洲计算机制造商协会,后更名为 Ecma International,主要致力于评估、开发和认可电信及...

    java基础复习笔记(第一阶段)

    Java基础复习笔记(第一阶段)涵盖了Java编程语言的核心概念,为初学者或复习者提供了全面的知识框架。在这一阶段的学习中,我们将深入探讨以下几个关键领域: 1. **Java基本语法**:这是学习Java的基石,包括变量...

Global site tag (gtag.js) - Google Analytics