快速回憶正則表達式

這不是入門級文章,但如果你對正則表達式有了解,或使用過,則能幫助你快速回憶。閱讀此文需要你之前使用過正則表達式或者有些瞭解,因爲我沒有寫很多的例子。總結正則表達式,只是因爲個人在幾年的積累之後,想通俗簡單的概述正則表達式中各種符號和用法。網上有很多關於正則表達式,但總感覺專業術語太多,讀起來拗口或繞彎子。最後兩小節來自正則表達式30分鐘入門教程(如果你完全不瞭解正則表達式,還是先去這看看,因爲他的例子較多,兩相比較有助深入理解),有部分修繕。

正則表達式字符串由兩種基本字符組成:原義文本字符和元字符。所謂元字符就是正則表達式中具有特殊意義的專用字符,元字符可能是一個字符,也可能是多個字符組成的一個基本單元。

元字符

一個元字符可能代表的是一個數字、字母、位置或數量。

代表字符

代碼

說明

.

匹配除換行符以外的任意字符

\w

匹配字母或數字或下劃線或漢字

\s

匹配任意的空白符

\d

匹配數字

\S

與\s相反

\D

與\d相反

\W

與\w相反


代表數量           

代碼

說明

*

重複零次或更多次

+

重複一次或更多次

?

重複零次或一次

{n}

重複n次

{n,}

重複n次或更多次

{n,m}

重複n到m次


代表位置

代碼

說明

\b

匹配單詞的開始或結束

\B

不是單詞開頭或結束的開始

^

匹配字符串的開始

$

匹配字符串的結束


轉義字符

既然元字符在正則表達式中有特殊意義,如果現在要把它們當成一般原義文本字符,怎麼辦?在元字符前添加反斜槓\,表示當前元字符已經失去了正則表達式中的特殊意義,變成了一個原義文本字符。

預定義字符集

元字符匹配範圍太廣,如果只是想匹配某小範圍內的字符,該怎麼辦?

很簡單,使用方括號列出來即可。如[aeiou]或[.?!],表示只能匹配方括號中出現的字符。

需要注意的是:在這個方括號中,代斜槓的元字符依然是元字符,但那些沒有斜槓前綴的則都不再是元字符,但多了一個元字符,就是連字符-。

  •  如果連字符在兩個字符之間,那麼就表示範圍,連字符本身不算,如[0-9], [a-z]等;
  •  如果連字符後面沒有字符,則表示連字符也是預定義的字符集的字符之一,如[*%-];

在預定義字符集中,可以使用轉義字符

反義(antonymy)

有時只需要查找不屬於某個簡單定義的字符集的字符,這就是反義

代碼

說明

\W

匹配任意不是字母,數字,下劃線,漢字的字符

\S

匹配任意不是空白符的字符

\D

匹配任意非數字的字符

\B

匹配不是單詞開頭或結束的位置

[^x]

匹配除了x以外的任意字符

[^aeiou]

匹配除了aeiou這幾個字母以外的任意字符


或 | 分支

這相當於或語句,具體方法是用垂直線|把不同的規則分隔開。如0\d{2}-\d{8}|0\d{3}-\d{7}這個表達式能匹配兩種以連字號分隔的電話號碼:一種是三位區號,8位本地號(如010-12345678),一種是4位區號,7位本地號(0376-2233445)。

分組(group)

所謂分組,其實就是把小括號()中正則表達式作爲一個小的匹配單元。

分組的作用有兩個:

  • 默認情況下,正則表達式會解析器給每一個小組分配一個組號,通過這種方式,後面的正則表達式就可以通過組號引用這個小組匹配的內容
  • 小組後可以跟代表數量的元字符,簡化正則表達式書寫;比如,(\d{1,3}\.){3}\d{1,3}是一個簡單的IP地址匹配表達式。

小組後跟代表數量的元字符有一個問題需要注意:感覺應該是生成了若干個小組,但其實就是一個分組,因爲組號是解析器解析的時候給的,代表小組的字符串在整個正則表達式字符串中只出現了一次,所以只分配了一個唯一的組號。因此,正則表達式執行完後,這個組匹配的內容是最後一次匹配的內容。

比如:

/(\d{1,3}\.){3}\d{1,3}/g.exec("201.202.203.204");

結果:["201.202.203.204", "203."]

後面的正則表達式通過組號引用前面分組匹配的內容,這個就叫後向引用。組號的規則是:從左向右,以分組的左括號爲標誌,第一個出現的分組的組號爲1,第二個爲2,依此類推。引用的時候記得在組號前加上斜槓。

比如:

/\b(\w+)\b\s+\1\b/.test("hellohello");  // true

/\b(\w+)\b\s+\1\b/.test("hellohell");  // false

分組的語法:

代碼/語法

說明

(exp)

匹配exp,並捕獲文本到自動命名的組裏

(?<name>exp)

匹配exp,並捕獲文本到名稱爲name的組裏,也可以寫成(?'name'exp)

(?:exp)

匹配exp,不捕獲匹配的文本,也不給此分組分配組號


零寬斷言

零寬斷言像\b、^、$一樣用於指定一個位置,但這個位置應該滿足一定的條件,這個條件就被稱之爲斷言。因爲這個斷言不像分組一樣有組號,不消耗匹配的字符串,所以稱之爲零寬斷言。

代碼/語法

說明

(?=exp)

斷言這個位置的後面,一定有匹配exp的內容;

(?<=exp)

斷言這個位置的前面,一定有匹配exp的內容;

(?!exp)

斷言這個位置的後面,一定沒有匹配exp的內容

(?<!exp)

斷言這個位置的前面,一定沒有匹配exp的內容

註釋

註釋用(?#comment)表示。

貪婪與懶惰(greedy and idle)

一段固定的正則表達式,它所能匹配的可能是整個字符串,也可能只是整個字符串的一部分。假如出現一段正則表達式既能匹配整個字符串,也能只匹配部分該如何處理?

/h.*o/.exec("hello ho");  // ["hello ho"]

/h.*?o/.exec("helloho");  // ["hello"]

以上這個例子中,匹配hello使用的是懶惰匹配,而沒有問號限定的則是使用的貪婪匹配。

所謂貪婪匹配就是儘可能多的重複,以匹配儘可能多的字符。反之,懶惰匹配就是匹配儘可能少的字符。默認是貪婪算法,只有在表示數量的元字符後使用了懶惰限定符(也就是問號)才表示這個地方使用懶惰算法。

懶惰限定符

語法

說明

*?

重複任意次,但儘可能少重複

+?

重複1次或更多次,但儘可能少重複

??

重複0次或1次,但儘可能少重複

{n,m}?

重複n到m次,但儘可能少重複

{n,}?

重複n次以上,但儘可能少重複


平衡組(balance group)/遞歸匹配(recursive match)

前面講的所有匹配都是線性的,對於匹配像( 100 * ( 50 + 15 ) )這樣的可嵌套的層次性結構,則上面的一切方法都沒用。因爲你不可能知道括號什麼時候出現,而且如果左括號和右括號出現的次數不相等怎麼辦?如何匹配到最長的,配對的括號之間的內容?

這裏需要用到以下的語法構造:

  • (?'group'exp) 把捕獲的內容命名爲group,並壓入堆棧(Stack)
  • (?'-group'exp) 從堆棧上彈出最後壓入堆棧的名爲group的捕獲內容,如果堆棧本來爲空,則本分組的匹配失敗
  • (?(group)yes|no) 如果堆棧上存在以名爲group的捕獲內容的話,繼續匹配yes部分的表達式,否則繼續匹配no部分
  • (?!) 零寬負向先行斷言,由於沒有exp,試圖匹配總是失敗

爲了避免(和\(把你的大腦徹底搞糊塗,我們還是用尖括號代替圓括號吧。現在我們的問題變成了如何把xx <aa <bbb> <bbb> aa> yy這樣的字符串裏,最長的配對的尖括號內的內容捕獲出來?

<                         #最外層的左括號

    [^<>]*                #最外層的左括號後面的不是括號的內容

    (

        (

           (?'Open'<)    #碰到了左括號,在黑板上寫一個"Open"

           [^<>]*       #匹配左括號後面的不是括號的內容

        )+

        (

           (?'-Open'>)   #碰到了右括號,擦掉一個"Open"

           [^<>]*        #匹配右括號後面不是括號的內容

        )+

    )*

    (?(Open)(?!))         #在遇到最外層的右括號前面,判斷黑板上還有沒有沒擦掉的"Open";如果還有,則匹配失敗

 

>                        #最外層的右括號

更多

常用轉義字符及其他部分元字符

代碼/語法

說明

\a

報警字符(打印它的效果是電腦嘀一聲)

\b

通常是單詞分界位置,但如果在字符類裏使用代表退格

\t

製表符,Tab

\r

回車

\v

豎向製表符

\f

換頁符

\n

換行符

\e

Escape

\0nn

ASCII代碼中八進制代碼爲nn的字符

\xnn

ASCII代碼中十六進制代碼爲nn的字符

\unnnn

Unicode代碼中十六進制代碼爲nnnn的字符

\cN

ASCII控制字符。比如\cC代表Ctrl+C

\A

字符串開頭(類似^,但不受處理多行選項的影響)

\Z

字符串結尾或行尾(不受處理多行選項的影響)

\z

字符串結尾(類似$,但不受處理多行選項的影響)

\G

當前搜索的開頭

\p{name}

Unicode中命名爲name的字符類,例如\p{IsGreek}

 

發佈了157 篇原創文章 · 獲贊 165 · 訪問量 115萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章