小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

python正則表達(dá)式很難?一文帶你輕松掌握

 南柯郡牧 2020-03-16

python正則表達(dá)式很難?一文帶你輕松掌握

本文含 10026 字 , 27 圖表截屏

建議閱讀 42分鐘

引言

正則表達(dá)式(Regular Expression, RE)就是一組定義某種搜索模式(pattern)的字符。

最簡單的 RE 例子如下。

這是小編準(zhǔn)備的python基礎(chǔ)學(xué)習(xí)資料,關(guān)注,轉(zhuǎn)發(fā),私信小編“01”即可免費(fèi)領(lǐng)??!

python正則表達(dá)式很難?一文帶你輕松掌握

'steven'

很明顯,這樣一個(gè) RE 只能搜索出含 steven 的字符串。

你可以用 Python 代碼來驗(yàn)證,但現(xiàn)在假設(shè)我們還不會(huì)寫,我們可以去 https:///來驗(yàn)證。如下圖右上角所示,匹配成功。

python正則表達(dá)式很難?一文帶你輕松掌握

這樣來搜索未免太傻了,有沒有稍微智能一點(diǎn)的方法。再看下面的 RE。

^s....n$

上面的 RE 定義的模式如下: 任何 6 個(gè)字符的單詞,以 s 開頭 (^s 的效果),以 n 收尾 (n$ 的效果)。 之所以是 6 個(gè)字符,是因?yàn)橛?4 個(gè)點(diǎn) (.) 加上 s 和 n 字符。用上面那個(gè)網(wǎng)站做驗(yàn)證,這個(gè) RE ^s....n$ 的若干匹配結(jié)果如下:

  • seven:不匹配(五個(gè)字母)
  • strong man:不匹配(十個(gè)字母加空格)
  • soften:匹配
  • steven:匹配
  • Steven:不匹配

看最后兩個(gè) steven 和 Steven,區(qū)別是第一個(gè)字母的大小寫,如果我想匹配兩者怎么辦呢?用下面的 RE

^[s|S]....n$

中括號(hào) [] 表示一個(gè)集合,而 | 分隔集合里面的元素,在本例是 s 和 S。意思就是匹配開頭的 s 或 S,結(jié)尾是 n 的 6 字符的單詞。

python正則表達(dá)式很難?一文帶你輕松掌握

python正則表達(dá)式很難?一文帶你輕松掌握

這樣每次固定單詞長度也不太智能吧(比如長度為 n 就需要手動(dòng)輸入 n 個(gè)點(diǎn) .),開頭 s 結(jié)尾 n 的單詞好多呢,我如果都想搜索出來該怎么辦呢?看下面的 RE

^s[a-z]+n$

現(xiàn)在 sun 和 strengthen 都可以匹配出來了。起作用的是 [a-z]+,[a-z] 表示小寫的字母 a 到 z 的集合,而 + 代表大于一次,聯(lián)合在一起的意思就是該單詞“以 s 開頭,以 n 結(jié)尾,中間有大于一個(gè)的任何小寫字母”。

python正則表達(dá)式很難?一文帶你輕松掌握

python正則表達(dá)式很難?一文帶你輕松掌握

但上述模式對(duì)單詞 self-restrain 不起作用,因?yàn)橛袀€(gè)短連接線(hyphen)。

python正則表達(dá)式很難?一文帶你輕松掌握

沒關(guān)系,我們把 - 加入字母集合里,寫成 [a-z-]+,注意第一個(gè) - 表示從 a 到 z,第二個(gè) - 表示短連接線。現(xiàn)在可以匹配 self-restrain 了。

python正則表達(dá)式很難?一文帶你輕松掌握

目前對(duì) RE 有點(diǎn)感覺了吧,即便不會(huì)確切的表示也沒關(guān)系,因?yàn)檫@就是本帖要介紹的。還是那句話,興趣最重要,有興趣才能有效的往下看。

本帖結(jié)構(gòu)如下:

  1. 原始字符串
  2. 五類元字符
  3. 七個(gè)函數(shù)
  4. 三個(gè)實(shí)例

注:本帖里的 RE 可視化可參考鏈接 https://www./ 。

1

原始字符串

原始字符串(raw string)是所有的字符串都是直接按照字面的意思來使用,沒有轉(zhuǎn)義特殊或不能打印的字符,通常簡稱為 r-string。

如果沒有轉(zhuǎn)義字符,原始字符串和普通字符串是一樣的,比如

print('hello')print(r'hello')
hellohello

如果有轉(zhuǎn)義字符(用反斜線 \),原始字符串和普通字符串是不一樣的,比如

print('\blake')print(r'\blake')
lake\blake

因此,不管什么時(shí)候用原始字符串準(zhǔn)沒錯(cuò)。

2

元字符

元字符(meta character)就是一種自帶特殊含義的字符,也叫特殊字符。比如 [] * + ? {} | () . ^ $ \ ,原字符按用途可分五類:

  • 表示集合: []
  • 表示次數(shù): * + ? {}
  • 表示并列: |
  • 用于提?。?()
  • 用于轉(zhuǎn)義: . ^ $ \

首先定義一個(gè)函數(shù),當(dāng)在句子(是個(gè)字符串 str)沒有發(fā)現(xiàn)模式 pat 時(shí),返回“沒有找到”,反之打印出所有符合模式的子字符串。

import redef look_for(pat, str): return '沒有找到' if re.search(pat, str) is None else re.findall(pat, str)

上面代碼中的 re是 Python 中正則表達(dá)式的庫,而 search 和 findall 是包里的兩個(gè)函數(shù),顧名思義它們做的就是 搜索找出全部 的意思,第三節(jié)會(huì)詳解講。

2.1

集合字符

中括號(hào)(square bracket)- []

中括號(hào)表示一個(gè)字符集,即創(chuàng)建的模式匹配中括號(hào)里指定字符集中的任意一個(gè)字符,字符集有三種方式來表現(xiàn):

  • 明確字符: [abc] 會(huì)匹配字符 a,b 或者 c
  • 范圍字符: [a-z] 會(huì)匹配字符 a 到 z
  • 補(bǔ)集字符: [^6] 會(huì)匹配除了 6 以外的字符

下面我們來一一細(xì)看。

明確字符

匹配中括號(hào)里面的任意一個(gè)字符。

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,因此前三個(gè)例子的字符串里都有相應(yīng)字符匹配,而最后例子里的 steven 不包含 a, b 或 c。

模式 [abc] 的可視圖如下,注意 “One of” 是說集合里面的字符是“或”的關(guān)系。

python正則表達(dá)式很難?一文帶你輕松掌握

范圍字符

在 [ ] 中加入 - 即可設(shè)定范圍,比如

  • [a-e] = [abcde]
  • [1-4] = [1234]
  • [a-ep] = [abcdep]
  • [0-38] = [01238]

看兩個(gè)例子。

print( look_for(r'[a-ep]', 'person') )print( look_for(r'[0-38]', '666') )
['p', 'e']沒有找到

分析如下:

  • 例一的模式等價(jià)于 [abcdep],匹配單詞 person 里面的 p 和 e 字符。
  • 例二的模式等價(jià)于 [01238],不匹配單詞 666 里面的任何字符。

模式 [a-ep] 和 [0-38] 的可視圖如下。

python正則表達(dá)式很難?一文帶你輕松掌握

python正則表達(dá)式很難?一文帶你輕松掌握

補(bǔ)集字符

在 [ ] 中加入 ^ 即可除去后面的字符集,比如

  • [^abc] 就是非 a, b, c 的字符
  • [ ^123] 就是非 1, 2, 3 的字符

看四個(gè)例子。

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']['+', '=']

分析如下:

  • 例一 baba 里面所有字母不是 a 就是 b,因此沒有匹配
  • 例二 steven 所有字母都不是 a, b 和 c,因此全部匹配
  • 例三 456 所有字母不是 1,2 和 3,因此全部匹配
  • 例四 1+2=3 有 +號(hào)=號(hào) 不是 1, 2 和 3,因此它倆匹配

模式 [^abc] 和 [^123] 的可視圖如下。注意 “None of” 是說集合里面的字符是的補(bǔ)集。

python正則表達(dá)式很難?一文帶你輕松掌握

python正則表達(dá)式很難?一文帶你輕松掌握

2.2

次數(shù)字符

上面的模式有個(gè)致命短板,就是只能匹配 一個(gè) 字符!這在實(shí)際應(yīng)用幾乎沒用,因此我們需要某些帶有“次數(shù)功能”的元字符,如下:

  • 貪婪模式:
    • * 表示后面可跟 0 個(gè)或多個(gè)字符
    • + 表示后面可跟 1 個(gè)或多個(gè)字符
    • ? 表示后面可跟 0 個(gè)或 1 個(gè)字符
  • 非貪婪模式:
    • *? 表示后面可跟 0 個(gè)或多個(gè)字符,但只取第一個(gè)
    • +? 表示后面可跟 1 個(gè)或多個(gè)字符,但只取第一個(gè)
    • ?? 表示后面可跟 0 個(gè)或 1 個(gè)字符,但只取第一個(gè)

貪婪模式和非貪婪模式的區(qū)別在下面講 ? 的時(shí)候會(huì)介紹。

星號(hào)(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 的可視圖如下。

python正則表達(dá)式很難?一文帶你輕松掌握

注意 u 附近有三條通路

  1. 上路 跳過 u,代表 零個(gè) u
  2. 中路 穿越 u,代表 一個(gè) u
  3. 下路 循環(huán) u,代表 多個(gè) u

加號(hào)(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 的可視圖如下。

python正則表達(dá)式很難?一文帶你輕松掌握

注意 u 附近有兩條通路

  1. 中路 穿越 u,代表 一個(gè) u
  2. 下路 循環(huán) u,代表 多個(gè) u

問號(hào)(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 的可視圖如下。

python正則表達(dá)式很難?一文帶你輕松掌握

注意 u 附近有兩條通路

  1. 上路 跳過 u,代表 零個(gè) u
  2. 中路 穿越 u,代表 一個(gè) u

有的時(shí)候一個(gè)句子里會(huì)有重復(fù)的字符,假如是 > 字符,如果我們要匹配這個(gè)>,到底在哪一個(gè) > 就停止了呢?

這個(gè)就是貪心(greedy)模式和非貪心(non-greedy)模式的區(qū)別,讓我們來看個(gè)例子。

heading  = r'<h1>TITLE</h1>'

如果模式是 <.+> ,那么我們要獲取的就是 以 < 開頭,以 > 結(jié)尾,中間有 1 個(gè)或多個(gè)字符 的字符串。這里我們先提前介紹 . 字符,它是一個(gè)通配符,可以代表任何除新行 (\n) 的字符。

pat = r'<.+>'print( look_for(pat, heading) )
['<h1>TITLE</h1>']

結(jié)果如上,獲取的字符串確實(shí) 以 < 開頭,以 > 結(jié)尾 ,但是仔細(xì)看下,其實(shí)在 heading[3] 出也是 >,為什么沒有匹配到它而是匹配到最后一個(gè) > 呢?

原因就是上面用了 貪婪模式 ,即在整個(gè)表達(dá)式匹配成功的前提下, 盡可能多 的匹配。那么其對(duì)立的 非貪婪模式 ,就是在整個(gè)表達(dá)式匹配成功的前提下, 盡可能少 的匹配。

實(shí)現(xiàn)非貪婪模式只需在最后加一個(gè) ? 字符,代碼如下:

pat = r'<.+?>'print( look_for(pat, heading) )
['<h1>', '</h1>']

結(jié)果無需解釋。

有意思的是,模式 <.+> 和 <.+?> 的可視化圖長得一樣,如下。這樣我們就無法從圖上分辨是否使用貪婪或非貪婪的模式了,只能從代碼中識(shí)別了。

python正則表達(dá)式很難?一文帶你輕松掌握

大括號(hào)(curly bracket)- {}

有的時(shí)候我們非常明確要匹配的字符出現(xiàn)幾次,比如

  • 中國的手機(jī)號(hào)位數(shù)是 13 位,n = 13
  • 密碼需要 8 位以上,n ≥ 8
  • 公眾號(hào)文章標(biāo)題長度不能超過 64,n ≤ 64
  • 用戶名需要在 8 到 16 位之間,8 ≤ n ≤ 16

這時(shí)我們可以設(shè)定 具體 的上界或(和)下界,使得代碼更加有效也更好讀懂,規(guī)則如下:

  • {n} 左邊的字符串是否出現(xiàn) n 次
  • {n, } 左邊的字符串是否出現(xiàn)大于等于 n 次
  • {, n} 左邊的字符串是否出現(xiàn)小于等于 n 次
  • {n, m} 左邊的字符串是否出現(xiàn)在 n 次和 m 次之間

用規(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']

需要解釋的是例三,匹配五個(gè)以下的 a 到 z 小寫字母,當(dāng)然也包括零個(gè),因此結(jié)果包含那些空字符。

模式 {n} , { ,n} , {n, } 和 {n, m} 的可視圖如下:

python正則表達(dá)式很難?一文帶你輕松掌握

上面都是貪婪模式,當(dāng)然也有其對(duì)應(yīng)的非貪婪模式,但只有 {n, m}? 有意義,原因自己想。上面的模式 對(duì)于前一個(gè)字符重復(fù) m 到 n 次,并且取盡可能少的情況。比如在字符串'sssss'中, s{2,4} 會(huì)匹配 4 個(gè) s,但 s{2,4}? 只匹配 2 個(gè) s。

2.3

并列字符

字符集合問題解決了,字符次數(shù)問題解決了,如果現(xiàn)在面臨的問題著是匹配 A 或 B 其中一個(gè)呢?用垂線 | 字符,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)在黑點(diǎn)到白點(diǎn)的并行通路上。

python正則表達(dá)式很難?一文帶你輕松掌握

2.4

提取字符

如果你想把匹配的內(nèi)容提取出來,用小括號(hào),而在小括號(hào)里面你可以設(shè)計(jì)任意正則表達(dá)式。

小括號(hào)(square bracket)- ()

首先定義“beat 的第三人稱,過去式,過去分詞和現(xiàn)在進(jì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”代表 () 起的作用。

python正則表達(dá)式很難?一文帶你輕松掌握

但其實(shí)這不是我們想要的,我們想把 帶著后綴的 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 里面。

python正則表達(dá)式很難?一文帶你輕松掌握

現(xiàn)在 帶著后綴的 beat 已經(jīng)獲取出來了,上面列表中每個(gè)元組的第一個(gè)元素,但完美主義者不想要后綴(即元組的第二個(gè)元素),可以用下面的騷模式。

在 () 中最前面加入 ?: 。 (?:) 代表只匹配不獲?。╪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)只有一個(gè) Group 1,那個(gè)內(nèi)括號(hào),代表不被獲取的內(nèi)容,沒有體現(xiàn)在下圖中。

python正則表達(dá)式很難?一文帶你輕松掌握

2.5

轉(zhuǎn)義字符

字符 集合問題解決了,字符 次數(shù)問題解決了,字符 并列問題解決了,字符 獲取問題解決了,看上去我們能做很多事了。別急,RE 給你最后一擊, 轉(zhuǎn)義字符 ,讓模式更加強(qiáng)大。

轉(zhuǎn)義字符,顧名思義,就是能轉(zhuǎn)換自身含義的字符。

點(diǎn) . 不再是點(diǎn),美元 $ 不再是美元,等等等等。。。

點(diǎn)(dot)- .

點(diǎn) . 表示除新行(newline)的任意字符,它是個(gè)通配符。用它最無腦簡便,但是代碼也最難讀懂,效率也最低下。

定義“ 含有 1 個(gè)或多個(gè)非新行字符 ”的模式。

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)- \

更厲害的是,反斜杠 \ 可對(duì)特殊字符進(jìn)行轉(zhuǎn)義,也可對(duì)普通字符轉(zhuǎn)義。

  • 將特殊字符轉(zhuǎn)成自身含義:用 \ 作用在 ^ . \ 等身上,代表乘方 \^ 、小數(shù)點(diǎn) \. 和除號(hào) \\
  • 將自身字符轉(zhuǎn)成特殊含義:用 \ 作用在 w d n 等身上,代表字母 \w 、數(shù)字 \d 和新行 \n

特殊 --> 自身

在反斜杠的限制下, $ 終于代表美元了!

pat = r'\$[0-9.]+'
print( look_for(pat, 'it costs $99.99') )
['$99.99']

在反斜杠的限制下, ^ . \ 終于代表乘方、小數(shù)點(diǎn)和除號(hào)了!

pat = r'(\\|\/|\^|\.)'
print( look_for(pat, '(5/2)^2=6.25') )
['/', '^', '.']

沒有了反斜杠的限制,一切亂了套,點(diǎn) . 就是通配符,可以匹配字符串里所有字符。

pat = r'(\|/|^|.)'
print( look_for(pat, '(5/2)^2=6.25') )
['', '(', '5', '/', '2', ')', '^', '2', '=', '6', '.', '2', '5']

但如果在中括號(hào) [] 集合里,每個(gè)字符就是它本身的意義,點(diǎn)就是點(diǎn),而不是通配符。

pat = r'[/^\.]'
print( look_for(pat, '(5/2)^2=6.25') )
['/', '^', '.']

自身 --> 特殊

規(guī)則總結(jié)如下(大寫和小寫互補(bǔ),兩者加一起是全集):

  • \b :匹配空字符串,但僅適用于單詞的“首尾”
  • \B :匹配空字符串,但僅適用于單詞的“非首尾”
  • \d :匹配任何“數(shù)字”字符,等價(jià)于 [0-9]
  • \D :匹配任何“非數(shù)字”字符,等價(jià)于 [^0-9]
  • \s :匹配任何“空白”字符,等價(jià)于 [\t\n\r]
  • \S :匹配任何“非空白”字符,等價(jià)于 [^\t\n\r]
  • \w :匹配任何“字母數(shù)字下劃線”字符,等價(jià)于 [a-zA-Z0-9_]
  • \W :匹配任何“非字母數(shù)字下劃線”字符,等價(jià)于 [^a-zA-Z0-9_]
  • \A :匹配句子的“開頭”字符,等價(jià)于 ^
  • \Z :匹配句子的“結(jié)尾”字符,等價(jià)于 $
  • \t :匹配句子的“制表鍵 (tab)”字符
  • \r :匹配句子的“回車鍵 (return)”字符
  • \n :匹配句子的“換行鍵 (newline)”字符
\b \B
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 \D
pat = 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 \S
pat = 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 \W
pat = 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 \Z
pat1 = 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 和 ^ 等價(jià),不解釋。

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 和 $ 等價(jià),不解釋。

2

常用函數(shù)

了解完上節(jié)介紹的元字符的基本知識(shí),本節(jié)的函數(shù)運(yùn)用就很簡單了。RE 包里常見的函數(shù)總結(jié)如下:

  • match(pat, str) :檢查 字符串的開頭 是否符合某個(gè)模式
  • search(pat, str) :檢查 字符串中 是否符合某個(gè)模式
  • findall(pat, str) :返回所有符合某個(gè)模式的字符串,以列表形式輸出
  • finditer(pat, str) :返回所有符合某個(gè)模式的字符串,以迭代器形式輸出
  • split(pat, str) :以某個(gè)模式為分割點(diǎn),拆分整個(gè)句子為一系列字符串,以列表形式輸出
  • sub(pat, repl, str) :句子 str 中找到匹配正則表達(dá)式模式的所有子字符串,用另一個(gè)字符串 repl 進(jìn)行替換
  • compile(pat) :將某個(gè)模式編譯成對(duì)象,供之后使用

match(pat, str)

判斷模式是否在 字符串開頭位置匹配。如果匹配,返回對(duì)象,如果不匹配,返回 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ù)返回的是個(gè)對(duì)象(包括匹配的子字符串和在句中的位置索引),如果只需要子字符串,需要用 group() 函數(shù)。

由于值匹配句頭,那么句中的 Bryant 無法被匹配到。

search(pat, str)

字符串中 查找匹配正則表達(dá)式模式的位置。如果匹配,返回對(duì)象,如果不匹配,返回 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ù)返回的是個(gè)對(duì)象(包括匹配的子字符串和在句中的位置索引),如果只需要子字符串,需要用 group() 函數(shù)。

如果句子出現(xiàn)兩個(gè) 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é)果只匹配出第一個(gè),我們需要下面的函數(shù)來匹配全部。

findall(pat, str)

在字符串中找到正則表達(dá)式所匹配的 所有子串 ,并組成一個(gè)列表返回。

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 類似,在字符串中找到正則表達(dá)式所匹配的所有子串,并組成一個(gè) 迭代器返回。

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)

將字符串匹配正則表達(dá)式的部 拆分開 并返回一個(gè)列表。

s = 'Kobe Bryant loves Gianna Bryant'print( re.split(r'\s', s) )
['Kobe', 'Bryant', 'loves', 'Gianna', 'Bryant']

按空格拆分,不解釋。

sub(pat, repl, str)

句子 str 中找到匹配正則表達(dá)式模式的所有子字符串,用另一個(gè)字符串 repl 進(jìn)行替換。如果沒有找到匹配模式的串,則返回未被修改的句子 str,其中 repl 既可以是字符串也可以是一個(gè)函數(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ù)字( 一個(gè)或多個(gè) ),但句中沒有數(shù)字,因此沒用替代動(dòng)作。

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ù)字( 零 個(gè)或多個(gè) ),雖然句中沒有數(shù)字,但是零個(gè)數(shù)字就是空字符,因此 _ 替代所有空字符。好玩吧 :)

compile(pat)

把正則表達(dá)式的模式轉(zhuǎn)化成正則表達(dá)式 對(duì)象 ,供其他函數(shù)如 match 和 search 使用。對(duì)象創(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 對(duì)象,供之后使用。

pat = r'[\w.-]+@[\w.-]+'obj = re.compile(pat)obj
re.compile(r'[\w.-]+@[\w.-]+', re.UNICODE)

在對(duì)象 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'>]

在對(duì)象 obj 上還可使用 sub 方法,結(jié)果如下:

print( obj.sub('---@---.---', email), '\n' )
Shengyuan Personal: ---@---.---Shengyuan Work: ---@---.---Shengyuan School: ---@---.---Obama: ---@---.---

在對(duì)象 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)建個(gè) RE 對(duì)象 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

密碼例子

密碼通常有如下要求。

  • 最少 8 個(gè)最多 16 個(gè)字符.
  • 至少含有一個(gè)大寫字母,一個(gè)小寫字母,一個(gè)數(shù)字
  • 至少含有一個(gè)特殊字符 @ $ ! % * ? & _,但不包括空格

根據(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é)果好像不太對(duì),因?yàn)槊艽a必須要含有數(shù)字,大小寫和特殊字符。

這時(shí)候需要用 (?=...) 這個(gè)騷操作,意思 就是匹配 ’…’ 之前的字符串 。在本例中 '...' 包括小寫 [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é)果完全正確。

上面模式的可視圖如下:

python正則表達(dá)式很難?一文帶你輕松掌握

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))

python正則表達(dá)式很難?一文帶你輕松掌握

咋一看結(jié)果是對(duì)的,但細(xì)看(高亮處)有些郵箱地址包含了 <> 的符號(hào),或者根本不是正常的郵箱地址,比如 apache@localhost。

這時(shí)候我們需要在模式中添加更多規(guī)則,如下。

  • '[a-zA-Z\d]\S+ 代表第一字符要是數(shù)字或字母
  • \w+\.[a-z]{2,3} 代表 A.B 這樣的結(jié)構(gòu),其中 A 由若干字母數(shù)字下劃線組成,而 B 由 2 或 3 個(gè)小寫字母組成(因?yàn)橥ǔ`]箱最后就是 com, net, gov, edu 等等)。
pat = r'[a-zA-Z\d]\S+@\w+\.[a-z]{2,3}'

python正則表達(dá)式很難?一文帶你輕松掌握

結(jié)果正常。

3.3

摘要例子

在下面摘要中獲取人物、買賣動(dòng)作、股票數(shù)量、股票代號(hào)、日期和股價(jià)這些關(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.'''

給大家留個(gè)任務(wù),讀懂下面代碼,看懂了本帖知識(shí)就掌握了。我相信能看到這里的都可以看懂。

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')]

上面模式的可視圖如下:

python正則表達(dá)式很難?一文帶你輕松掌握

4

總結(jié)

累死了,這次真不想總結(jié)了。

記住元字符的用處:集合、次數(shù)、并列、獲取和轉(zhuǎn)義。

記住函數(shù)的用法:先 compile 成 RE 對(duì)象,在 findall 或 finditer,由你想查看的信息完整度決定。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多