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