基於element的Cascader的省市縣聯動
本節內容
- 正則表示式簡介
- 正則表示式中的字元
- 元字元詳解
- 常用正則表示式例項
- 正則表示式的匹配過程
- 正則表示式中的標誌位-flag
- 參考資料
需要提前說明的是:正則表示式的語法是由正則表示式引擎決定的(目前主流的正則引擎分為3類:DFA、傳統型NFA 和 POSIX NFA),不同程式語言或應用程式所使用的引擎可能不同,它們對正則表示式的語法支援會有差別。
一、正則表示式簡介
1. 什麼是正則表示式
正則表示式(Regluar Expressions)又稱規則表示式,這個概念最初是由Unix中的工具軟體(如sed 和 grep)普及開的。正則表示式在程式碼中常簡寫為REs,regexes或regexp(regex patterns)。它本質上是一個小巧的、高度專用的程式語言。
2. 正則表示式能做什麼
正則表示式的主要應用物件是文字,使用正則表示式可以指定想要匹配的字串規則,然後通過這個規則來匹配、查詢、替換或切割那些符合指定規則的文字。總體來講,正則表示式可以對指定的文字實現以下功能:
- 匹配驗證:判斷給定的字串是否符合正則表示式所指定的過濾規則,從而可以判斷某個字串的內容是否符合特定的規則(如email地址、手機號碼等);當正則表示式用於匹配驗證時,通常需要在正則表示式字串的首部和尾部加上^和$,以匹配整個待驗證的字串。
- 查詢與替換:判斷給定字串中是否包含滿足正則表示式所指定的匹配規則的子串,如查詢一段文字中的所包含的IP地址。另外,還可以對查詢到的子串進行內容替換。
- 字串分割與子串擷取:基於子串查詢功能還可以以符合正則表示式所指定的匹配規則的字串作為分隔符對給定的字串進行分割。
二、正則表示式中的字元
正則表示式的主要應用物件是文字,其最基礎的功能是文字匹配,而文字是由一個個的字元組成,因此正則表示式實際上是對字元的匹配。正則表示式中的字元分為普通字元和元字元,而正則表示式就是這些普通字元和特殊元字元組合成的表示一個特定匹配規則的表示式。
1. 普通字元
實際上,大多數字符都將簡單地匹配它們的自身值,它們被稱為普通字元,如數字(0-9),字母(a-z, A-Z)等。例如,正則表示式hello123
將匹配字串'hello123',因為該這則表示式中都是普通字元,不包含特殊元字元。當然,我們可以通過指定正則表示式的匹配模式為 忽略字母大小寫模式,這麼正則表示式hello123
提示:其實我們並不需要去記憶哪些字元是普通字元,我們只需要知道哪些字元是特殊元字元就可以了,除了特殊元字元之外的所有字元都是普通字元。
2. 元字元
上面提到,正則表示式除了進行字元自身之的匹配外,還可以基於指定的規則進行模糊匹配。這就意味著它需要一些特殊字元來表示這些模糊的匹配規則,因此這些特殊字元預設情況下並不能匹配到它們自身的字面值,而是表示某些特殊的功能。這些特殊元字元包括:., [, ], (, ), *, +, ?, ^, $, \, |。這些特殊字元的使用,會在下面進行詳細講解。正則表示式的重點和難點也就在於對正則表示式引擎的工作原理以及對這些特殊元字元掌握和靈活運用。
提示:那麼如果想匹配這些特殊元字元本身的字面值怎麼辦呢?我們可以通過其中一個特殊字元對其它特殊字元進行轉義,從而達到可以匹配這些特殊字元自身字面值的目的。
三、元字元詳解
現在我們來詳細說明一下正則表示式中的特殊元字元到底能完成哪些複雜的匹配功能。
1.單個字元匹配
說明:所有的特殊字元在[ ]內都將失去其原有的特殊含義:
- 有些特殊字元在[ ]中被賦予新的特殊含義,如 '^'出現在[ ]中的開始位置表示取反,它出現在[]中的其他位置表示其本身(變成了一個普通字元);
- 有些特殊字元則變為普通字元,如 '.', '*', '+', '?', '$'
- 有的普通字元變為特殊字元,如 '-' 在[ ]中的位置不是第一個字元則表示一個數字或字母區間,如果在[ ]中的位置是第一個字元則表示其本身(一個普通字元)
- 在[ ]中,如果要使用'-', '^' 或']',可在在它們前面加上反斜槓,或把'-', ']'放在第一個字元的 位置,把'^'放在非第一個字元的位置。
2. 預定義字符集
我們可以在反斜槓後面跟上一個指定的字母來表示預定義的字元集合
3. 字元次數匹配--量詞
在正則表示式中,我們還可以指定匹配某個字元出現次數
說明:{m,n}中的m和n可以省略其中一個,{,n}相當於{0,n},{m,}相當於{m,整數最大值}。
我們可以得出以下結論:
- {0,1}或{,1} 等價於 ?
- {1,} 等價於 +
- {0,} 等價於 *
我們優先選擇使用 ?, + 和 *,因為他們書寫簡單,也可以使整個正則表示式變得簡潔。
說明:? 這個字元在正則表達中與 ?, +, *, {m,n}連用時還有一個額外的功能,就是將匹配模式由貪婪模式(儘可能的增加匹配次數) 變成 非貪婪模式(儘可能減少匹配次數), 這個會在下面的內容中進行詳細說明。
4. 邊界匹配
正則表示式中還可以對邊界位置進行匹配,如一個字串的開頭或結尾,一個單詞的開頭或結尾。
5. 邏輯與分組
語法 | 說明 | 表示式例項 | 可匹配到的字串例項
6. 特殊構造
說明:上面所說的“不消耗字串內容”是指只是進行匹配,但是不移動原始字串的匹配位置,這樣就可以完成多次匹配。下面有個匹配密碼的正則表示式例項,就是用這個特性巧妙完成的。
四、常用正則表示式例項
通常寫一個合適的正則表示式是比較耗費時間的,因此我們可以保留一些常用的正則表示式以備不時之需。但是需要說明的是,沒有任何一個人敢說自己寫的正則表示式是百分之百嚴謹的,而且也沒有百分之百相同的匹配需求,因此這裡只是列舉我自己寫的幾個常用的正則表示式,歡迎大家留言討論。
說明:下面只是一些簡單的匹配規則,實際情況中需要我們根據具體情況再這些正則表示式的首部和尾部加上相應的邊界符,如:^, $, \A, \Z, \b, \B等
匹配一個網路地址(URL)
[a-zA-Z]+://[\S]+
需要說明的是,網路地址不一定是一個網頁地址(http或https連結),還可能是ftp地址等。如果我們要匹配特定協議的網路地址,如http或http連結可以這樣寫:
(https?://)?[\S]+
匹配一個IP地址
最簡單的寫法:
(\d+[.]){3}\d+
嚴謹一點的寫法:
(((?:[1-9]\d?)|(?:1\d{2})|(?:2[0-4]\d)|(?:25[0-5]))[.]){3}((?:[1-9]\d?)|(?:1\d{2})|(?:2[0-4]\d)|(?:25[0-5]))
或
((([1-9]\d?)|(1\d{2})|(2[0-4]\d)|(25[0-5]))[.]){3}(([1-9]\d?)|(1\d{2})|(2[0-4]\d)|(25[0-5]))
匹配一個郵箱地址
最簡單的寫法:
\S+@\S+\.\S+
嚴謹一點的寫法(保證只出現一個@符):
[^\s@]+@[^\s@]+\.[^\s@]+
如果要非常嚴謹的話,就要區分不同的郵箱了,因為網易(126郵箱,163郵箱)、qq郵箱、hotmail郵箱以及gmail郵箱對郵箱名稱中可以包含的字元都有不同的要求。
匹配網易郵箱:6-18個字元,只能包含字母、數字和下劃線,且只能以字母開頭
[a-zA-Z]\w{5,17}@(126|163)\.com
匹配qq郵箱:3-18個字元,只能包含字母、數字、點、減號和下劃線
[\w.-]{3,18}@qq\.com
如果要多個郵箱的嚴謹匹配用一個正則表示式來匹配,比如要匹配網易郵箱和qq郵箱可以這樣寫:
(?:[a-zA-Z]\w{5,17}@(126|163)\.com)|(?:[\w.-]{3,18}@qq\.com)
當然,也可以分別匹配多個正則表示式,再通過程式邏輯來得到最後的結果
匹配密碼是否合法:
要求比較簡單的情況,比如只要求為非空字元且限定密碼長度為6-18位
^\S[6-18]$
要求比較複雜的情況,比如必須同時包含含數字、大小字母、小寫字母和標點符號,這就需要用到前面所說的正則表示式的特殊構造了(?=...), (?!=...)
(?=^.{6,8}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*\W+)
如果要求必須同時包含且只能包含數字、大小字母、小寫字母和標點符號,可以這樣寫:
(?=^[\d\Wa-zA-Z]{6,8}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*\W+)
匹配大陸身份證號碼(15位或18位)
\d{15}|(\d{18}|(\d{17}[Xx]))
小貼士:當今的身份證號碼有15位和18位之分。1985年我國實行居民身份證制度,當時簽發的身份證號碼是15位的,1999年簽發的身份證由於年份的擴充套件(由兩位變為四位)和末尾加了效驗碼,就成了18位。這兩種身份證號碼將在相當長的一段時期內共存。兩種身份證號碼的含義如下:
匹配日期(年-月-日)
(\d{2}|\d{4})-((0?[1-9])|(1[0-2]))-((0?[1-9])|([12][0-9])|(3[01]))
24小時制時間(小時:分鐘:秒)
(((0?|1)[0-9])|(2[0-3])):([0-5][0-9]):([0-5][0-9])
其他常用正則表示式
匹配內容 | 正則表示式 |
---|---|
QQ號碼 | [1-9]\d{4,} |
中國大陸固定電話號碼 | (\d{3,4}-)?\d{7,8} |
中國大陸手機號碼 | 1\d{10} |
中國大陸郵政編碼 | \d{6} |
漢字 | [\u4e00-\u9fa5] |
中文及全形標點符號 | [\u3000-\u301e\ufe10-\ufe19\ufe30-\ufe44\ufe50-\ufe6b\uff01-\uffee] |
不含abc的單詞 | (?=\w+)(?!abc) |
正整數 | [1-9]+ |
負整數 | -[1-9]+ |
非負整數(正整數+0) | [1-9]+ |
非正整數(負整數+0) | -[1-9]+ |
整數+0 | -?[1-9]+ |
正浮點數 | \d+.\d+ |
負浮點數 | -\d+.\d+ |
浮點數 | -?\d+.\d+ |
再次說明,實際情況中需要我們根據具體情況再這些正則表示式的首部和尾部加上相應的邊界符,如:^, $, \A, \Z, \b, \B等
五、正則表示式的匹配過程
基於量詞(如?, +, *, {m,n}, {m,})的字元重複次數匹配是正則表示式優於普通字串處理方法的一個重要方面,也是正則表示式的一個重要組成部分。量詞對正則表示式的匹配過程具有非常重大的影響,因此在介紹正則表示式的匹配過程時,必不可少的要提到量詞的兩個重要分類:
- 匹配優先量詞我們上面介紹的量詞就是匹配優先量詞包括:?, +, *, {m,n},但是不包括{m}
- 忽略優先量詞在匹配優先量詞後面加上一個問號就變成了忽略優先量詞,包括:??, +?, *?, {m,n}?
如果大家對這兩個詞不熟悉的話,那麼大家一定聽說過這兩個詞:
- 貪婪模式(或非惰性匹配)顧名思義,就是在整個表示式匹配成功的前提下,儘可能多的去匹配量詞所修飾的字元
- 非貪婪模式(或惰性匹配)在整個表示式匹配成功的前提下,儘可能少的去匹配量詞所修飾的字元
它們之間的關係是:
- 匹配優先量詞修飾的子表示式使用的是就是貪婪模式(非惰性匹配);
- 忽略優先量詞修飾的子表示式使用的就是模式就是非貪婪模式(惰性匹配);
下面我們通過一個例項來分析 貪婪模式 和 非貪婪模式 下的正則匹配過程:
-
要匹配的字串:'abcbd'
-
貪婪模式正則表示式:
a[bcd]*b
-
非貪婪模式正則表示式:
a[bcd]*?b
1. 貪婪模式匹配過程分析
2. 非貪婪模式匹配過程分析
3. 總結
貪婪模式與非貪婪模式影響的是被量詞修飾的子表示式的匹配行為,貪婪模式在整個表示式匹配成功的前提下,儘可能多的匹配;非貪婪模式在整個表示式匹配成功的前提下,儘可能少的匹配。另外,非貪婪模式只被部分NFA引擎所支援。從匹配效率上來看,能達到相同匹配結果時,貪婪模式的匹配效率通常會比較高,因為它回溯過程會比較少。
4. 補充示例
偶然看到一個比較好的關於貪婪模式的匹配過程示例,分享給大家。該示例出自《這篇文章》
-
首先由“<”取得控制權,由位置0位開始嘗試匹配,匹配字元“a”,匹配失敗,第一輪匹配結束。第二輪匹配從位置1開始嘗試匹配,同樣匹配失敗。第三輪從位置3開始嘗試匹配,匹配字元“<”,匹配成功,控制權交給“d”。
-
“d”嘗試匹配字元“d”,匹配成功,控制權交給“i”。重複以上過程,直到由“>”匹配到字元“>”,控制權交給“.*”。
-
“.*”屬於貪婪模式,將從B處後的字元“t”開始,一直匹配到E處,也就是字串結束位置,將控制權交給“<”。
-
“<”從字串結束位置嘗試匹配,匹配失敗,向前查詢可供回溯的狀態,把控制權交給“.”,由“.”讓出一個字元“c”,把控制權再交給“<”,嘗試匹配,匹配失敗,向前查詢可供回溯的狀態。一直重複以上過程,直到“.*”讓出已匹配的字元“<”,實際上也就是到讓出了已匹配的子串
“</div>cc"
為止,“<”才匹配字元“<”成功,控制權交給“/”。 -
接下來由“/”、“d”、“i”、“v”分別匹配對應的字元成功,此時整個正則表示式匹配完畢。
六、正則表示式中的標誌位-flag
上面提到的貪婪模式與非貪婪模式影響的是被量詞修飾的子表示式的匹配行為,而這裡所說的標誌位將會影響正則表示式的整體工作方式。不同程式語言中通常都會有預設的常量值來表示這些標誌位,大家在用到時自己查下文件既可以。常用的標誌位如下:
標誌位作用 | 描述 |
---|---|
表示忽略大小寫的標誌位 | 預設情況下,正則表示式在進行匹配時是區分大小寫的 |
表示匹配任何字元的標誌位 | 這個標誌位影響的是'.'這個元字元,因為它預設情況下是匹配除換行符之外的任意字元,當指定這個標誌位之後,'.'將可以匹配任意字元 |
表示多行匹配的標誌位 | 它影響是是'^'和'$'這兩個元字元,它們預設匹配的是一個字串的開頭和結尾,指定這個標誌位後,它們可以匹配每一行的行首和行尾 |
七、參考資料
- https://docs.python.org/3.5/howto/regex.html
- http://blog.csdn.net/lxcnn/article/details/4756030
- 一張經典的總結圖(收藏了很久,忘記了出處,如果哪位知道請告知,這裡會附上鍊接地址,謝謝。)