正規表示式的功法大全,做NLP再也不怕搞不定字串了

機器之心發表於2018-10-10

選自Medium,作者:Jonny Fox,機器之心編譯。

在自然語言處理中,很多時候我們都需要從文字或字串中抽取出想要的資訊,並進一步做語義理解或其它處理。在本文中,作者由基礎到高階介紹了很多正規表示式,這些表示式或規則在很多程式語言中都是通用的。

正規表示式(regex 或 regexp)對於從文字中抽取資訊極其有用,它一般會搜尋匹配特定模式的語句,而這種模式及具體的 ASCII 序列或 Unicode 字元。從解析/替代字串、預處理資料到網頁爬取,正規表示式的應用範圍非常廣。

其中一個比較有意思的地方是,只要我們學會了正規表示式的語句,我們幾乎可以將其應用於多有的程式語言,包括 JavaScript、Python、Ruby 和 Java 等。只不過對於各程式語言所支援的最高階特徵與語法有細微的區別。

下面我們可以具體討論一些案例與解釋。

基本語句

錨點:^ 和 $

^The        匹配任何以“The”開頭的字串 -> Try it! (https://regex101.com/r/cO8lqs/2)
end$        匹配以“end”為結尾的字串
^The end$   抽取匹配從“The”開始到“end”結束的字串
roar        匹配任何帶有文字“roar”的字串複製程式碼

數量符:*、+、?和 {}

abc*        匹配在“ab”後面跟著零個或多個“c”的字串 -> Try it! (https://regex101.com/r/cO8lqs/1)
abc+        匹配在“ab”後面跟著一個或多個“c”的字串
abc?        匹配在“ab”後面跟著零個或一個“c”的字串
abc{2}      匹配在“ab”後面跟著兩個“c”的字串
abc{2,}     匹配在“ab”後面跟著兩個或更多“c”的字串
abc{2,5}    匹配在“ab”後面跟著2到5個“c”的字串
a(bc)*      匹配在“a”後面跟著零個或更多“bc”序列的字串
a(bc){2,5}  匹配在“a”後面跟著2到5個“bc”序列的字串複製程式碼

或運算子:| 、 []

a(b|c)     匹配在“a”後面跟著“b”或“c”的字串 -> Try it! (https://regex101.com/r/cO8lqs/3)
a[bc]      匹配在“a”後面跟著“b”或“c”的字串複製程式碼

字元類:\d、\d、\s 和 .

\d         匹配數字型的單個字元 -> Try it! (https://regex101.com/r/cO8lqs/4)
\w         匹配單個詞字(字母加下劃線) -> Try it! (https://regex101.com/r/cO8lqs/4)
\s         匹配單個空格字元(包括製表符和換行符) 
.          匹配任意字元 -> Try it! (https://regex101.com/r/cO8lqs/5)複製程式碼

使用「.」運算子需要非常小心,因為常見類或排除型字元類都要更快與精確。\d、\w 和\s 同樣有它們各自的排除型字元類,即\D、\W 和\S。例如\D 將執行與\d 完全相反的匹配方法:

\D         匹配單個非數字型的字元 -> Try it! (https://regex101.com/r/cO8lqs/6)複製程式碼

為了正確地匹配,我們必須使用轉義符反斜槓「\」定義我們需要匹配的符號「^.[$()|*+?{\」,因為我們可能認為這些符號在原文字中有特殊的含義。

\$\d       匹配在單個數字前有符號“$”的字串 -> Try it! (https://regex101.com/r/cO8lqs/9)複製程式碼

注意我們同樣能匹配 non-printable 字元,例如 Tab 符「\t」、換行符「\n」和回車符「\r」

Flags

我們已經瞭解如何構建正規表示式,但仍然遺漏了一個非常基礎的概念:flags。

正規表示式通常以/abc/這種形式出現,其中搜尋模式由兩個反斜槓「/」分離。而在模式的結尾,我們通常可以指定以下 flag 配置或它們的組合:

  • g(global)在第一次完成匹配後並不會返回結果,它會繼續搜尋剩下的文字。

  • m(multi line)允許使用^和$匹配一行的開始和結尾,而不是整個序列。

  • i(insensitive)令整個表示式不區分大小寫(例如/aBc/i 將匹配 AbC)。

中級語句

分組和捕獲:()

a(bc)           圓括弧會建立一個捕獲性分組,它會捕獲匹配項“bc” -> Try it! (https://regex101.com/r/cO8lqs/11)
a(?:bc)*        使用 “?:” 會使捕獲分組失效,只需要匹配前面的“a” -> Try it! (https://regex101.com/r/cO8lqs/12)
a(?<foo>bc)     使用 “?<foo>” 會為分組配置一個名稱 -> Try it! (https://regex101.com/r/cO8lqs/17)複製程式碼

捕獲性圓括號 () 和非捕獲性圓括弧 (?:) 對於從字串或資料中抽取資訊非常重要,我們可以使用 Python 等不同的程式語言實現這一功能。從多個分組中捕獲的多個匹配項將以經典的陣列形式展示:我們可以使用匹配結果的索引訪問它們的值。

如果需要為分組新增名稱(使用 (?<foo>...)),我們就能如字典那樣使用匹配結果檢索分組的值,其中字典的鍵為分組的名稱。

方括弧表示式:[]

[abc]            匹配帶有一個“a”、“ab”或“ac”的字串 -> 與 a|b|c 一樣 -> Try it! (https://regex101.com/r/cO8lqs/7)
[a-c]            匹配帶有一個“a”、“ab”或“ac”的字串 -> 與 a|b|c 一樣
[a-fA-F0-9]      匹配一個代表16進位制數字的字串,不區分大小寫 -> Try it! (https://regex101.com/r/cO8lqs/22)
[0-9]%           匹配在%符號前面帶有0到9這幾個字元的字串
[^a-zA-Z]        匹配不帶a到z或A到Z的字串,其中^為否定表示式 -> Try it! (https://regex101.com/r/cO8lqs/10)複製程式碼

記住在方括弧內,所有特殊字元(包括反斜槓\)都會失去它們應有的意義。

Greedy 和 Lazy 匹配

數量符(* + {})是一種貪心運算子,所以它們會遍歷給定的文字,並儘可能匹配。例如,<.+> 可以匹配文字「This is a <div> simple div</div> test」中的「<div>simple div</div>」。為了僅捕獲 div 標籤,我們需要使用「?」令貪心搜尋變得 Lazy 一點:

<.+?>            一次或多次匹配 “<” 和 “>” 裡面的任何字元,可按需擴充套件 -> Try it! (https://regex101.com/r/cO8lqs/24)複製程式碼

注意更好的解決方案應該需要避免使用「.」,這有利於實現更嚴格的正規表示式:

<[^<>]+>         一次或多次匹配 “<” 和 “>” 裡面的任何字元,除去 “<” 或 “>” 字元 -> Try it! (https://regex101.com/r/cO8lqs/23)複製程式碼

高階語句

邊界符:\b 和 \B

\babc\b          執行整詞匹配搜尋 -> Try it! (https://regex101.com/r/cO8lqs/25)複製程式碼

\b 如插入符號那樣表示一個錨點(它與$和^相同)來匹配位置,其中一邊是一個單詞符號(如\w),另一邊不是單詞符號(例如它可能是字串的起始點或空格符號)。

它同樣能表達相反的非單詞邊界「\B」,它會匹配「\b」不會匹配的位置,如果我們希望找到被單詞字元環繞的搜尋模式,就可以使用它。

\Babc\B          只要是被單詞字元環繞的模式就會匹配 -> Try it! (https://regex101.com/r/cO8lqs/26)複製程式碼

前向匹配和後向匹配:(?=) 和 (?<=)

d(?=r)       只有在後面跟著“r”的時候才匹配“d”,但是“r”並不會成為整個正規表示式匹配的一部分 -> Try it! (https://regex101.com/r/cO8lqs/18)
(?<=r)d      只有在前面跟著“r”時才匹配“d”,但是“r”並不會成為整個正規表示式匹配的一部分 -> Try it! (https://regex101.com/r/cO8lqs/19)複製程式碼

我們同樣能使用否定運運算元:

d(?!r)       只有在後面不跟著“r”的時候才匹配“d”,但是“r”並不會成為整個正規表示式匹配的一部分 -> Try it! (https://regex101.com/r/cO8lqs/20)
(?<!r)d      只有在前面不跟著“r”時才匹配“d”,但是“r”並不會成為整個正規表示式匹配的一部分* *->* **Try it!* (https://regex101.com/r/cO8lqs/21)複製程式碼

結語

正如上文所示,正規表示式的應用領域非常廣,很可能各位讀者在開發的過程中已經遇到了它,下面是正規表示式常用的領域:

  • 資料驗證,例如檢查時間字串是否符合格式;

  • 資料抓取,以特定順序抓取包含特定文字或內容的網頁;

  • 資料包裝,將資料從某種原格式轉換為另外一種格式;

  • 字串解析,例如捕獲所擁有 URL 的 GET 引數,或捕獲一組圓括弧內的文字;

  • 字串替代,將字串中的某個字元替換為其它字元。

    正規表示式的功法大全,做NLP再也不怕搞不定字串了

原文連結:medium.com/factory-min…



相關文章