免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
python正則表達式很難?一文帶你輕松掌握

本文含 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é)果如下:

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

看最后兩個 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)如下:

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

注:本帖里的 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ù): * + ? {}
  • 表示并列: |
  • 用于提?。?()
  • 用于轉(zhuǎn)義: . ^ $ \

首先定義一個函數(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):

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

下面我們來一一細看。

明確字符

匹配中括號里面的任意一個字符。

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è)定范圍,比如

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

看兩個例子。

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

分析如下:

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

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

補集字符

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

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

看四個例子。

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 有 +號=號 不是 1, 2 和 3,因此它倆匹配

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

2.2

次數(shù)字符

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

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

貪婪模式和非貪婪模式的區(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 附近有三條通路

  1. 上路 跳過 u,代表 零個 u
  2. 中路 穿越 u,代表 一個 u
  3. 下路 循環(huán) u,代表 多個 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 附近有兩條通路

  1. 中路 穿越 u,代表 一個 u
  2. 下路 循環(huán) u,代表 多個 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 附近有兩條通路

  1. 上路 跳過 u,代表 零個 u
  2. 中路 穿越 u,代表 一個 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ù)是 13 位,n = 13
  • 密碼需要 8 位以上,n ≥ 8
  • 公眾號文章標題長度不能超過 64,n ≤ 64
  • 用戶名需要在 8 到 16 位之間,8 ≤ n ≤ 16

這時我們可以設(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']

需要解釋的是例三,匹配五個以下的 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)義。

  • 將特殊字符轉(zhuǎn)成自身含義:用 \ 作用在 ^ . \ 等身上,代表乘方 \^ 、小數(shù)點 \. 和除號 \\
  • 將自身字符轉(zhuǎn)成特殊含義:用 \ 作用在 w d n 等身上,代表字母 \w 、數(shù)字 \d 和新行 \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é)如下(大寫和小寫互補,兩者加一起是全集):

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

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

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

密碼例子

密碼通常有如下要求。

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

根據(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ī)則,如下。

  • '[a-zA-Z\d]\S+ 代表第一字符要是數(shù)字或字母
  • \w+\.[a-z]{2,3} 代表 A.B 這樣的結(jié)構(gòu),其中 A 由若干字母數(shù)字下劃線組成,而 B 由 2 或 3 個小寫字母組成(因為通常郵箱最后就是 com, net, gov, edu 等等)。
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,由你想查看的信息完整度決定。

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Perl 中的正則表達式1
Lua模式匹配
【知識】模式匹配基礎(chǔ)篇
說說Python中search()和match()的區(qū)別?
C# string 中的 @
Java中正則表達式的使用
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服