分鐘學會正則表示式(譯)
正則表示式(“regexes”)即增強查詢/字串替換操作。當在文字編輯器中編輯文字時,正則表示式經常用於:
- 檢查文字是否包含一個給定的模式
- 查詢任何匹配的模式
- 從文字中拉取資訊(比如截斷)
- 修改文字
和文字編輯器一樣,絕大多數高階程式語言支援正則表示式。在本文中,“文字”僅僅是一個字串變數,但是有效的操作卻是一致的。某些程式語言(Perl,JavaScript)甚至為正則表示式提供專用的語法。
但是正則表示式是什麼?
一個正則表示式僅僅為一個字串。它沒有長度限制,但是通常該字串很短。下面看幾個例子:
I had a S+ day today
[A-Za-z0-9-_]{3,16}
dddd-dd-dd
v(d+)(.d+)*
TotalMessages="(.*?)"
<[^<>]>
這個字串實際上是一個極小的計算程式,並且正則表示式是一門語法小而簡潔,領域特定的程式語言。牢記以下幾點,它們不該在學習過程中讓你感到驚訝:
- 每個正則表示式都能分解成一串指令。“找到這個,再找到那個,然後找到其中一個...”
- 一個正則表示式擁有輸入(文字)和輸出(模式匹配,和有些時候的自定義文字)。
- 存在語法錯誤——不是每個字串都是合法的正則表示式!
- 語法有些怪異,也可以說是恐怖。
- 一個正則表示式有時候可以被編譯以便更快執行。
正則實現一直有著顯著的改變。對於本文,我所關注的是那些幾乎每個正則表示式都實現了的核心語法。
練習
獲取一個支援正則的文字編輯器。我推薦Notepad++。
下載一篇很長的散文故事比如Gutenberg出版社出版的H. G. Wells的《時光機器》然後開啟它。
下載一部字典,比如這個,解壓然後開啟。
一切準備就緒,稍後開始練習。
提示:正則表示式與檔案萬用字元語法完全不相容,比如*.xml
。
正則表示式基礎語法
字面值(Literals)
正則表示式由只代表自身的字面值和代表特定含義的元字元組成。
這裡也有一些例子。我會對元字元進行高亮。
I had aS+day today
[A-Za-z0-9-_]{3,16}
dddd-dd-dd
v(d+)(.d+)*
TotalMessages="(.*?)
<[^<>]*>
大部分字元,包括字母數字字元,會以字面值的形式出現。這意味著它們查詢的是自身。比如,正則表示式cat
代表“先找到c
,接著找到a
,最後找到t
”。
目前為止感覺良好。這的確很像
- 一個普通的查詢對話方塊
- Java中的
String.indexOf()
函式 - PHP中的
strpos()
函式 - 等等
提示:除非特別說明,正則表示式是區分大小寫的。然而,絕大多數實現都會提供一個標記來開啟不區分大小寫的功能。
句點(dot)
我們第一個元字元是句號(譯者注:句點,英文句號),.
。一個.
表示匹配任何單個字元。下面這個正則表示式c.t
代表“先找到c
,接著找到任何單個字元,再找到t
”。
在一段文字中,這個表示式將會找到cat
,cot
,czt
,甚至字面值為c.t
的字串(c
,句點,t
),但是不包括ct
或者coot
。
在正則表示式裡,空格是有效的。正則表示式 'c t' 代表”先找到 'c',接著找到空格,再找到 't'“。
任何元字元如果用一個反斜杆進行轉義就會變成字面值。所以上述的正則表示式c.t
就代表“先找到c
,接著找到句號,再找到t
”。
反斜槓是一個元字元,這意味著它也可以使用反斜槓轉義。所以正則表示式ct
代表“先找到c
,接著找到反斜杆,再找到t
”。
注意!在一些實現中,.
會匹配除了換行符的任意字元。這意味著“換行符”在不同的實現中也會變化。 要檢視你的文件。在這篇文章中, 我會確保.
會匹配任意字元。
在其它情況下, 通常會有一個標記來調整這種行為,那就是`DOTALL`或類似的標記
練習
使用你目前所學,在字典中使用正則表示式,匹配一個有兩個z
的單詞,其中這兩個z
離得越遠越好。
練習
在《時光機器》這本書中,使用正則表示式來查詢以介詞收尾的句子。
字元類(Character classes)
字元類是字元在方括號中的集合。表示“找到集合裡任意一個字元”。
- 正則表示式
c[aeiou]t
表示“找到c
後跟一個母音字母,再找到t
”。在一段文字中,將會匹配到cat
,cet
,cit
,cot
和cut
。 - 正則表示式
[0123456789]
表示找到一個數字 - 正則表示式
[a]
和a
意義相同:“找到a
”
一些轉義的例子:
-
[a]
表示“找到一個左方括號緊跟著一個a
,再跟著一個右方括號”。 -
[[]ab]
表示“匹配一個左方括號或者右方括號或者a
或者b
”。 -
[[]]
表示“匹配一個反斜杆或者一個左方括號或者一個右方括號”。(嘔!)
在字元類中順序和重複字元並不重要。[dabaaabcc]
跟[abcd]
一樣。
重要的提示
在字元類內部的“規則”和在字元類內部的規則有所不同。一些字元在字元類內部扮演著元字元的角色,但在字元類外部則充當字面值。還有一些字元做著相反的事。一些字元在兩種情形都為元字元,但在各自情形裡代表不同的含義。
特別地,.
表示“匹配任意字元”,但是[.]
表示“匹配句點”。不能併為一談。
練習
結合目前所學,在字典中,使用正則表示式查詢有連續的母音和連續的子音的單詞。
字元類區間(ranges)
你可以在字元類中使用連字元來表示一個字母或數字的區間:
-
[b-f]
和[bcdef]
都表示“找到一個b
或c
或d
或e
或f
”。 -
[A-Z]
和[ABCDEFGHIJKLMNOPQRSTUVWXYZ]
都表示“匹配大寫字母”。 -
[1-9]
和[123456789]
都表示“匹配一個非零數字”。
連字元在字元類外部使用時並沒有特別都含義。正則表示式a-z
表示“找到一個a
接著跟著一個連字元,然後匹配一個z
”。
區間和單獨的字元可能會共存於吥