前言
說句實話的,自從我整理這篇筆記後,基本上專案裡遇到的所有正則匹配的問題,不論多複雜,只要回過頭看這篇筆記,理解透徹(請注意我的用詞描述,我這裡描述比較嚴謹,理解好中文意思更方便你理解正則意義。這是個文字遊戲[奸笑]),基本上都能解決的。這裡關於深內容的描述,我這邊也舉了不少內容幫助大家理解。
這是一篇男女老少入門精通咸宜的正則筆記。
正規表示式是什麼?
字元是計算機軟體處理文字時最基本的單位,字串是0個或更多個字元的序列。 在編寫處理字串的程式或網頁時,經常會有查詢符合某些複雜規則的字串的需要。正規表示式就是用於描述這些規則的工具,就是來用於匹配字串中字元組合的模式。
怎麼建立?
正規表示式字面量
/ pattern / flag
呼叫RegExp物件的建構函式
new RegExp(pattern, flag)
這裡的pattern,有三種形式:
- 引數變數
- 帶引號的匹配模式
- 帶//的匹配模式
注意:帶引號的方式,需要常規的字元轉義規則(在前面加反斜槓 \),而帶//的方式,則跟字面量形式一樣。
E.g. 都表示同一個規則
var re = new RegExp("\\w+");
var re = new RegExp(/\w+/);
var re = /\w+/;
複製程式碼
flag標識
如果指定,標誌可以具有以下值的任意組合:
g : 全域性匹配;找到所有匹配,而不是在第一個匹配後停止
i : 忽略大小寫
m : 多行; 將開始和結束字元(^和$)視為在多行上工作(例如,分別匹配每一行的開始和結束(由 \n 或 \r 分割),而不只是只匹配整個輸入字串的最開始和最末尾處。
區別:
當正規表示式保持為常量時使用字面量;如果你知道正規表示式模式將會改變,或者你事先不知道什麼模式,而是從另一個來源獲取,如使用者輸入,這些情況都可以使用建構函式。
規則
簡單匹配,精確匹配
常見的特殊字元
特殊字元 | 描述 |
---|---|
\b | 匹配一個詞的邊界。\b匹配這樣的位置:它的前一個字元和後一個字元不全是(一個是,一個不是或不存在)\w。注意,一個匹配的詞的邊界並不包含在匹配的內容中,跟^$ 一樣,或者可以這麼理解:由於這是個位置,所以匹配出來自然就沒東西了。舉例說明: 一個字串 reading books ,\b匹配的是哪裡呢?畫個圖,看錶格最下方圖一。就是橙色線的位置就是\b的位置了 |
\w | 匹配一個單字字元(字母、數字或者下劃線) |
\s | 匹配一個空白字元,包括空格、製表符、換頁符和換行符。 |
\d | 匹配一個數字。匹配的是正整數,且匹配檢查的物件是數字型別時,如1. 會當成1 處理,即/^\d$/.test(1.) 返回true,/^\d$/.test(‘1.’) 返回false; |
. | (小數點)匹配除換行符之外的任何單個字元。 |
[\u4e00-\u9fa5] | 匹配漢字 |
? | 匹配前面一個表示式0次或者1次。等價於 {0,1}。如果緊跟在任何量詞 *、 +、?或 {} 的後面,將會使量詞變為非貪婪的(匹配儘量少的字元),和預設使用的貪婪模式(匹配儘可能多的字元)正好相反。 |
* | 匹配前一個表示式0次或多次。等價於 {0,}。 |
+ | 匹配前面一個表示式1次或者多次。等價於 {1,}。 |
{n} | n是一個正整數,匹配了前面一個字元剛好發生了n次 |
{n,m} |
n 和 m 都是正整數。匹配前面的字元至少n次,最多m次。如果 n 或者 m 的值是0, 這個值被忽略。 |
^ | 匹配輸入的開始。如果多行標誌被設定為true,那麼也匹配換行符後緊跟的位置。當'^'作為第一個字元出現在一個字符集合模式時,它將表示“非” |
$ | 匹配輸入的結束。如果多行標示被設定為true,那麼也匹配換行符前的位置。 |
| | 如x|y ,匹配‘x’或者‘y’。匹配分枝條件時,將會從左到右地測試每個條件,如果滿足了某個分枝的話,就不會去再管其它的條件了。 |
[ ] | 一個字符集合。匹配方括號的中任意字元,包括轉義序列。你可以使用破折號(-)來指定一個字元範圍。不過[.?!*]匹配標點符號(.或?或!或*) 。他們不必進行轉義,不過轉義也是起作用的。 |
[^] | 當 '^'作為第一個字元出現在一個字符集合模式時,它將表示“非”。而且是對於其後的所有表示式取非,而不是緊接其後的一個表示式。 匹配的結果為不是這些字元的字元。 如 [^aei] 匹配除了aei這幾個字母以外的任意字元,注意是單獨取非,即不能出現a或e或i,而不是不能出現aei 這個整體字串。例如: /[^aei]/.test('a') ,結果為false,因為他出現了a。再如/[^aei]/.test('as') ,結果為true,因為這裡的有出現s,滿足既不為a也不為e也不為i。 |
\ | 在非特殊字元之前的反斜槓表示下一個字元是特殊的,不能從字面上解釋。反斜槓也可以將其後的特殊字元,轉義為字面量。使用 new RegExp("pattern") 的時候不要忘記將 \ 進行轉義,因為 \ 在字串裡面也是一個轉義字元。 |
此外,對應還有\B \W \S \D,都匹配跟上面小寫的意思相反
正規表示式裡的單詞是什麼意思吧:就是不少於一個的連續的\w。
圖一:
“(”和“)”也是元字元,需要時也是要使用轉義的。
規則 | 描述 |
---|---|
(x) | 使用小括號指定一個子表示式後,匹配這個子表示式的文字(也就是此分組捕獲的內容)可以在表示式或其它程式中作進一步的處理。 |
(?:x) | 匹配 'x'但是不記住匹配項。這種叫作非捕獲括號,不給此分組分配組號 |
(?x) |
匹配x,並捕獲文字到名稱為name的組裡,也可以寫成 (?'name'x) |
這裡詳細說說關於 (x)
預設情況下,每個分組會自動擁有一個組號,規則是:分組0對應整個正規表示式實際上組號分配過程是要從左向右掃描兩遍的:第一遍只給未命名組分配,第二遍只給命名組分配--因此所有命名組的組號都大於未命名的組號
可以使用(?:exp)
這樣的語法來剝奪一個分組對組號分配的參與權。
後向引用用於重複搜尋前面某個分組匹配的文字。
例如,\1代表分組1匹配的文字。
注意,這個不適合這樣使用([...]),不適合包括在方括號裡,這樣的必須後面出現的跟第一次出現的匹配型別一致。
例如,/^([a-z\d])\1$/,可以是字母也可數字,但是第一次出現的數字的話,後面必須也是數字這個表示式才會正確用。
這個例子去執行一下就知道應用場景和意義了:'2018-09-87'.match(/^(\d{1,4})(-|/)(\d{1,2})\2(\d{1,2})$/)
零寬斷言
即零寬度斷言,斷言就是判斷條件,零寬度就是匹配出來的內容是零,表現跟上述的\b^$
一樣,也是用來匹配位置的。
規則 | 描述 |
---|---|
(?=x) |
匹配這麼一個位置:後面接著出現的是匹配表示式x。 比如\b\w+(?=ing\b),匹配以ing結尾的單詞的前面部分(除了ing以外的部分)。 如查詢I'm singing while you're dancing.時,它會匹配sing和danc。 如表格下放的圖二所示,所以匹配出來的結果是橙線和藍線之間的內容,即上述表達的\w+部分 |
(?<=x) | 匹配這麼一個位置:前面接著出現的是匹配表示式x。 比如(?<=\bre)\w+\b會匹配以re開頭的單詞的後半部分(除了re以外的部分),例如在查詢reading a book時,它匹配ading。如表格下放的圖三所示 |
(?!x) | 匹配這麼一個位置:後面接著出現的不能是匹配表示式x。 例如:\d{3}(?!\d)匹配三位數字,而且這三位數字的後面不能是數字 |
(?<!x) | 匹配這麼一個位置:前面接著出現的不能是匹配表示式x。 |
圖二:
圖三:
這裡有相容性問題,有些瀏覽器只支援正向的零寬斷言即(?=exp)和(?!exp),不支援負向零寬斷言。而且就算瀏覽器支援了,可能你的編譯工具在執行編譯時也會報錯。
值得一提,有個經典的用法,也是運用到零寬斷言,假設有個需求需要匹配一個字串裡不能包含某個字串。例如,判斷一個字串裡不能包含hello
正規表示式: ^((?!hello).)*$
如果用test()來校驗的話,那麼只要這個字串裡包含hello,就會為false。當然其實這種需求並不需要這麼麻煩去處理。直接用/hello/.test(somestring)
,然後取反就能識別了。
貪婪與懶惰
當正規表示式中包含能接受重複的限定符時,通常的行為是(在使整個表示式能得到匹配的前提下)匹配儘可能多的字元。
以這個表示式為例:a.*b,它將會匹配最長的以 a開始,以b結束的字串。如果用它來搜尋aabab的話,它會匹配整個字串aabab。這被稱為貪婪匹配。
有時,我們更需要懶惰匹配,也就是匹配儘可能少的字元。前面給出的限定符都可以被轉化為懶惰匹配模式,只要在它後面加上一個問號?。
這樣.*?就意味著匹配任意數量的重複,但是在能使整個匹配成功的前提下使用最少的重複。
a.*?b匹配最短的,以a開始,以b結 束的字串。如果把它應用於aabab的話,它會匹配aab(第一到第三個字元)和ab(第四到第五個字元)。 為什麼第一個匹配是aab(第一到第三個字元)而不是ab(第二到第三個字元)?
簡單地說,因為正規表示式有另 一條規則,比懶惰/貪婪規則的優先順序更高:** 最先開始的匹配擁有最高的優先權。**
懶惰模式適用於 * ? + {} 後面;
還有一個很好例子說明懶惰模式的用處,'{{f}},fas{{fsfsf}}a{{fsa}}'.match(/{{.+?}}/g)
。 這個就能把一個字串裡所有{{}}的最小單位給篩選出來,可以去控制檯裡輸出看看
怎麼用?
被用於 RegExp 的 exec 和 test 方法, 以及 String 的 match、replace、search 和 split 方法。
exec()
語法
regexObj.exec(str)
返回值
如果匹配成功,exec()方法返回一個陣列,並更新正規表示式物件的屬性。返回的陣列將完全匹配成功的文字作為第一項,將正則括號裡匹配成功(即括號捕獲)的作為陣列填充到後面。
如果匹配失敗,exec() 方法返回 null。
test()
語法
regexObj.test(str)
返回值
如果正規表示式與指定的字串匹配,返回true;否則false。
search()
語法
str.search(regexp)
引數regexp,一個正規表示式(regular expression)物件。如果傳入一個非正規表示式物件,則會使用 new RegExp(obj)
隱式地將其轉換為正規表示式物件。
返回值
如果匹配成功,則search()返回正規表示式在字串中首次匹配項的索引。否則,返回 -1。
match()
語法
str.match(regexp);
引數regexp,一個正規表示式物件。如果傳入一個非正規表示式物件,則會隱式地使用 new RegExp(obj)
將其轉換為一個RegExp。如果你未提供任何引數,直接使用 match() ,那麼你會得到一個包含空字串的 Array :[""] 。
返回值
一個包含了整個匹配結果以及任何括號捕獲的匹配結果的 Array ;如果沒有匹配項,則返回 null 。
描述
如果正規表示式沒有 g 標誌,則str.match()
會返回和 RegExp.exec()
相同的結果。
相比有g,返回的Array擁有一個額外的input屬性,該屬性包含被解析的原始字串。另外,還擁有一個index屬性,該屬性表示匹配結果在原字串中的索引(以0開始)。
如果正規表示式包含 g 標誌,則該方法返回一個 Array ,它包含所有匹配的子字串而不是匹配物件。捕獲組不會被返回(即不返回index屬性和input屬性)。如果沒有匹配到,則返回 null 。
注意:
- 只有用match方法才能體現g的意義
- 注意返回值,其中有個描述是會返回括號捕獲結果,這個資訊非常重要。舉個例子說明其重要性:針對這麼一個字串
CrawlerName":"BW","Sis":444'
,獲取“BW”這個字串。如果用正則匹配出來?
第一反應,使用上述的零寬斷言,執行一下指令碼
'"CrawlerName":"BW","Sis":444'.match(/(?<="CrawlerName":").*(?=",)/)
複製程式碼
返回值為一個陣列—— ['BW']
然而,可能瀏覽器不支援負向零寬斷言,所以找個通用的辦法,這時候就要發揮match返回括號捕獲的功能了。執行一下指令碼
'"CrawlerName":"BW","Sis":444'.match(/"CrawlerName":"(.*)",/)
複製程式碼
這時候返回['"CrawlerName":"BW",', 'BW']
,大家注意到第二個元素了沒,就是括號捕獲的內容了,是不是比用零寬斷言簡單好多咧
最後
如果你有正則匹配問題,歡迎諮詢,儘量為你解答。
喜歡請點贊~轉載註明出處謝謝~寫文章不易,用手機寫的,如有格式問題請諒解