定義
在javascript我們可以通過(guò)內(nèi)建的類來(lái)定義一個(gè)正則表達(dá)式。
1 | var reName = new RegExp( "nowamagic" ); |
實(shí)際上RegExp類的構(gòu)造函數(shù)可以接受兩個(gè)參數(shù),除了本身需要匹配的模式字符串外,還可以定義指定額外處理方式的第二個(gè)參數(shù)。
1 | var reName = new RegExp( "nowamagic" , "i" ); //忽略大小寫(xiě) |
我很好奇輸出reName會(huì)得到什么結(jié)果呢?于是:
得到結(jié)果:/nowamagic/i,于是我們得到j(luò)avascript中正則表達(dá)式的第二種定義方法(perl風(fēng)格):
1 | var reName = /nowamagic/; |
那第二個(gè)參數(shù)呢?當(dāng)然,同樣可以為其指定第二個(gè)參數(shù):
1 | var reName = /nowamagic/i; |
這兩種定義方式都是可行的,完全可以根據(jù)個(gè)人習(xí)慣進(jìn)行選擇。就像可以使用var s = new String(“for a simple life”);定義字符串的同時(shí)還可以使用var s = “for a simple life”;來(lái)定義是完全相同的。建議使用perl風(fēng)格的寫(xiě)法,除了簡(jiǎn)潔外,還省去了使用RegExp構(gòu)造函數(shù)定義時(shí)需要對(duì)“\”轉(zhuǎn)義的麻煩。
如果要匹配字符“\”,perl風(fēng)格的寫(xiě)法是:
而構(gòu)造函數(shù)的寫(xiě)法則需要對(duì)兩個(gè)“\”都進(jìn)行轉(zhuǎn)義:
1 | var res = new RegExp( "\\\\" ); |
感覺(jué)上是不是就麻煩了很多?
記住,在一個(gè)完整的正則表達(dá)式中“\”后面總是跟著另外一個(gè)字符。
javascript中的正則表達(dá)式
其實(shí)上面已經(jīng)在開(kāi)始講了javascript對(duì)正則表達(dá)式的實(shí)現(xiàn)方式了,只定義了正則表達(dá)式,但是如何在javascript中真正使用正則表達(dá)式呢?在javascript中RegExp和String對(duì)象都有處理正則表達(dá)式的方法。
- test -- RegExp的test方法用來(lái)測(cè)試字符串是否匹配給出的匹配模式,返回布爾值;
- exec -- RegExp的exec方法返回包含第一個(gè)匹配的的數(shù)組或null;
- match -- String的match方法返回包含所有匹配子字符串的數(shù)組;
- replace -- String的replace方法完成string的替換操作,支持正則表達(dá)式;
- search -- 與String的indexof方法類似,不同的是search支持正則表達(dá)式,而不僅僅是字符串;
- split -- 按照一定規(guī)則拆分字符串并將子字符串存儲(chǔ)到數(shù)組中的String方法。
關(guān)于這些函數(shù)的具體使用方法,可以參閱JS的相關(guān)函數(shù)手冊(cè)。
一個(gè)實(shí)例對(duì)象除了方法當(dāng)然還有屬性,一個(gè)正則表達(dá)式有以下屬性:
- global -- 布爾值,若全局選項(xiàng)g已設(shè)置則返回true,否則返回false;
- ignoreCase -- 布爾值,若忽略大小寫(xiě)選項(xiàng)i已設(shè)置則返回true,否則返回false;
- lastIndex -- 整數(shù),使用exec或test方法時(shí)被填入,表示下次匹配將會(huì)從哪個(gè)字符位置開(kāi)始;
- multiline -- 布爾值,表示多行模式選項(xiàng)m是否設(shè)置,若設(shè)置則返回true,否則返回false;
- source -- 正則表達(dá)式的元字符串形式。/\\/的source將返回”\\“。
元字符
在正則表達(dá)式中有一些特殊的字符符號(hào)我們是不能直接使用的,必須對(duì)其進(jìn)行轉(zhuǎn)義后才能使用。如“\”,因?yàn)檫@些字符在正則表達(dá)式中有特殊的語(yǔ)法含義,這類字符被稱為元字符,正則表達(dá)式中的元字符有:
1 | .,\,/,*,?,+,[,(,),],{,},^,$,| |
可能不太好記憶,當(dāng)無(wú)法確定某個(gè)字符是否是元字符的時(shí)候就勇敢的對(duì)其進(jìn)行轉(zhuǎn)義是沒(méi)有錯(cuò)的,對(duì)不是元字符的字符進(jìn)行轉(zhuǎn)義是不會(huì)出什么問(wèn)題的,但是如果不對(duì)元字符轉(zhuǎn)義就會(huì)有意想不到的錯(cuò)誤產(chǎn)生了。
分組匹配
一個(gè)簡(jiǎn)單的字符就可以是一個(gè)匹配模式,但是現(xiàn)實(shí)情況往往不會(huì)這么簡(jiǎn)單。比如我們要匹配一個(gè)0-9的數(shù)字:
這個(gè)正則表達(dá)式要如何書(shū)寫(xiě)才能同時(shí)匹配這兩個(gè)數(shù)字呢?簡(jiǎn)單的字符表達(dá)式當(dāng)然無(wú)法完成了,這個(gè)時(shí)候我們就可以為0-9十個(gè)數(shù)字來(lái)定義一個(gè)字符集合(字符類)來(lái)進(jìn)行匹配。
1 | var reNum = /[0123456789]/; |
2 | document.write(reNum.test(i)); //true |
3 | document.write(reNum.test(j)); //true |
使用test方法測(cè)試匹配結(jié)果都輸出了true。
范圍匹配
上一個(gè)例子使用了分組匹配,但是如果要匹配所有26個(gè)英文字母,還要包括大小寫(xiě),仍然可以使用分組匹配:
1 | var reLetter = /abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ/; |
恩,這個(gè)正則表達(dá)式是完全正確的,但是是不是感覺(jué)太長(zhǎng)了,有沒(méi)有辦法讓它更為簡(jiǎn)潔一點(diǎn)?當(dāng)然是有的,為字符或數(shù)字指定一個(gè)匹配范圍就可以了。
2 | var reLetter = /[a-zA-Z]/; |
這樣就可以了,“-”用來(lái)定義一個(gè)匹配區(qū)間,字符的具體順序由ASCII字符表確定,所以不能寫(xiě)成/A-z/,因?yàn)閆-a之間還包含著其他字符。
取非匹配
很多編程語(yǔ)言中都使用“!”取非操作,包括javascript。正則表達(dá)式中也有取非操作,比如/[^0-9]/就是一個(gè)取非操作的正則表達(dá)式了。
4 | document.write(rec.test(i)); //false |
5 | document.write(rec.test(s)); //true |
符號(hào)^用來(lái)完成取非操作,同時(shí)^0-9也是必須包含在[]中的,因?yàn)閊其實(shí)還有另外一種特殊用途。
特殊字符
可能你覺(jué)得/[a-zA-Z]/,/[0-9]/還是不夠簡(jiǎn)潔,的確,在正則表達(dá)式中一些特定的字符集合可以使用一些特殊的元字符來(lái)代替。這些特殊的字符并不是必不可少的,但是卻可以給我們帶來(lái)不少方便。/[0-9]/就完全可以寫(xiě)成這樣:
那大小寫(xiě)字母字符類呢?很遺憾,除了POSIX字符類(javascript不支持POSIX字符類)中有支持大小寫(xiě)字母的特殊字符類外并沒(méi)有專門替代方法。
常見(jiàn)的特殊字符有:
- \d 任何一個(gè)數(shù)字字符,等價(jià)于[0-9]
- \D 任何一個(gè)非數(shù)字字符,等價(jià)于[^0-9]
- \w 任何一個(gè)字母數(shù)字或下劃線字符,等價(jià)于[a-zA-Z_]
- \W 任何一個(gè)非字母數(shù)字和下劃線字符,等價(jià)于[^a-zA-Z_]
- \s 任何一個(gè)空白字符,包括換頁(yè)符、換行符、回車符、制表符和垂直制表符,等價(jià)于[\f\n\r\t\v]
- \S 任何一個(gè)非空白字符,等價(jià)于[^\f\n\r\t\v]
- . 換行和回車以外的任何單個(gè)字符,等價(jià)于[^\n\r]
相同字母大小寫(xiě)總是進(jìn)行取非操作的。
十六進(jìn)制和八進(jìn)制字符
在正則表達(dá)式中使用十六進(jìn)制或八進(jìn)制字符也是完全可行的,他們所匹配的字符即是由其轉(zhuǎn)換成十進(jìn)制后的數(shù)值在ASCII中所對(duì)應(yīng)的字符。
1 | var reAt = /\x40/; //十六進(jìn)制字符\x40(64)對(duì)應(yīng)字符“@” |
2 | var reA = /\0101/; //八進(jìn)制字符\0101(65)對(duì)應(yīng)字符“A” |
重復(fù)匹配
以匹配一個(gè)email地址為例,mymail@mail.com這樣的一個(gè)email地址必須包括一個(gè)合法的用戶名mymail,@符號(hào)以及一個(gè)合法的域。其中用戶名和域名的字符個(gè)數(shù)都是無(wú)法判斷的,但是有一點(diǎn)是肯定的——用戶名必須至少是一個(gè)字符,域名至少是兩個(gè)字符中間還必須有一個(gè)點(diǎn)號(hào)。于是我們可以這樣做:
1 | var reMail = /\w+@\w+\.\w+/i; |
2 | var email = "mymail@mail.com" ; |
3 | document.write(reMail.test(email)); //true |
“+”表示字符出現(xiàn)一次或多次,至少出現(xiàn)一次。這個(gè)正則表達(dá)式其實(shí)并不能匹配所有合法的email地址,后面我們繼續(xù)完善。
除了“+”可以指定至少匹配一次外,還有很多其他的可以指定匹配次數(shù)的方式。
- ? 出現(xiàn)零次或一次,最多一次
- * 出現(xiàn)任意次(零次、一次、多次)
- + 出現(xiàn)一次或多次,至少一次
- {n} 能且只能出現(xiàn)n次
- {n,m} 至少出現(xiàn)n次,最多出現(xiàn)m次
www.gogle.com,www.google.com,www.gooogle.com這三個(gè)網(wǎng)址都能正確地打開(kāi)google的首頁(yè),于是就可以用{n,m}匹配其中的1個(gè),2個(gè)或3個(gè)字母”o”。
1 | var gogle = "www.gogle.com" ; |
2 | var google = "www.google.com" ; |
3 | var gooogle = "www.gooogle.com" ; |
4 | var reGoogle = /w{3}\.go{1,3}gle\.com/i; |
5 | document.write(reGoogle.test(gogle)); //true |
6 | document.write(reGoogle.test(google)); //true |
7 | document.write(reGoogle.test(gooogle)); //true |
在上面的正則表達(dá)式中,我們使用了{(lán)3}來(lái)制定字符“w”能且只
能出現(xiàn)3次,用{1,3}來(lái)制定字母“o”可以出現(xiàn)1到3次。
防止過(guò)度匹配
有這樣一段HTML文本:
1 | var html = "<em>nowamagic</em>for a simple life<em>http:///</em>" ; |
如果現(xiàn)在要講<em></em>及其中間的文本匹配出來(lái),正則表達(dá)式可以這樣寫(xiě):
1 | var reEm1 = /<em>.*<\/em>/gi; |
2 | document.write(html.match(reEm1)); //"<em>nowamagic</em>for a simple life<em>http:///</em>" |
3 | var reEm2 = /<em>.*?<\/em>/gi; |
4 | document.write(html.match(reEm2)); //<em>nowamagic</em>,<em>http:///</em> |
當(dāng)使用貪婪模式的時(shí)候,”.*”會(huì)最大程度地進(jìn)行字符匹配,所以輸出了整個(gè)字符串。而在惰性模式中,”.*?”只進(jìn)行最小限度的匹配,所以完整的輸出了我們需要的字符串。
惰性模式的語(yǔ)法很簡(jiǎn)單,即是在貪婪模式后面加上一個(gè)“?”即可。
- * –> *?
- + –> +?
- {n,} –> {n,}?
位置匹配
1 | var s = “_Don’t do it!”; |
如何將單詞“do”匹配出來(lái)?it’s easy!
2 | document.write(s.match(reDo)); //Do,do |
但是這個(gè)簡(jiǎn)單的正則表達(dá)式/do/gi將“don’t”中的“do”也進(jìn)行了匹配,可這并不是想要的結(jié)果。而在正則表達(dá)式中有專門用來(lái)進(jìn)行單詞邊界匹配的限定符”\b“。
2 | document.write(s.match(reDo)); //do |
“\b”到底匹配的什么呢?”\b”匹配的是一個(gè)位置,一個(gè)位于”\w“(字母,數(shù)字,下劃線)和”\W“之間的位置。
既然有”\b”,那有”\B”嗎?當(dāng)然,他和”\b“剛好相反,由來(lái)匹配一個(gè)不是單詞邊界的位置。比如上例中匹配”don’t”中的”do”時(shí)”\B”就可派上用場(chǎng)。
2 | document.write(s.match(reDo)); //Do |
在介紹取非匹配的時(shí)候介紹^只用位于[]并緊跟[方能取非匹配,而^還有另外一種用途——字符串邊界匹配。
- ^ 用來(lái)匹配字符串開(kāi)頭
- $ 用來(lái)匹配字符串結(jié)尾
比如我們要匹配一個(gè)http://形式的net域名:
2 | var reUrl = /^(http):\/\/nowamagic\.(net)$/gi; |
3 | document.write(reUrl.test(url)); //true |
正則表達(dá)式reUrl限制url必須以”http”開(kāi)頭,以”net”結(jié)尾。
又如經(jīng)常被擴(kuò)展的string方法trim:
2 | return s.replace(/(^\s*)|(\s*$)/g, "" ); |
同時(shí)我們可以在整個(gè)模式的最前面使用(?m)來(lái)啟用分行匹配模式。這樣,^不但匹配正常的字符串開(kāi)頭,還將匹配行分隔符(換行符)后面的開(kāi)始位置;$不僅匹配正常的字符串結(jié)尾,還將匹配行分隔符(換行符)后面的結(jié)束位置。 延伸閱讀此文章所在專題列表如下: - 什么是正則表達(dá)式?
- 正則入門:匹配固定的單個(gè)字符
- 正則入門:匹配任意的單個(gè)字符
- 正則入門:字符組的使用
- 正則入門:在字符組中使用字符區(qū)間
- 正則入門:反義字符組的使用
- 正則入門:匹配空字符
- 正則入門:匹配一個(gè)或多個(gè)字符
- 正則入門:匹配零個(gè)或多個(gè)字符
- 正則入門:匹配零個(gè)或一個(gè)字符串
- 正則入門:匹配固定數(shù)目的字符
- 正則入門:匹配區(qū)間內(nèi)數(shù)目的字符
- 正則入門:貪婪匹配
- 正則入門:惰性匹配
- 正則入門:兩個(gè)匹配模式
- 正則入門:匹配單詞邊界
- 正則入門:邊界的定義與相對(duì)性
- 正則入門:匹配非單詞邊界
- 正則入門:匹配文本首和尾
- 正則入門:子模式
- 正則入門:“或”匹配
- 正則入門:后向引用文本替換
- 正則入門:非獲取匹配
- 正則總結(jié):JavaScript中的正則表達(dá)式
- 正則總結(jié):正則表達(dá)式在js中的高級(jí)應(yīng)用
|