1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        正則表達(dá)式學(xué)習(xí),請(qǐng)參考這篇

        共 17102字,需瀏覽 35分鐘

         ·

        2020-08-16 13:27


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


        最簡(jiǎn)單的 RE 例子如下。

        'steven'

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


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



        這樣來搜索未免太傻了,有沒有稍微智能一點(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 字符的單詞。?



        這樣每次固定單詞長(zhǎng)度也不太智能吧(比如長(zhǎng)度為 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è)的任何小寫字母”。



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



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




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


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


        1. 原始字符串

        2. 五類元字符

        3. 七個(gè)函數(shù)

        4. 三個(gè)實(shí)例


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





        1
        原始字符串


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


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

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


        如果有轉(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 re
        def look_for(pat, str): return '沒有找到' if re.search(pat, str) is None else re.findall(pat, str)


        上面代碼中的 re 是 Python 中正則表達(dá)式的庫(kù),而 searchfindall 是包里的兩個(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)系。






        范圍字符


        [ ]?中加入 - 即可設(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]?的可視圖如下。





        補(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ǔ)集。




        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?的可視圖如下。


        注意?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?的可視圖如下。



        注意?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?的可視圖如下。



        注意?u 附近有兩條通路


        1. 上路跳過?u,代表零個(gè)?u

        2. 中路穿越?u,代表一個(gè)?u




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


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

        heading  = r'

        TITLE

        '


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

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


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


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


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

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

        ', '

        ']


        結(jié)果無(wú)需解釋。


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



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

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


        • 中國(guó)的手機(jī)號(hào)位數(shù)是 13 位,n = 13

        • 密碼需要 8 位以上,n?≥ 8

        • 公眾號(hào)文章標(biāo)題長(zhǎng)度不能超過 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} 的可視圖如下:



        上面都是貪婪模式,當(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)的并行通路上。




        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”代表?() 起的作用。?



        但其實(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 里面。



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


        在 () 中最前面加入 ?:。(?:)?代表只匹配不獲取(non-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)在下圖中。




        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è)通配符。用它最無(wú)腦簡(jiǎn)便,但是代碼也最難讀懂,效率也最低下。


        定義“含有 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']
        沒有找到
        沒有找到
        沒有找到


        \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') )
        沒有找到
        沒有找到
        沒有找到
        ['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') )
        沒有找到
        沒有找到
        ['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']
        沒有找到
        沒有找到


        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']
        沒有找到
        沒有找到

        匹配開頭字符,\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']
        沒有找到
        沒有找到

        匹配結(jié)尾字符,\Z?和?$?等價(jià),不解釋。





        2
        常用函數(shù)


        了解完上節(jié)介紹的元字符的基本知識(shí),本節(jié)的函數(shù)運(yùn)用就很簡(jiǎ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'>
        Kobe
        None


        該函數(shù)返回的是個(gè)對(duì)象(包括匹配的子字符串和在句中的位置索引),如果只需要子字符串,需要用 group() 函數(shù)。


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



        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)] )
        [0, 4), match='Kobe'>]
        [5, 11), match='Bryant'>,
        ?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: [email protected]Shengyuan Work: [email protected]Shengyuan School: [email protected]Obama: [email protected]'''print(email)
        Shengyuan Personal:?quantsteven@gmail.com
        Shengyuan Work: shengyuan@octagon-advisors.com
        Shengyuan School:[email protected]
        Obama:[email protected]



        創(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='[email protected]'>?

        ['[email protected]',
        ?'[email protected]',
        ?'[email protected]',
        ?'[email protected]']

        [<re.Match?object; span=(20,?41), match='[email protected]'>,
        <re.Match?object; span=(58,?88), match='[email protected]'>,
        <re.Match?object; span=(107,?126), match='[email protected]'>,
        <re.Match?object; span=(134,?161), match='[email protected]'>]


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

        print( obj.sub('[email protected]', email), '\n' )
        Shengyuan Personal: [email protected]
        Shengyuan Work: [email protected]
        Shengyuan School: [email protected]
        Obama: [email protected]


        在對(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']
        沒有找到
        沒有找到
        沒有找到
        沒有找到


        結(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') )
        沒有找到
        沒有找到
        沒有找到
        沒有找到
        ['Steven@1031']
        沒有找到
        沒有找到
        沒有找到
        沒有找到

        結(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é)果是對(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}'


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


        上面模式的可視圖如下:






        4
        總結(jié)


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


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


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


        下帖我會(huì)來個(gè)關(guān)于 RE 的實(shí)際案例分析,并記錄我在操作時(shí)遇到的問題和解決方案。


        Stay Tuned!


        瀏覽 20
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            免费国产一级黄色片 | 婷婷五月在线 | 色综合久久久久 | 久久久国产精品视频 | 欧美性美交XXXX | 一级片大片 | xxxwww拍拍视频 | 成人免费无码婬片在线看片视频 | 亚洲精品中文字幕乱码三区91 | 97人人操人人干 |