Linux正則表達式

一、正則表達式簡介


1. 什麼是正則表達式

正則表達式(Regluar Expressions)又稱規則表達式,這個概念最初是由Unix中的工具軟件(如sed 和 grep)普及開的。正則表達式在代碼中常簡寫爲REs,regexes或regexp(regex patterns)。它本質上是一個小巧的、高度專用的編程語言。 許多程序設計語言都支持通過正則表達式進行字符串操作。例如,在Perl中就內建了一個功能強大的正則表達式引擎。

2. 正則表達式能做什麼

正則表達式的主要應用對象是文本,使用正則表達式可以指定想要匹配的字符串規則,然後通過這個規則來匹配、查找、替換或切割那些符合指定規則的文本。總體來講,正則表達式可以對指定的文本實現以下功能:

  • 匹配驗證: 判斷給定的字符串是否符合正則表達式所指定的過濾規則,從而可以判斷某個字符串的內容是否符合特定的規則(如email地址、手機號碼等);當正則表達式用於匹配驗證時,通常需要在正則表達式字符串的首部和尾部加上^和$,以匹配整個待驗證的字符串。

  • 查找與替換: 判斷給定字符串中是否包含滿足正則表達式所指定的匹配規則的子串,如查找一段文本中的所包含的IP地址。另外,還可以對查找到的子串進行內容替換。

  • 字符串分割與子串截取: 基於子串查找功能還可以以符合正則表達式所指定的匹配規則的字符串作爲分隔符對給定的字符串進行分割。

二、正則表達式中的字符


正則表達式的主要應用對象是文本,其最基礎的功能是文本匹配,而文本是由一個個的字符組成,因此正則表達式實際上是對字符的匹配。正則表達式中的字符分爲 普通字符元字符,而正則表達式就是這些普通字符和特殊元字符組合成的表示一個特定匹配規則的表達式。

1. 普通字符

實際上,大多數字符都將簡單地匹配它們的自身值,它們被稱爲普通字符,如數字(0-9),字母(a-z, A-Z)等。例如,正則表達式hello123將匹配字符串'hello123',因爲該這則表達式中都是普通字符,不包含特殊元字符。當然,我們可以通過指定正則表達式的匹配模式爲 忽略字母大小寫模式,這麼正則表達式hello123將能夠匹配'Hello123', 'HellO123', 'HELLO123'等字符串。

提示: 其實我們並不需要去記憶哪些字符是普通字符,我們只需要知道哪些字符是特殊元字符就可以了,除了特殊元字符之外的所有字符都是普通字符。

2. 元字符

上面提到,正則表達式除了進行字符自身之的匹配外,還可以基於指定的規則進行模糊匹配。這就意味着它需要一些特殊字符來表示這些模糊的匹配規則,因此這些特殊字符默認情況下並不能匹配到它們自身的字面值,而是表示某些特殊的功能。這些特殊元字符包括:., [, ], (, ), *, +, ?, ^, $, \, |。這些特殊字符的使用,會在下面進行詳細講解。正則表達式的重點和難點也就在於對正則表達式引擎的工作原理以及對這些特殊元字符掌握和靈活運用。

提示: 那麼如果想匹配這些特殊元字符本身的字面值怎麼辦呢?我們可以通過其中一個特殊字符對其它特殊字符進行轉義,從而達到可以匹配這些特殊字符自身字面值的目的。

三、元字符詳解


現在我們來詳細說明一下正則表達式中的特殊元字符到底能完成哪些複雜的匹配功能。

1.單個字符匹配

說明: 所有的特殊字符在[ ]內都將失去其原有的特殊含義:

  • 有些特殊字符在[ ]中被賦予新的特殊含義,如 '^'出現在[ ]中的開始位置表示取反,它出現在[]中的其他位置表示其本身(變成了一個普通字符);

  • 有些特殊字符則變爲普通字符,如 '.', '*', '+', '?', '$'

  • 有的普通字符變爲特殊字符,如 '-' 在[ ]中的位置不是第一個字符則表示一個數字或字母區間,如果在[ ]中的位置是第一個字符則表示其本身(一個普通字符)

  • 在[ ]中,如果要使用'-', '^' 或']',可在在它們前面加上反斜槓,或把'-', ']'放在第一個字符的 位置,把'^'放在非第一個字符的位置。

2. 預定義字符集

我們可以在反斜槓後面跟上一個指定的字母來表示預定義的字符集合

3. 字符次數匹配--量詞

在正則表達式中,我們還可以指定匹配某個字符出現次數

我們可以得出以下結論:

  • {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[0-9]2)|(?:2[0-4][0-9])|(?:25[0-5]))[.]){3}((?:[1-9]\d?)|(?:1[0-9]2)|(?:2[0-4][0-9])|(?: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+)

如果要求必須同時包含且只能包含數字、大小字母、小寫字母和標點符號,可以這樣寫:

(?=^.{6,8}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*\W+)(?!.*[^\d\Wa-zA-Z])
匹配大陸×××號碼(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”,把控制權再交給“<”,嘗試匹配,匹配失敗,向前查找可供回溯的狀態。一直重複以上過程,直到“.*”讓出已匹配的字符“<”,實際上也就是讓出了已匹配的子串“cc”爲止,“<”才匹配字符“<”成功,控制權交給“/”。

  • 接下來由“/”、“d”、“i”、“v”分別匹配對應的字符成功,此時整個正則表達式匹配完畢。

六、正則表達式中的標誌位-flag


上面提到的貪婪模式與非貪婪模式影響的是被量詞修飾的子表達式的匹配行爲,而這裏所說的標誌位將會影響正則表達式的整體工作方式。不同編程語言中通常都會有預設的常量值來表示這些標誌位,大家在用到時自己查下文檔既可以。常用的標誌位如下:

標誌位作用描述
表示忽略大小寫的標誌位默認情況下,正則表達式在進行匹配時是區分大小寫的
表示匹配任何字符的標誌位這個標誌位影響的是'.'這個元字符,因爲它默認情況下是匹配除換行符之外的任意字符,當指定這個標誌位之後,'.'將可以匹配任意字符
表示多行匹配的標誌位它影響是是'^'和'$'這兩個元字符,它們默認匹配的是一個字符串的開頭和結尾,指定這個標誌位後,它們可以匹配每一行的行首和行尾

七、參考資料


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章