本文含 10026 字 , 27 圖表截屏
建議閱讀 42分鐘
引言
正則表達式(Regular Expression, RE)就是一組定義某種搜索模式(pattern)的字符。
最簡單的 RE 例子如下。
這是小編準備的python基礎(chǔ)學習資料,關(guān)注,轉(zhuǎn)發(fā),私信小編“01”即可免費領(lǐng)??!
'steven'
很明顯,這樣一個 RE 只能搜索出含 steven 的字符串。
你可以用 Python 代碼來驗證,但現(xiàn)在假設(shè)我們還不會寫,我們可以去 https://regex101.com/來驗證。如下圖右上角所示,匹配成功。
這樣來搜索未免太傻了,有沒有稍微智能一點的方法。再看下面的 RE。
^s....n$
上面的 RE 定義的模式如下: 任何 6 個字符的單詞,以 s 開頭 (^s 的效果),以 n 收尾 (n$ 的效果)。 之所以是 6 個字符,是因為有 4 個點 (.) 加上 s 和 n 字符。用上面那個網(wǎng)站做驗證,這個 RE ^s....n$ 的若干匹配結(jié)果如下:
看最后兩個 steven 和 Steven,區(qū)別是第一個字母的大小寫,如果我想匹配兩者怎么辦呢?用下面的 RE
^[s|S]....n$
中括號 [] 表示一個集合,而 | 分隔集合里面的元素,在本例是 s 和 S。意思就是匹配開頭的 s 或 S,結(jié)尾是 n 的 6 字符的單詞。
這樣每次固定單詞長度也不太智能吧(比如長度為 n 就需要手動輸入 n 個點 .),開頭 s 結(jié)尾 n 的單詞好多呢,我如果都想搜索出來該怎么辦呢?看下面的 RE
^s[a-z]+n$
現(xiàn)在 sun 和 strengthen 都可以匹配出來了。起作用的是 [a-z]+,[a-z] 表示小寫的字母 a 到 z 的集合,而 + 代表大于一次,聯(lián)合在一起的意思就是該單詞“以 s 開頭,以 n 結(jié)尾,中間有大于一個的任何小寫字母”。
但上述模式對單詞 self-restrain 不起作用,因為有個短連接線(hyphen)。
沒關(guān)系,我們把 - 加入字母集合里,寫成 [a-z-]+,注意第一個 - 表示從 a 到 z,第二個 - 表示短連接線?,F(xiàn)在可以匹配 self-restrain 了。
目前對 RE 有點感覺了吧,即便不會確切的表示也沒關(guān)系,因為這就是本帖要介紹的。還是那句話,興趣最重要,有興趣才能有效的往下看。
本帖結(jié)構(gòu)如下:
注:本帖里的 RE 可視化可參考鏈接 https://www.debuggex.com/ 。
1
原始字符串
原始字符串(raw string)是所有的字符串都是直接按照字面的意思來使用,沒有轉(zhuǎn)義特殊或不能打印的字符,通常簡稱為 r-string。
如果沒有轉(zhuǎn)義字符,原始字符串和普通字符串是一樣的,比如
print('hello')print(r'hello')
hellohello
如果有轉(zhuǎn)義字符(用反斜線 \),原始字符串和普通字符串是不一樣的,比如
print('\blake')print(r'\blake')
lake\blake
因此,不管什么時候用原始字符串準沒錯。
2
元字符
元字符(meta character)就是一種自帶特殊含義的字符,也叫特殊字符。比如 [] * + ? {} | () . ^ $ \ ,原字符按用途可分五類:
首先定義一個函數(shù),當在句子(是個字符串 str)沒有發(fā)現(xiàn)模式 pat 時,返回“沒有找到”,反之打印出所有符合模式的子字符串。
import redef look_for(pat, str): return '沒有找到' if re.search(pat, str) is None else re.findall(pat, str)
上面代碼中的 re是 Python 中正則表達式的庫,而 search 和 findall 是包里的兩個函數(shù),顧名思義它們做的就是 搜索 和 找出全部 的意思,第三節(jié)會詳解講。
2.1
集合字符
中括號(square bracket)- []
中括號表示一個字符集,即創(chuàng)建的模式匹配中括號里指定字符集中的任意一個字符,字符集有三種方式來表現(xiàn):
下面我們來一一細看。
明確字符
匹配中括號里面的任意一個字符。
pat = r'[abc]'
print( look_for(pat, 'a') )print( look_for(pat, 'ac') )print( look_for(pat, 'cba') )print( look_for(pat, 'steven') )
['a']['a', 'c']['c', 'b', 'a']沒有找到
分析如下:
該模式只匹配字符 a,b 或者 c,因此前三個例子的字符串里都有相應(yīng)字符匹配,而最后例子里的 steven 不包含 a, b 或 c。
模式 [abc] 的可視圖如下,注意 “One of” 是說集合里面的字符是“或”的關(guān)系。
范圍字符
在 [ ] 中加入 - 即可設(shè)定范圍,比如
看兩個例子。
print( look_for(r'[a-ep]', 'person') )print( look_for(r'[0-38]', '666') )
['p', 'e']沒有找到
分析如下:
模式 [a-ep] 和 [0-38] 的可視圖如下。
補集字符
在 [ ] 中加入 ^ 即可除去后面的字符集,比如
看四個例子。
print( look_for(r'[^abc]', 'baba') )print( look_for(r'[^abc]', 'steven') )print( look_for(r'[^123]', '456') )print( look_for(r'[^123]', '1+2=3') )
沒有找到['s', 't', 'e', 'v', 'e', 'n']['4', '5', '6']['+', '=']
分析如下:
模式 [^abc] 和 [^123] 的可視圖如下。注意 “None of” 是說集合里面的字符是的補集。
2.2
次數(shù)字符
上面的模式有個致命短板,就是只能匹配 一個 字符!這在實際應(yīng)用幾乎沒用,因此我們需要某些帶有“次數(shù)功能”的元字符,如下:
貪婪模式和非貪婪模式的區(qū)別在下面講 ? 的時候會介紹。
星號(asterisk)- *
首先定義“ 字符 u 可以出現(xiàn) 0 次或多次 ”的模式,結(jié)果不需要解釋。
pat = r'colou*r'
print( look_for(pat, 'color') )print( look_for(pat, 'colour') )print( look_for(pat, 'colouuuuuur') )
['color']['colour']['colouuuuuur']
模式 colou*r 的可視圖如下。
注意 u 附近有三條通路
加號(plus sign)- +
首先定義“ 字符 u 可以出現(xiàn) 1 次或多次”的模式,結(jié)果不需要解釋。
pat = r'colou+r'
print( look_for(pat, 'color') )print( look_for(pat, 'colour') )print( look_for(pat, 'colouuuuuur') )
沒有找到['colour']['colouuuuuur']
模式 colou+r 的可視圖如下。
注意 u 附近有兩條通路
問號(question mark)- ?
首先定義“ 字符 u 可以出現(xiàn) 0 次或 1 次”的模式,結(jié)果不需要解釋。
pat = r'colou?r'
print( look_for(pat, 'color') )print( look_for(pat, 'colour') )print( look_for(pat, 'colouuuuuur') )
['color']['colour']沒有找到
模式 colou+r 的可視圖如下。
注意 u 附近有兩條通路
有的時候一個句子里會有重復(fù)的字符,假如是 > 字符,如果我們要匹配這個>,到底在哪一個 > 就停止了呢?
這個就是貪心(greedy)模式和非貪心(non-greedy)模式的區(qū)別,讓我們來看個例子。
heading = r'<h1>TITLE</h1>'
如果模式是 <.+> ,那么我們要獲取的就是 以 < 開頭,以 > 結(jié)尾,中間有 1 個或多個字符 的字符串。這里我們先提前介紹 . 字符,它是一個通配符,可以代表任何除新行 (\n) 的字符。
pat = r'<.+>'print( look_for(pat, heading) )
['<h1>TITLE</h1>']
結(jié)果如上,獲取的字符串確實 以 < 開頭,以 > 結(jié)尾 ,但是仔細看下,其實在 heading[3] 出也是 >,為什么沒有匹配到它而是匹配到最后一個 > 呢?
原因就是上面用了 貪婪模式 ,即在整個表達式匹配成功的前提下, 盡可能多 的匹配。那么其對立的 非貪婪模式 ,就是在整個表達式匹配成功的前提下, 盡可能少 的匹配。
實現(xiàn)非貪婪模式只需在最后加一個 ? 字符,代碼如下:
pat = r'<.+?>'print( look_for(pat, heading) )
['<h1>', '</h1>']
結(jié)果無需解釋。
有意思的是,模式 <.+> 和 <.+?> 的可視化圖長得一樣,如下。這樣我們就無法從圖上分辨是否使用貪婪或非貪婪的模式了,只能從代碼中識別了。
大括號(curly bracket)- {}
有的時候我們非常明確要匹配的字符出現(xiàn)幾次,比如
這時我們可以設(shè)定 具體 的上界或(和)下界,使得代碼更加有效也更好讀懂,規(guī)則如下:
用規(guī)則來看例子,很容易看懂。
s = 'a11bbb2222ccccc'
print( look_for(r'[a-z]{1}', s) )print( look_for(r'[0-9]{2,}', s) )print( look_for(r'[a-z]{,5}', s) )print( look_for(r'[0-9]{2,4}', s) )
['a', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'c']['11', '2222']['a', '', '', 'bbb', '', '', '', '', 'ccccc', '']['11', '2222']
需要解釋的是例三,匹配五個以下的 a 到 z 小寫字母,當然也包括零個,因此結(jié)果包含那些空字符。
模式 {n} , { ,n} , {n, } 和 {n, m} 的可視圖如下:
上面都是貪婪模式,當然也有其對應(yīng)的非貪婪模式,但只有 {n, m}? 有意義,原因自己想。上面的模式 對于前一個字符重復(fù) m 到 n 次,并且取盡可能少的情況。比如在字符串'sssss'中, s{2,4} 會匹配 4 個 s,但 s{2,4}? 只匹配 2 個 s。
2.3
并列字符
字符集合問題解決了,字符次數(shù)問題解決了,如果現(xiàn)在面臨的問題著是匹配 A 或 B 其中一個呢?用垂線 | 字符,A|B,如果 A 匹配了,則不再查找 B,反之亦然。
垂線(vertical line)- |
首先定義“句子出現(xiàn) like 或 love 一詞”的模式。
pat = r'like|love'
print( look_for(pat, 'like you') )print( look_for(pat, 'love you') )
['like']['love']
模式 like|love 的可視圖如下,其并列模式體現(xiàn)在黑點到白點的并行通路上。
2.4
提取字符
如果你想把匹配的內(nèi)容提取出來,用小括號,而在小括號里面你可以設(shè)計任意正則表達式。
小括號(square bracket)- ()
首先定義“beat 的第三人稱,過去式,過去分詞和現(xiàn)在進行式”的模式,為了獲取 beat 加正確后綴的所有單詞。
pat = r'beat(s|ed|en|ing)'
print( look_for(pat, 'beats') )print( look_for(pat, 'beated') )print( look_for(pat, 'beaten') )print( look_for(pat, 'beating') )
['s']['ed']['en']['ing']
我們將出現(xiàn)在 () 里面的后綴都獲取出來了,其可視圖如下,我們發(fā)現(xiàn)“Group 1”代表 () 起的作用。
但其實這不是我們想要的,我們想把 帶著后綴的 beat 給獲取出來。那么只有在最外面再加一層 (),模式如下。
pat = r'(beat(s|ed|en|ing))'
print( look_for(pat, 'beats') )print( look_for(pat, 'beated') )print( look_for(pat, 'beaten') )print( look_for(pat, 'beating') )
[('beats', 's')][('beated', 'ed')][('beaten', 'en')][('beating', 'ing')]
其可視圖如下,我們發(fā)現(xiàn) Group 2 嵌套在 Group 1 里面。
現(xiàn)在 帶著后綴的 beat 已經(jīng)獲取出來了,上面列表中每個元組的第一個元素,但完美主義者不想要后綴(即元組的第二個元素),可以用下面的騷模式。
在 () 中最前面加入 ?: 。 (?:) 代表只匹配不獲?。╪on-capturing),結(jié)果看上去非常自然。
pat = r'(beat(?:s|ed|en|ing))'
print( look_for(pat, 'beats') )print( look_for(pat, 'beated') )print( look_for(pat, 'beaten') )print( look_for(pat, 'beating') )
['beats']['beated']['beaten']['beating']
其可視圖如下,我們發(fā)現(xiàn)只有一個 Group 1,那個內(nèi)括號,代表不被獲取的內(nèi)容,沒有體現(xiàn)在下圖中。
2.5
轉(zhuǎn)義字符
字符 集合問題解決了,字符 次數(shù)問題解決了,字符 并列問題解決了,字符 獲取問題解決了,看上去我們能做很多事了。別急,RE 給你最后一擊, 轉(zhuǎn)義字符 ,讓模式更加強大。
轉(zhuǎn)義字符,顧名思義,就是能轉(zhuǎn)換自身含義的字符。
點 . 不再是點,美元 $ 不再是美元,等等等等。。。
點(dot)- .
點 . 表示除新行(newline)的任意字符,它是個通配符。用它最無腦簡便,但是代碼也最難讀懂,效率也最低下。
定義“ 含有 1 個或多個非新行字符 ”的模式。
pat = r'.+'
print( look_for(pat, 'a') )print( look_for(pat, 'b1') )print( look_for(pat, 'C@9') )print( look_for(pat, '$ 9_fZ') )print( look_for(pat, '9z_\t\r\n') )
['a']['b1']['C@9']['$ 9_fZ']['9z_\t\r']
除了最后例子中的 \n 沒有匹配到,其他的字符全部匹配出來。
托字符(carat)- ^
托字符 ^ 表示字符串開頭。
定義“ 以 s 開頭字符串 ”的模式。
pat = r'^s[\w]*'
print( look_for(pat, 'son') )print( look_for(pat, 'shot') )print( look_for(pat, 'come') )
['son']['shot']沒有找到
結(jié)果太明顯,不解釋。
美元符(dollar sign)- $
美元符 $ 表示字符串結(jié)尾。
定義“ 以 s 結(jié)尾字符串 ”的模式。
pat = r'[\w]*s$'
print( look_for(pat, 'yes') )print( look_for(pat, 'mess') )print( look_for(pat, 'come') )
['yes']['mess']沒有找到
結(jié)果太明顯,不解釋。
反斜杠(backslash)- \
更厲害的是,反斜杠 \ 可對特殊字符進行轉(zhuǎn)義,也可對普通字符轉(zhuǎn)義。
特殊 --> 自身
在反斜杠的限制下, $ 終于代表美元了!
pat = r'\$[0-9.]+'
print( look_for(pat, 'it costs $99.99') )
['$99.99']
在反斜杠的限制下, ^ . \ 終于代表乘方、小數(shù)點和除號了!
pat = r'(\\|\/|\^|\.)'
print( look_for(pat, '(5/2)^2=6.25') )
['/', '^', '.']
沒有了反斜杠的限制,一切亂了套,點 . 就是通配符,可以匹配字符串里所有字符。
pat = r'(\|/|^|.)'
print( look_for(pat, '(5/2)^2=6.25') )
['', '(', '5', '/', '2', ')', '^', '2', '=', '6', '.', '2', '5']
但如果在中括號 [] 集合里,每個字符就是它本身的意義,點就是點,而不是通配符。
pat = r'[/^\.]'
print( look_for(pat, '(5/2)^2=6.25') )
['/', '^', '.']
自身 --> 特殊
規(guī)則總結(jié)如下(大寫和小寫互補,兩者加一起是全集):
pat = r'\blearn\b'print( look_for(pat, 'learn Python') )print( look_for(pat, 'relearn Python') )print( look_for(pat, 'learning Python') )print( look_for(pat, 'relearning Python') )
['learn']沒有找到?jīng)]有找到?jīng)]有找到
\b 只能抓住 learn 前后的 首尾 空字符,那么只能匹配 不帶前綴和后綴 的 learn, 即 learn 本身。
pat = r'\Blearn\B'print( look_for(pat, 'learn Python') )print( look_for(pat, 'relearn Python') )print( look_for(pat, 'learning Python') )print( look_for(pat, 'relearning Python') )
沒有找到?jīng)]有找到?jīng)]有找到['learn']
\B 只能抓住 learn 前后的 非首尾 空字符,那么只能匹配 帶前綴和后綴 的 learn,即 relearning。
pat = r'\blearn\B'print( look_for(pat, 'learn Python') )print( look_for(pat, 'relearn Python') )print( look_for(pat, 'learning Python') )print( look_for(pat, 'relearning Python') )
沒有找到?jīng)]有找到['learn']沒有找到
learn 前 \b 后 \B ,只能匹配 帶后綴 的 learn,即 learning。
pat = r'\Blearn\b'print( look_for(pat, 'learn Python') )print( look_for(pat, 'relearn Python') )print( look_for(pat, 'learning Python') )print( look_for(pat, 'relearning Python') )
沒有找到['learn']沒有找到?jīng)]有找到
learn 前 \B 后 \b ,只能匹配 帶前綴 的 learn,即 relearn。
\d \Dpat = r'\d+'print( look_for(pat, '12+ab34-cd56*ef78/gh90%ij') )
['12', '34', '56', '78', '90']
匹配數(shù)字,不解釋。
pat = r'\D+'print( look_for(pat, '12+ab34-cd56*ef78/gh90%ij') )
['+ab', '-cd', '*ef', '/gh', '%ij']
匹配非數(shù)字,不解釋。
\s \Spat = r'\s+'s = '''please don'tleave me alone'''print( look_for(pat, s) )
[' ', '\n', ' ', '\n ']
匹配各種空格比如制表、回車或新行,不解釋。
pat = r'\S+'print( look_for(pat, s) )
['please', 'don't', 'leave', 'me', 'alone']
匹配各種非空格,不解釋。
\w \Wpat = r'\w+'print( look_for(pat, '12+ab_34-cd56_ef78') )
['12', 'ab_34', 'cd56_ef78']
匹配字母數(shù)字下劃線,不解釋。
pat = r'\W+'print( look_for(pat, '12+ab_34-cd56_ef78') )
['+', '-']
匹配非字母數(shù)字下劃線,不解釋。
\A \Zpat1 = r'^y[\w]*'pat2 = r'\Ay[\w]*'str1 = 'you rock'str2 = 'rock you'print( look_for(pat1, str1) )print( look_for(pat2, str1) )print( look_for(pat1, str2) )print( look_for(pat2, str2) )
['you']['you']沒有找到?jīng)]有找到
匹配開頭字符, \A 和 ^ 等價,不解釋。
pat1 = r'[\w]*k$'pat2 = r'[\w]*k\Z'str1 = 'you rock'str2 = 'rock you'print( look_for(pat1, str1) )print( look_for(pat2, str1) )print( look_for(pat1, str2) )print( look_for(pat2, str2) )
['rock']['rock']沒有找到?jīng)]有找到
匹配結(jié)尾字符, \Z 和 $ 等價,不解釋。
2
常用函數(shù)
了解完上節(jié)介紹的元字符的基本知識,本節(jié)的函數(shù)運用就很簡單了。RE 包里常見的函數(shù)總結(jié)如下:
match(pat, str)
判斷模式是否在 字符串開頭位置匹配。如果匹配,返回對象,如果不匹配,返回 None。
s = 'Kobe Bryant'print( re.match(r'Kobe', s) )print( re.match(r'Kobe', s).group() )print( re.match(r'Bryant', s) )
<re.Match object; span=(0, 4), match='Kobe'>KobeNone
該函數(shù)返回的是個對象(包括匹配的子字符串和在句中的位置索引),如果只需要子字符串,需要用 group() 函數(shù)。
由于值匹配句頭,那么句中的 Bryant 無法被匹配到。
search(pat, str)
在 字符串中 查找匹配正則表達式模式的位置。如果匹配,返回對象,如果不匹配,返回 None。
s = 'Kobe Bryant'print( re.search(r'Kobe', s) )print( re.search(r'Kobe', s).group() )print( re.search(r'Bryant', s) )print( re.search(r'Bryant', s).group() )
<re.Match object; span=(0, 4), match='Kobe'>Kobe<re.Match object; span=(5, 11), match='Bryant'>Bryant
該函數(shù)返回的是個對象(包括匹配的子字符串和在句中的位置索引),如果只需要子字符串,需要用 group() 函數(shù)。
如果句子出現(xiàn)兩個 Bryant 呢?
s = 'Kobe Bryant loves Gianna Bryant'print( re.search(r'Bryant', s) )print( re.search(r'Bryant', s).group() )print( re.search(r'Bryant', s) )
<re.Match object; span=(5, 11), match='Bryant'>Bryant<re.Match object; span=(5, 11), match='Bryant'>
根據(jù)結(jié)果只匹配出第一個,我們需要下面的函數(shù)來匹配全部。
findall(pat, str)
在字符串中找到正則表達式所匹配的 所有子串 ,并組成一個列表返回。
s = 'Kobe Bryant loves Gianna Bryant'print( re.findall(r'Kobe', s) )print( re.findall(r'Bryant', s) )print( re.findall(r'Gigi', s) )
['Kobe']['Bryant', 'Bryant'][]
結(jié)果不解釋。
finditer(pat, str)
和 findall 類似,在字符串中找到正則表達式所匹配的所有子串,并組成一個 迭代器返回。
s = 'Kobe Bryant loves Gianna Bryant'print( [i for i in re.finditer(r'Kobe', s)] )print( [i for i in re.finditer(r'Bryant', s)] )print( [i for i in re.finditer(r'Gigi', s)] )
[<re.Match object; span=(0, 4), match='Kobe'>][<re.Match object; span=(5, 11), match='Bryant'>, <re.Match object; span=(25, 31), match='Bryant'>][]
如果需要匹配子串在原句中的位置索引,用 finditer ,此外用 findall 。
split(pat, str)
將字符串匹配正則表達式的部 拆分開 并返回一個列表。
s = 'Kobe Bryant loves Gianna Bryant'print( re.split(r'\s', s) )
['Kobe', 'Bryant', 'loves', 'Gianna', 'Bryant']
按空格拆分,不解釋。
sub(pat, repl, str)
句子 str 中找到匹配正則表達式模式的所有子字符串,用另一個字符串 repl 進行替換。如果沒有找到匹配模式的串,則返回未被修改的句子 str,其中 repl 既可以是字符串也可以是一個函數(shù)。
s = 'Kobe Bryant loves Gianna Bryant'print( re.sub(r'\s', '-', s) )
Kobe-Bryant-loves-Gianna-Bryant
用 - 代替空格。
print( re.sub(r'Gianna', 'Gigi', s) )
Kobe Bryant loves Gigi Bryant
用 Gigi 代替 Gianna。
print( re.sub(r'\d+', '_', s) )
Kobe Bryant loves Gianna Bryant
用 _ 代替數(shù)字( 一個或多個 ),但句中沒有數(shù)字,因此沒用替代動作。
print( re.sub(r'\d*', '_', s)
_K_o_b_e_ _B_r_y_a_n_t_ _l_o_v_e_s_ _G_i_a_n_n_a_ _B_r_y_a_n_t_
用 _ 代替數(shù)字( 零 個或多個 ),雖然句中沒有數(shù)字,但是零個數(shù)字就是空字符,因此 _ 替代所有空字符。好玩吧 :)
compile(pat)
把正則表達式的模式轉(zhuǎn)化成正則表達式 對象 ,供其他函數(shù)如 match 和 search 使用。對象創(chuàng)建出來可以循環(huán)使用,如果某種模式要重復(fù)使用話,用“先 compile 再 findall”的方式更加高效。
用處理電郵地址來舉例。
email = '''Shengyuan Personal: quantsteven@gmail.comShengyuan Work: shengyuan@octagon-advisors.comShengyuan School: g0700508@nus.edu.sgObama: barack.obama@whitehouse.gov'''print(email)
Shengyuan Personal: quantsteven@gmail.comShengyuan Work: shengyuan@octagon-advisors.comShengyuan School: g0700508@nus.edu.sgObama: barack.obama@whitehouse.gov
創(chuàng)建電郵的模式 r'[\w.-]+@[\w.-]+' ,用 compile 先創(chuàng)建 RE 對象,供之后使用。
pat = r'[\w.-]+@[\w.-]+'obj = re.compile(pat)obj
re.compile(r'[\w.-]+@[\w.-]+', re.UNICODE)
在對象 obj 上分別使用 match, search, findall, findieter 等方法,結(jié)果如下:
print( obj.match(email), '\n')print( obj.search(email), '\n' )print( obj.findall(email), '\n' )print( [i for i in obj.finditer(email)])
None<re.Match object; span=(20, 41), match='quantsteven@gmail.com'> ['quantsteven@gmail.com', 'shengyuan@octagon-advisors.com', 'g0700508@nus.edu.sg', 'barack.obama@whitehouse.gov'][<re.Match object; span=(20, 41), match='quantsteven@gmail.com'>,<re.Match object; span=(58, 88), match='shengyuan@octagon-advisors.com'>,<re.Match object; span=(107, 126), match='g0700508@nus.edu.sg'>,<re.Match object; span=(134, 161), match='barack.obama@whitehouse.gov'>]
在對象 obj 上還可使用 sub 方法,結(jié)果如下:
print( obj.sub('---@---.---', email), '\n' )
Shengyuan Personal: ---@---.---Shengyuan Work: ---@---.---Shengyuan School: ---@---.---Obama: ---@---.---
在對象 obj 上還可使用 split 方法,即把 @ 前后的子串拆分出來,結(jié)果如下:
for addr in obj.findall(email): print( re.split(r'@', addr))
['quantsteven', 'gmail.com']['shengyuan', 'octagon-advisors.com']['g0700508', 'nus.edu.sg']['barack.obama', 'whitehouse.gov']
我們還可以再創(chuàng)建個 RE 對象 obj1,專門用來做拆分。
obj1 = re.compile(r'@')for addr in obj.findall(email): print( obj1.split(addr))
['quantsteven', 'gmail.com']['shengyuan', 'octagon-advisors.com']['g0700508', 'nus.edu.sg']['barack.obama', 'whitehouse.gov']
3
示例展示
3.1
密碼例子
密碼通常有如下要求。
根據(jù)上面要求創(chuàng)建模式,相信都可以讀懂了吧。
pat = r'^[0-9a-zA-Z@!$#%_-]{8,16}$'print( look_for(pat, 'stevenwang') )print( look_for(pat, '19831031') )print( look_for(pat, 'steven1031') )print( look_for(pat, 'steven@1031') )print( look_for(pat, 'Steven@1031') )print( look_for(pat, 's1031') )print( look_for(pat, 's@1031') )print( look_for(pat, 'stevenwang19831031') )print( look_for(pat, 'stevenwang@19831031') )
['stevenwang']['19831031']['steven1031']['steven@1031']['Steven@1031']沒有找到?jīng)]有找到?jīng)]有找到?jīng)]有找到
結(jié)果好像不太對,因為密碼必須要含有數(shù)字,大小寫和特殊字符。
這時候需要用 (?=...) 這個騷操作,意思 就是匹配 ’…’ 之前的字符串 。在本例中 '...' 包括小寫 [a-z],大寫 [A-Z],數(shù)字 \d,特殊字符 [@$!%*?&_],言下之義就是上面這些必須包含中密碼中。
pat = r'^(?=.*[a-z]) (?=.*[A-Z]) (?=.*\d) (?=.*[$@$!%*?&_]) [A-Za-z\d$@$!%*?&_]{8,16}$'print( look_for(pat, 'stevenwang') )print( look_for(pat, '19831031') )print( look_for(pat, 'steven1031') )print( look_for(pat, 'steven@1031') )print( look_for(pat, 'Steven@1031') )print( look_for(pat, 's1031') )print( look_for(pat, 's@1031') )print( look_for(pat, 'stevenwang19831031') )print( look_for(pat, 'stevenwang@19831031') )
沒有找到?jīng)]有找到?jīng)]有找到?jīng)]有找到['Steven@1031']沒有找到?jīng)]有找到?jīng)]有找到?jīng)]有找到
結(jié)果完全正確。
上面模式的可視圖如下:
3.2
郵箱例子
首先定義郵箱地址的模式 '\S+@\S+' ,還記得 \S 是非空格字符,基本代表了所需的字符要求。我們想從從 email.txt 文本中篩選出所有郵箱信息。
pat = r'\S+@\S+'obj = re.compile(pat)email_list = []hand = open('email.txt')for line in hand: line = line.rstrip() email_addr = obj.findall(line)if len(email_addr) > 0: email_list.append(email_addr[0])list(set(email_list))
咋一看結(jié)果是對的,但細看(高亮處)有些郵箱地址包含了 <> 的符號,或者根本不是正常的郵箱地址,比如 apache@localhost。
這時候我們需要在模式中添加更多規(guī)則,如下。
pat = r'[a-zA-Z\d]\S+@\w+\.[a-z]{2,3}'
結(jié)果正常。
3.3
摘要例子
在下面摘要中獲取人物、買賣動作、股票數(shù)量、股票代號、日期和股價這些關(guān)鍵信息。
news = \'''Jack Black sold 15,000 shares in AMZN on 2019-03-06 at a price of $1044.00.David V.Love bought 811 shares in TLSA on 2020-01-19 at a price of $868.75.Steven exercised 262 shares in AAPL on 2020-02-04 at a price of $301.00.'''
給大家留個任務(wù),讀懂下面代碼,看懂了本帖知識就掌握了。我相信能看到這里的都可以看懂。
pat = r'([a-zA-Z. ]*)' \'\s(sold|bought|exercised)' \'\s*([\d,]+)' \'.*in\s([A-Z]{,5})' \'.*(\d{4}-\d{2}-\d{2})' \'.*price of\s(\$\d*.\d*)'re.findall( pat, news )
[('Jack Black', 'sold', '15,000', 'AMZN', '2019-03-06', '$1044.00'), ('David V.Love', 'bought', '811', 'TLSA', '2020-01-19', '$868.75'), ('Steven', 'exercised', '262', 'AAPL', '2020-02-04', '$301.00')]
上面模式的可視圖如下:
4
總結(jié)
累死了,這次真不想總結(jié)了。
記住元字符的用處:集合、次數(shù)、并列、獲取和轉(zhuǎn)義。
記住函數(shù)的用法:先 compile 成 RE 對象,在 findall 或 finditer,由你想查看的信息完整度決定。