雖然不少web開發者在忽視正則表達式後,還能夠順利工做,但在javascript中還存在一些問題,若是不用正則表達式,是沒辦法進行很好的解決的.javascript
固然,也許還有其餘的方式可以解決相同的問題.但一般,用一句正確的正則表達式頗有可能就能夠省略半屏幕的代碼.java
假設咱們要驗證一個字符串是否爲格式正確的手機號碼.手機號碼開頭都是1,而後後面都是數字,而後長度都爲11位
讓咱們建立一個函數,對一個輸入內容進行驗證web
function isPhoneNumber(candidate){ if(typeof candidate !== "string" || candidate.length!=11){ return false; //過濾明顯不符合條件的輸入內容 } if(isNaN(candidate)){ return false; //過濾非純數字的輸入內容 } return true; }
雖然這段代碼實現的很合理,檢查了輸入內容的類型和長度,可是對於一個簡單的手機號碼檢查,看起來依然有太多的代碼
如今考慮這樣一種方式正則表達式
function isPhoneNumber(candidate){ return /^1[0-9]{10}$/.test(candidate) }
除了函數體內的一些深奧的語法之外,這種方式看起來更簡潔,更優雅,不是嗎?
這就是正則表達式的威力,並且這只是它冰山的一角.若是語法看起來像鍵盤上爬行的蜥蜴的話,也不要擔憂.express
讓咱們開始對正則表達式進行深挖,要學習正則表達式,就先了解正則表達式出現的由來數組
"正則表達式(regular expression)" 這個詞源於中世紀的數學,當時,一個名叫Stephen Kleene的數學家,使用了一個名爲"正則集合"的數學符號描述自動計算模式.但這不會幫助咱們瞭解任何關於正則表達式的內容,那麼讓咱們把它簡單化,正則表達式一般被稱爲一個模式(pattern),是一個用簡單方式描述或者匹配一系列符合某個語法規則的字符串.表達式自己包含了容許定義這些模式的術語和操做符.咱們很快就會看到這些術語和操做符.瀏覽器
在 javascript 中,與大多數其餘對象類型同樣,有兩種方式能夠建立正則表達式:經過正則表達式字面量,或者經過構造 RegExp 對象的實例.函數
例如,若是要建立一個正則表達式 (或簡稱爲正則(regex)),用於精確匹配字符串 "test" ,可使用正則字面量:學習
var pattern = /test/;
正斜槓可能看起來有點奇怪,可是就像字符串使用引號進行界定的同樣,正則字面量是用正斜槓進行界定的.測試
或者,咱們能夠構造一個 RegExp 實例,將正則做爲字符串傳入:
var pattern = new RexExp('test');
這兩種格式在pattern變量中建立的正則表達式都是同樣的.
在開發過程當中,若是正則是已知的,則優先選擇字面量語法,而構造器方式則是用於在運行時,經過動態建立字符串來構建正則表達式.
字面量語法優先於用字符串構建正則表達式的其中一個緣由是反斜槓字符在正則表達式中發揮重要的做用(很快就能看到).但因爲反斜槓字符在普通字符串中也是一個轉義字符,因此,若是要在字符串內表示反斜槓,咱們就要使用 \\
(兩個反斜槓).這回讓原本語法就很神祕的正則表達式變得更加怪異了.
除了表達自己,還有三個標誌能夠與正則表達式進行關聯.
i
讓正則表達式不區分大小寫,因此/test/i
不只能夠匹配 "test" ,還能夠匹配 "Test" "TEST" "tEsT" 等.
g
匹配模式中的全部實例,而不是默認的只匹配第一次出現的結果
m
容許匹配多個行,好比能夠匹配文本區元素(textarea)中的字
這些標誌將附加到字面量尾部(例如: /test/ig
) 或者做爲 RegExp 構造器的第二個字符參數 (new RegExp("test","ig")
) .
方法 | 描述 |
---|---|
compile | 編譯正則表達式。 |
exec | 檢索字符串中指定的值。返回找到的值,並肯定其位置。 |
test | 檢索字符串中指定的值。返回 true 或 false。 |
例子:
須要注意的是String對象也有個match方法,也能夠檢索目標字符串,用法和RegExp的exec方法相似,區別是
exec是正則表達式的方法,而不是字符串的方法,它的參數纔是字符串
exec和match返回的都是數組,可是match是返回全部匹配的字符串合成的數組,可是正則表達式必須指定全局g屬性才能返回全部匹配,不指定g屬性則會返回一個只有一個元素的數組。exec永遠返回與第一個匹配相關的信息,其返回數組包括第一個匹配的字串,全部分組的反向引用。
參考資料:http://www.jb51.net/article/46374.htm
正則表達式,就像咱們熟悉的大多數其餘表達式同樣,由術語和驗證這些術語的操做符組成.在接下來的小節中,咱們將瞭解這些術語和操做符,看看它們是如何用於表達模式的.
若是一個字符不是特殊字符或操做符(後續會進行介紹),則表示該字符必須在表達式中出現.例如,在/test/
正則中,有4個術語,它們表示這些字符必須在一個字符串中出現,才能匹配該模式.
一個接一個的字符,隱式表達了"後面跟着(followed by)"這樣一個操做.因此,/test/
的意思是說, "t" 後面跟着 "e","e" 後面跟着 "s","s" 後面又跟着 "t".
不少時候,咱們並不像匹配一個特定的字符,而是想匹配一個有限字符集中的某一個字符.咱們能夠經過將字符集放到中括號內,來指定該字符集操做符(也被稱爲字符類(character class)操做符): [abc]
上述示例,是說咱們要匹配 "a" "b" "c" 中的任何一個字符.
有的時候,咱們想要匹配一組有限字符集之外的字符.能夠經過在中括號第一個開括號後面加一個插入符(^)來實現,好比:
[^abc]
其意義將改變爲:除了 "a" "b" 或 "c" 之外的任意字符.
在字符集操做方面,還有一個更加劇要的變異操做: 指定一個範圍. 例如,若是要匹配 "a" 和 "m" 之間的任何一個小寫字母,咱們能夠這樣寫: [abcdefghijklm]
但能夠寫成更加簡潔的 [a-m]
中橫線表示從 "a" 到 "m" 之間的全部字符 (包含 a 和 m,按字典順序)都在該字符集內.
並非全部的字符和其字符字面量都是等價的.固然,全部的字母和十進制數字字符都能表明本身,可是,咱們很快就會發現.像$ 和點(.) 這樣的特殊字符,表示的是他們自身之外的東西,或者表示爲驗證術語的操做符. 事實上,咱們已經看到了如何用[、]、-、^
字符表示它們自身之外的東西
若是咱們須要匹配[、]、$、^或其餘這樣的特殊字符,該怎麼辦?在正則裏,使用反斜槓能夠對任意字符進行轉義,讓被轉義字符做爲字符自己進行匹配.因此,\[
表示要匹配[
字符.兩個反斜槓(\\
)則匹配一個反斜槓
咱們可能常常須要確保模式匹配一個字符串的開始,或者一個字符串的結束.插入符號(^),若是做爲正則表達式的第一個字符,則表示要從字符串的開頭進行匹配,這樣/^test/
就只能匹配以 "test" 開頭的字符串了.(注意,這只是&字符的一個重載,它還能夠用於否認一個字符類集,好比 /[^abc]/
用於選擇除了除了 "a" "b" 或 "c" 之外的任意字符)
相似的,美圓符號$表示該模式必須出如今字符串的結尾:/test$/
.
同時使用^和$則代表指定的模式必須包含整個候選字符串.
若是要匹配連續的4個 "a" 字符,能夠用/aaaa/
來表示,但若是咱們想匹配任意數量的相同字符串呢?
在重複選項上,正則表達式提供了不少方式.
在一個字符後面加一個問號( ? ),能夠定義爲該字符是可選的(也就是,能夠出現一次,或根本不出現)
若是一個字符要出現一次或屢次,可使用加號(+),例如/t+est/
能夠匹配"test","ttest","tttest",而不能匹配"est"
若是一個字符要出現零次或屢次,可使用星號( * ),例如/t*est/
能夠匹配"test","ttest","tttest",以及"est"
也能夠在字符後面的花括號裏指定一個數字來表示重複次數,例如,/a{4}/
表示匹配含有連續四個"a"字符的字符串
也能夠在字符後面的花括號裏指定兩個數字(用逗號隔開)來表示重複次數區間,例如,/a{4,10}/
表示匹配任何含有連續4個至10個 "a" 字符的字符串.
次數區間的第二個值是可選的(可是要保留逗號),其表示一個開區間.例如,/a{4,}/
表示匹配任何含有連續4個或多於4個 "a" 字符的字符串.
這些重複操做符能夠是貪婪的(greedy)或非貪婪的(nongreedy).默認狀況下,它們是貪婪的:它們匹配全部的字符組合.在操做符後面加一個問號?字符(?操做符的一個重載),如 a+?,可讓該表達式變成非貪婪的:進行最小限度的匹配
舉個例子,若是咱們對字符串 "aaa" 進行匹配,正則表達式 /a+/
將匹配全部這三個字符,而非貪婪表達式/a+?/
則只匹配一個 "a" 字符,由於一個a字符就能夠符合a+術語.
有一些咱們想匹配的字符,是不可能用字面量字符來表示的(如像回車的控制字符),還有一些咱們可能常常想匹配的字符類,好比小數位數或一組空白字符.正則表達式語法提供了不少表示這些字符或經常使用類的預約義術語,這樣在正則表達式中,咱們就可使用這些控制字符進行匹配了,所以,咱們不須要再去依靠經常使用的字符集.
下面列出了這些術語以及它們表示的字符或字符集.
預約義術語 | 匹配內容 |
---|---|
\t |
水平製表符 |
\b |
空格 |
\v |
垂直製表符 |
\f |
換頁符 |
\r |
回車 |
\n |
換行符 |
\cA: \cZ |
控制符,例如\cM 匹配一個Control-M |
\x0000:\xFFFF |
十六進制Unicode碼 |
\x00:\xFF |
十六進制ASCII碼 |
. |
匹配除了新行(\n )以外的任意字符 |
\d |
匹配任意數字,等價於[0-9] |
\D |
匹配任意非數字,等價於[^0-9] |
\w |
匹配包括下劃線的任意單詞字符,等價於[A-Za-z0-0] |
\W |
匹配任何非單詞字符,等價於[^A-Za-z0-9] |
\s |
匹配任何空白字符,包括空格,製表符,換頁符 |
\S |
匹配任何非空白字符 |
\b |
匹配單詞邊界 |
\B |
匹配非單詞邊界 |
這些預約義集,讓正則表達式看起來不那麼過於神祕了.
到目前爲止,咱們看到的操做符(如+和*)只能影響前面的術語.若是將操做符應用於一組術語,能夠像數學表達式同樣在該組上使用小括號.例如,/(ab)+/
匹配一個或多個連續出現的字字符串 "ab".
當正則表達式有一部分使用括號進行分組時,它具備雙重責任,同時也建立所謂的捕獲(capture).正則表達式有不少捕獲,咱們將在下面對其進行更深刻的討論.
能夠用豎線(|)字符表示或者的關係.例如:/a|b/
匹配 "a" 或 "b" 字符,/(ab)+|(cd)+/
則匹配出現一次或屢次的 "ab" 或 "cd".
正則表達式中最複雜的術語是在正則中所定義的捕獲(captures)的反向引用,咱們將在後面幾節花很大篇幅來介紹捕獲方面的內容,但如今就將捕獲做爲正則表達式中可以成功匹配術語時的候選字符串.
這種術語表示法是在反斜槓後面加一個要引用的捕獲數量,該數字從1開始,如\1
,\2
等
舉例來講,/^([dtn])a\1/
表示能夠任意一個以 "d" "t" "n" 開頭,且後面跟着一個 a 字符,而且後面跟着的是和第一個捕獲相同字符的字符串.後面這一點很重要! 它和/^[dtn]a[dtn]/
不同,由於a後面跟着的雖然也是"d" "t" "n" 中的一個,但可能和開頭字符不一樣,例如:在全局模式下,有兩個字符串"dad" 和 "dat" /^([dtn])a\1/
會選中 "dad",而不會選中 "dat",
/^[dtn]a[dtn]/
既選中 "dad",也會選中 "dat"
要匹配像 " whatever" 這樣的元素,不適用反向引用,是沒法作到的,由於咱們沒法知道關閉標籤和開始標籤是否匹配.
例如:/<(\w+).+<\/\1>/g
就能夠選中正常閉合的簡單標籤
在上面的代碼中,咱們使用\1
引用了表達式中的第一個捕獲,在本例中,該捕獲是標籤名稱
還有一個方法能夠獲取捕捉的引用,那就是經過調用String對象的replace()方法替換字符串.在這裏,咱們使用$1
$2
$3
語法表示每一個捕獲的數字.
例子:
var pattern=/([A-Z])/g; var str="fontFamily"; console.log(str.replace(pattern,"-$1").toLowerCase()); //font-family
在上面的代碼中,首先獲取的捕獲值(在本例中是大寫字母F),在替換字符串中進行了引用(經過$1).這種方式容許咱們制定一個替換字符串,即使是在運行以前還不知道它的值.這是個強大的武器.
這種能夠引用正則表達式捕捉結果的能力,讓不少原本很複雜困難的代碼變得至關簡單.其富有表現力的特性,將原本可能很遲鈍,複雜且冗長的代碼,最終變成了一些簡短的語句.
有時候咱們須要在一個字符串裏找到特定的數據,好比須要在爬蟲爬到的數據裏找到特定的數據,好比天氣溫度,用戶id,特定標籤裏面的內容.拿在特定標籤裏找裏面的內容來舉例,用正則匹配的話咱們一般會先找到這個標籤,好比<(strong).+<\/\1>
,這樣就能匹配到整個strong標籤和strong標籤裏面的內容了,
咱們找到了strong標籤和它裏面的內容,那麼又怎麼找到strong標籤裏面的test呢?
其實,只須要給''.+''加個括號就行
因而咱們看到匹配內容多了個test
因爲返回的匹配是個數組,因此咱們只須要用數組下標就能取到test這個值
爲何加個括號就能取到test了呢 ?
咱們前面已經知道了小括號具備雙重責任,不只能夠用來分組,還會建立所謂的 "捕獲" .
所以,使用小括號指定一個子表達式後,匹配這個子表達式的文本(也就是此分組捕獲的內容)能夠在表達式或其它程序中做進一步的處理。
既然小括號既有分組的做用,又有捕獲的做用,若是咱們只想分組,又不想捕獲怎麼辦呢?
思考一下以下的正則表達式:
var pattern=/((good-)+)man/;
在單詞 "man" 前面,容許前綴 "good" 出現一次或屢次,而且但願捕獲整個前綴.這個正則表達式須要兩層括號
定義捕獲(man 以前的全部字符串)的小括號
針對+操做符,對 "good-" 文本進行分組的小括號
一切運行正常,但因爲括號分組的功能,不只是單一目標捕獲
要讓一組括號不進行結果捕獲,正則表達式的語法容許咱們在開始括號後加一個 ?:
標記.這就是所謂的 被動表達式
將咱們的正則表達式修改爲以下這樣:
var pattern=/((?:good-)+)man/;
該表達式只會爲外層的括號建立捕獲.內層括號被轉換爲一個被動子表達式
String的replace()方法是一個強大且靈活的方法,將正則表達式做爲replace()方法的第一個參數時,致使在該模式的匹配元素(全局匹配的話,就是多個匹配元素)上進行替換,而不是在固定字符串上進行替換.
舉個例子,假如須要讓全部的大寫字符串都替換成"X",咱們能夠這樣編寫:
"ABCEDfg".replace(/[A-Z]/g,"X")
其結果爲 "XXXXXfg". 很不錯.
不過,獲取replace()最強大的特性是能夠接受一個函數做爲替換值,而不是一個固定的字符串.
當替換值(第二個參數)是一個函數時,每一個匹配都會調用該函數(記住,全局搜索會在源字符串中匹配全部的模式實例)並帶有一串參數列表.
匹配的完整文本.
匹配到捕獲,一個捕獲對應一個參數
匹配字符在源字符串中的索引
源字符串
函數的返回值是即將要替換掉值.
這給了咱們不少思考的餘地,讓咱們在運行時肯定應該替換的字符串,並掌握大量與匹配特性有關的信息.
例如,在下面的代碼中,咱們使用一個函數,動態地將中橫線分割的字符轉換成等價的駝峯拼寫字符.
function upper(all,letter){ return letter.toUpperCase(); } var pattern=/-(\w)(\w)/g; var str="border-bottom-width"; console.log(str.replace(pattern,upper)); //borderBttomWdth
在這裏,咱們提供了一個正則表達式,用於匹配中橫線字符後的任意一個字符.全局正則中的捕獲結果就是該匹配的字符(不包括之中橫線).函數在每次被調用的時候(本例中是兩次),傳入匹配的完整的字符串做爲第一個參數,捕獲結果(本例只有一個)做爲第二個參數.咱們對其餘參數不感興趣,因此沒有指定他們.
函數在第一次被調用的時候,傳入了 "-b" 和 "b" ,第二次被調用的時候傳入的是 "-w" 和 "w",在本例中,將捕獲字符轉換成大寫,並做爲替換值返回.最終,咱們將 "-b" 替換成了 "B",將 "-w" 替換成了 "W"
將字符串先後多餘的空格進行刪除是一種常見的需求,但String對象卻沒有這種功能(最近纔有).對於沒有String.trim()方法的舊版瀏覽器,幾乎全部的JavaScript庫都提供了一種實現.
最經常使用的實現,相似以下代碼:
function trim(str){ return (str||"").replace(/^\s+|\s+$/g,""); } console.log(trim(" #id div.class ")==="#id div.class"); //true