這是一篇男女老少入門精通咸宜的正則筆記

pekonchan發表於2018-12-15

前言

說句實話的,自從我整理這篇筆記後,基本上專案裡遇到的所有正則匹配的問題,不論多複雜,只要回過頭看這篇筆記,理解透徹(請注意我的用詞描述,我這裡描述比較嚴謹,理解好中文意思更方便你理解正則意義。這是個文字遊戲[奸笑]),基本上都能解決的。這裡關於深內容的描述,我這邊也舉了不少內容幫助大家理解。

這是一篇男女老少入門精通咸宜的正則筆記。

正規表示式是什麼?

字元是計算機軟體處理文字時最基本的單位,字串是0個或更多個字元的序列。 在編寫處理字串的程式或網頁時,經常會有查詢符合某些複雜規則的字串的需要。正規表示式就是用於描述這些規則的工具,就是來用於匹配字串中字元組合的模式。

怎麼建立?

正規表示式字面量
/ pattern / flag
呼叫RegExp物件的建構函式
new RegExp(pattern, flag)
這裡的pattern,有三種形式:

  1. 引數變數
  2. 帶引號的匹配模式
  3. 帶//的匹配模式

注意:帶引號的方式,需要常規的字元轉義規則(在前面加反斜槓 \),而帶//的方式,則跟字面量形式一樣。

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 。

注意

  1. 只有用match方法才能體現g的意義
  2. 注意返回值,其中有個描述是會返回括號捕獲結果,這個資訊非常重要。舉個例子說明其重要性:針對這麼一個字串CrawlerName":"BW","Sis":444',獲取“BW”這個字串。如果用正則匹配出來?

第一反應,使用上述的零寬斷言,執行一下指令碼

'"CrawlerName":"BW","Sis":444'.match(/(?<="CrawlerName":").*(?=",)/)
複製程式碼

返回值為一個陣列—— ['BW']

然而,可能瀏覽器不支援負向零寬斷言,所以找個通用的辦法,這時候就要發揮match返回括號捕獲的功能了。執行一下指令碼

'"CrawlerName":"BW","Sis":444'.match(/"CrawlerName":"(.*)",/)
複製程式碼

這時候返回['"CrawlerName":"BW",', 'BW'],大家注意到第二個元素了沒,就是括號捕獲的內容了,是不是比用零寬斷言簡單好多咧

最後

如果你有正則匹配問題,歡迎諮詢,儘量為你解答。

喜歡請點贊~轉載註明出處謝謝~寫文章不易,用手機寫的,如有格式問題請諒解

相關文章