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