十分鐘正則表達式入門教程
本教程主要針對不熟悉或者對概念上略有模糊的人。我會大致解釋一下正則表達式的各個概念和作用,至於實際使用還需要各位自己反覆練習。另外需要更細緻或者深入的教程可以依次參考以下內容:
- 本專欄作者之一 @shell von 的30分鐘學正則,口水話雖然多了一點,但也大致 cover 的相關的概念
- 已經更新維護了十多年的 正則表達式30分鐘入門教程,最早就是通過這篇教程入門
- Orielly 的《精通正則表達式(Mastering Regular Expression)》第三版
釋題
正則表達式使用單個字元串來描述、匹配一系列符合某個句法規則的字元串。在很多文本編輯器里,正則表達式通常被用來檢索、替換那些符合某個模式的文本。
正則表達式流派很多,不同語言之間使用方式和結果也有不同程度上的差異。所以十分鐘能夠覆蓋的只可能是概念上的內容。只是由於後面 Perl 6 系列的文章多少回引用到相應的概念,所以在這裡有這麼一個導入是非常有必要的,同時也作為一個教程給初學者用。
為了方便使用,所有的代碼採用的是 JavaScript,你可以隨時開啟瀏覽器的開發者工具來測試和學習。
一個簡單的示例
text = "the quick brown fox jumps over the lazy dog";text.match(/.[aeiou]./g);// => [ "he ", "qui", "row", "fox", "jum", " ov", "he ", "laz", "dog" ]
處在 /
和 /g
之間的就是正則表達式本體。這段正則表達式的作用是匹配一個三個字元、其中第二個字元是原因字母( aeiou 之一)的子串。
當然其中有一個例外就是,over
的 ver
本來也應該被匹配到才對。但由於 ov
這個子串已經被匹配了,所以粗線了這麼一個意外的 case。
Pattern 模式
正則表達式表示的是一個特定的匹配規則,又叫匹配模式,就是你想要匹配的內容的直接描述。
比如
text.match(/quick/g) // => [ "quick" ]
Alternative 分支條件
有時候我們可能希望匹配多種情況,比如兩個不同的單詞,這樣可以使用 |
來分隔不同的模式:
text.match(/fox|dog/g) // => [ "fox", "dog" ]
|
符號可以連續使用,比如
text.match(/quick|brown|lazy/g)
Grouping 分組
()
可以用於分組,亦即,把一個正則表達式分解成幾個子表達式。
text.match(/(d|f)o/g) // => [ "fo", "do" ] from "fox" and "dog"
Character Class 字元類
比如我們希望匹配所有的字母,或者一些複雜一點的集合,當然分組配合分支條件的話就可以幫我們實現目標了:
text.match(/(b|c|d|f|g|h|j|k|l|m|n|p|q|r|s|t|v|w|x|y|z)(a|e|i|o|u)/g)// => [ "he", "qu", "ro", "fo", "ju", "ve", "he", "la", "do" ]
不過字元類給了我們更直接的方式:
text.match(/[a-z][aeiou]/g)// => [ "he", "qu", "ro", "fo", "ju", "ve", "he", "la", "do" ]
當然了僅僅是能得到一樣的結果,[a-z]
字元類相比於前面我們寫的那一堆還是不夠的細化的。
另外正則表達式還有一些預定義的字元類:
w
匹配所有字母、數字、連字元和下劃線d
匹配所有數字s
匹配所有空白字元.
(英文句號) 匹配任意字元
如果你想要表示匹配不存在某字元類的字元,可以使用[^]
,或者,把預定義字元類的符號改成大寫字母。
所以這個時候,最前面的那個例子你們應該就能夠看得懂了。
Repeating 重複
如果我們希望一個模式可以被重複匹配,可以用下面幾種方式來表示:
?
重複零次或一次,亦即,該模式是可選的。*
重複零次或多次。+
重複一次或多次 。{m}
重複 m 次。{m,}
重複 m 或者更多次。{m, n}
重複 m 到 n 次。
比如,尋找出現連續5個字母的子串:
text.match(/w{5}/g) // => [ "quick", "brown", "jumps" ]
直到目前,所有的正則表達式還都能跟正則語言掛鉤,但後面就開始魔性了。
Capturing & Back Reference 捕獲和後向引用
()
不僅僅有分組的作用,還會把匹配到的子串作為一個結果以方便後面引用。
先看例子:
text.match(/(w+).*1/g) // => [ "the quick brown fox jumps over the" ]
1
就是用來引用前面分組中捕獲到結果的一種方式。
我們依次拆分這個正則表達式:
(w+)
匹配一個單詞並捕獲.*
匹配任意長的任意字元1
匹配前面捕獲的單詞
換句話說就是匹配兩個一樣的單詞和他們之間的內容。而示例中的文本,只有兩個 the 能滿足這個條件。
捕獲是按順序的,所以對於更多的捕獲可以依次用 1
、2
……來引用他們。但是當表達式異常複雜的時候很難確定順序的,這個時候我們可以使用命名捕獲:
text.match(/(?<word>w+).*k<word>/g)// => [ "the quick brown fox jumps over the" ]
(?<>)
用來給分組命名,k<>
用來引用命名分組。
當然我們也可以選擇不對分組進行捕獲。使用(?:)
就好。:)
Assertion 斷言
(其他翻譯可能把通用的斷言也稱作「環視」)
斷言用於指定所匹配的位置是否滿足要求。其中有一些通用斷言(也有人叫 Anchor 錨):
用來匹配單詞的開始/結束,
B
用來匹配相反的位置。^
和$
用來匹配文本的開始與結束。
示例:
text.match(/w{3}/g) // => [ "the", "fox", "the", "dog" ]text.match(/w{3}B/g)// => [ "qui", "bro", "jum", "ove", "laz" ]
以及一些更靈活的斷言:
(?=)
斷言緊隨其後的位置匹配某模式(?<=)
斷言其前的位置匹配某模式(?!)
斷言緊隨其後的位置不匹配某模式(?<!)
斷言其前的位置不匹配某模式
text.match(/w(?=o)/g) // => [ "r", "f", "d" ]text.match(/(?<=o)w/g) // => [ "w", "x", "v", "g" ]text.match(/w(?![aeiou])w+/g) // 第二個字母不是母音的單詞// => [ "the", "brown", "over", "the" ]
未提及的內容
如前所述,本文僅能概括部分內容,其中另有一些作為深入研究或者參考的內容我把他們列出來,大家可以自行搜索查詢,或者去前面我推薦的內容看:
- 貪婪和懶惰匹配
- 平衡和遞歸
- 回溯
- 語言/流派相關的正則內容
- 實現原理、自動機
參考
- Modified ECMAScript regular expression grammar C++ Reference
- Regular expression Wikipedia
- perlre - perldoc.perl.org Perl Doc
推薦閱讀:
※EOS初學者入門指南(上)
※思維導圖入門應該知道的一些小知識
※「教練,我想打籃……額不,做皮具,怎麼入門?」
※MATLAB-Dijkstra演算法
※篆隸入門是專業學習書法的唯一捷徑!能給個理由嗎?⊙?⊙