正則表示式中常用符號
一:
正則在Perl、Py森、Ruby、Java等語言中文字的正則表示式幾乎是一樣的
以前常用到的在網上都有現成的例子拿來用,比如電話格式、郵箱格式之類的。
但是自然語言處理中往往會根據自己的需求來制定一個表示式,如果正則的知識掌握的比較片面,在編寫自然語言處理程式時可能會覺得苦惱。
在《自然語言處理簡明教程》裡面有很系統的正則表示式教程,特意總結出來消化吸收。
二:
- 雙斜線“//”
最簡單的正則表示式就是這樣的,由類似於/hello world /的正則來搜尋語料庫中包含子字串“hello world”的任何字串相匹配。/e/是可以匹配到字串hello的。
- 中括號“[]”
[]會匹配其中的某一個字元。比如現在有嫌疑人,我們只知道他的名字可能是下面三種的某一種,分別是張偉、李偉或者黃偉。就可以使用/[張李黃]偉/來在人口資料庫中匹配。
正則 |
匹配 |
模式例子 |
/[hH]/ello/ /[abc]/ /[1234567890]/ |
hello or Hello ‘a’或者’b’或者’c’ 數字 |
“Hello!” “happy~” “1993年” |
- {}表示限制長度。如:.{1}表示匹配一個任意字元
- 連字元“-”
用來劃定範圍,表示某一範圍內的任何字元。比如上個例子裡面的/[1234567890]/是不是感覺很不方便。如果表示為/[0-9]/就會顯得精簡的多。
正則 |
匹配 |
模式例子 |
/[A-Z]/ /[a-z]/ /[0-9]/ |
大寫字母 小寫字母 數字 |
“Hello!” “happy~” “1993年” |
- 脫字元“^”
如果在方括號之後有脫字元“^”,對應的模式就是否定的。
正則 |
匹配 |
模式例子 |
/[^A-Z]/ /[^aA]/ /[0-9]/ |
非大寫字母 既不是a也不是A 數字 |
“Hello!” “happy~” “1993年” |
- 問號“?”
比如我們在語料庫中搜索詩人“李白”或者“李太白”,此時方括號就無法幫助我們,因為[]只能表示xx或者XX,但是不能表示有xx或者沒xx。此時可以用“?”來表示前一個字元有或者無。
正則 |
匹配 |
/李太?白/ /times?/ |
“李白”或者“李太白” time或者times |
- “Kleene*”
當正則表示式用來表示重複的字元時,比如在下載檔案時,出現了下面的下載進度:
99.9%
99.99%
99.999%
99.9999%
……
此時我們可以用/9*/來表示重複了“0或者若干次”的字元“9”,因此想表示出現了一次或者多次的“9”,需要用/99*/。
因此/99\.99*%/可以用來表示上述進度條。(此處的“.”需要轉譯,因為我們不想讓他表示為萬用字元)
稍微複雜點:
/[10]*/可以用來表示一個二進位制串,比如“1010100001110001”
- “Kleene+”
但是我用/99*/來表示重複了“0或者若干次”的字元“9”時總覺得多寫了一個“9”很不爽,這時有沒有辦法幫我們省掉多打一個“9”的時間呢?
/9+/就可以了~
因此使用/[10]+/來搜尋語料中的二進位制字串看起來更順手
- 萬用字元“.”
哎那個馬什麼民,幫我把襪子洗一下。
這樣說話是很不禮貌的,但是當我們確實忘了別人名字時怎麼辦呢?
正則表示式同樣可以解決這個問題。
正則裡面有一個點號,萬用字元(/./)可以表示任何字元。
/馬.民/可以在你的班級花名冊裡面搜尋到所有叫“馬X民”的人。
正則 |
匹配 |
模式例子 |
/[beg.n]/ |
beg和n中包含一個字元的字串 |
begin,beg‘n,begun |
當/./和/*/或者/+/碰到時,什麼神奇的事情會發生呢?
比如我們想知道有一天 馬X民對黃偉做了什麼事情,怎麼寫正則來在班級日誌裡面縮小搜尋範圍呢?
/馬.民.+黃偉/就可以表示在一個長字串裡面,符合馬X民bulabula黃偉的句子。
- 錨號“^”和“$”
顧名思義,錨號是用來把正則“錨”在字串的特定位置的。最普通的錨號是“^”和“$”,當“^”用作錨號的時候,表示一行的開始。比如/^The/就表示單詞The必須出現在一句話的開頭。相反“$”表示一行的結尾。
回到上面的例子,比如我們想在班級日誌裡面找到“馬興民xxx黃偉。”這樣的句子,就可以使用錨號來定位。/^馬興民.+黃偉。$/
- 詞界“\b”“\B”
\b表示詞界,\B表示非詞界。
首先,在計算機語言裡面,什麼是一個詞呢。從技術上說,詞被定義為數字、下劃線、或者字母的任何序列。
比如我們在搜尋單詞“a”的時候,如果僅僅用/a/,同樣會匹配到類似於“happy”這樣的單詞,但是如果用/\ba\b/的話,就會只找到單詞a。比如“I am a handsome man.”
它僅僅會匹配到單詞“a”而非“am”。
- 析取符“|”
析取符表示或者。
前面有個小夥子很眼熟,我記得他的名字叫馬興民或者黃偉。現在我開啟線上班級花名冊,輸入正則/黃偉|馬興民/,一下就找到了兩個人的資訊和照片,一看哦都不是。
析取符就是用來表示或者,/a|b/表示a或者b。
在英文語料中,有時候會出現名詞的複數形式比如cat和cats,這種形式我們很容易表達。但是有些複數比如family的複數families該如何表達呢。
首先famil串已經是固定的了,這時我們只需要尾串跟上y或者ies即可,此時我們使用圓括號運算子(),將一個模式括起來,使得它就像一個單獨的字元。因此如果我們想找到語料中的family和families時,需要用/famil(y|ies)/,析取符僅僅運用於字尾y或者ies。
三:
- 通用字符集的替換
正則 |
擴充表示式 |
匹配 |
\d \D \w \W \s \S |
[0-9] [^0-9] [a-zA-Z0-9] [^\w] [_\r\t\n\f] [^\s] |
數字字元 非數字字元 數字字母下劃線 相反 表格,換行等空白區域 非空白區域 |
-
計數符
正則 |
匹配 |
* + ? {n} {n,m} {n,} |
零或多 一或多 零或一 出現n次 n到m次 至少n次 |
- ? 貪婪模式與非貪婪模式
比如在HTML中有許多標籤<div>part1</div><div>part2</div><div>part3</div><div>part4</div>
使用<div>.*</div>匹配到的是:<div>part1</div><div>part2</div><div>part3</div><div>part4</div>,此時為貪婪模式。
使用<div>.*?</div>匹配到的是:<div>part1</div>
- (?<=exp) (?=exp)零寬斷言
(?<=exp) 表示匹配exp之後的,(?=exp)表示匹配exp之前的。
比如上面標籤例子:<div>part1</div><div>part2</div><div>part3</div><div>part4</div>
如果我們僅僅想拿到標籤裡面的內容,就可以使用零寬斷言:
(?<=<div>).*(?=</div>)此時匹配到:part1</div><div>part2</div><div>part3</div><div>part4
結合上面的零寬斷言:
(?<=<div>).*(?=</div>)此時匹配到:part1 part2 part3 part4
- 一個開發思路的例子
當我們想在語料中查詢英語冠詞the的時候,隨手寫下正則/the/。
但是隨後發現有些地方是不完善的,比如當the出現在開頭的時候,就是The,因此我們把正則改成/[tT]he/。
此時這個正則還是不完善的,因為它會找到諸如There,other這樣的單詞,因此我們給它加上了詞界/\b[tT]he\b/。
問題又來了,假如我們不想把下劃線和數字作為詞界,我們還希望能夠找到形如 _the,the99這樣的詞。此時我們需要給the的兩段加上限制:不能為字母。於是正則變成了/[^a-zA-Z][tT]he[^a-zA-Z]/。
萬事大吉了嗎?這時候我們發現,當the出現在開頭的時候,正則找不到它,因為我們規定了the的開頭必須有一個非英文字元,因此我們需要修改開頭為,the出現在開頭或者開頭有一個非英文字元。即/(^|[^a-zA-Z])[tT]he[^a-zA-Z]/。
終於到這裡,一個符合要求的簡單的定冠詞the被搜尋了出來。
平時書寫正則的時候也要注意,提升準確率,提升覆蓋率。