《python》-12:字符串和正則表達(dá)式

每一個(gè)不曾起舞的日子岸军,都是對(duì)生命的辜負(fù)突委!

使用正則表達(dá)式

正則表達(dá)式相關(guān)知識(shí)

在編寫(xiě)處理字符串的程序或網(wǎng)頁(yè)時(shí),經(jīng)常會(huì)有查找符合某些復(fù)雜規(guī)則的字符串的需要封锉,正則表達(dá)式就是用于描述這些規(guī)則的工具莽使,換句話說(shuō)正則表達(dá)式是一種工具锐极,它定義了字符串的匹配模式(如何檢查一個(gè)字符串是否有跟某種模式匹配的部分或者從一個(gè)字符串中將與模式匹配的部分提取出來(lái)或者替換掉)。如果你在Windows操作系統(tǒng)中使用過(guò)文件查找并且在指定文件名時(shí)使用過(guò)通配符(*和?)芳肌,那么正則表達(dá)式也是與之類似的用來(lái)進(jìn)行文本匹配的工具溪烤,只不過(guò)比起通配符正則表達(dá)式更強(qiáng)大味咳,它能更精確地描述你的需求(當(dāng)然你付出的代價(jià)是書(shū)寫(xiě)一個(gè)正則表達(dá)式比打出一個(gè)通配符要復(fù)雜得多,要知道任何給你帶來(lái)好處的東西都是有代價(jià)的檬嘀,就如同學(xué)習(xí)一門(mén)編程語(yǔ)言一樣)槽驶,比如你可以編寫(xiě)一個(gè)正則表達(dá)式,用來(lái)查找所有以0開(kāi)頭鸳兽,后面跟著2-3個(gè)數(shù)字掂铐,然后是一個(gè)連字號(hào)“-”,最后是7或8位數(shù)字的字符串(像028-12345678或0813-7654321)揍异,這不就是國(guó)內(nèi)的座機(jī)號(hào)碼嗎全陨。最初計(jì)算機(jī)是為了做數(shù)學(xué)運(yùn)算而誕生的,處理的信息基本上都是數(shù)值衷掷,而今天我們?cè)谌粘9ぷ髦刑幚淼男畔⒒旧隙际俏谋緮?shù)據(jù)辱姨,我們希望計(jì)算機(jī)能夠識(shí)別和處理符合某些模式的文本,正則表達(dá)式就顯得非常重要了戚嗅。今天幾乎所有的編程語(yǔ)言都提供了對(duì)正則表達(dá)式操作的支持雨涛,Python通過(guò)標(biāo)準(zhǔn)庫(kù)中的re模塊來(lái)支持正則表達(dá)式操作。

我們可以考慮下面一個(gè)問(wèn)題:我們從某個(gè)地方(可能是一個(gè)文本文件懦胞,也可能是網(wǎng)絡(luò)上的一則新聞)獲得了一個(gè)字符串替久,希望在字符串中找出手機(jī)號(hào)和座機(jī)號(hào)。當(dāng)然我們可以設(shè)定手機(jī)號(hào)是11位的數(shù)字(注意并不是隨機(jī)的11位數(shù)字躏尉,因?yàn)槟銢](méi)有見(jiàn)過(guò)“25012345678”這樣的手機(jī)號(hào)吧)而座機(jī)號(hào)跟上一段中描述的模式相同蚯根,如果不使用正則表達(dá)式要完成這個(gè)任務(wù)就會(huì)很麻煩。

關(guān)于正則表達(dá)式的相關(guān)知識(shí)胀糜,大家可以閱讀一篇非常有名的博客叫《正則表達(dá)式30分鐘入門(mén)教程》颅拦,讀完這篇文章后你就可以看懂下面的表格,這是我們對(duì)正則表達(dá)式中的一些基本符號(hào)進(jìn)行的扼要總結(jié)教藻。

符號(hào) 解釋 正則表達(dá)式 匹配內(nèi)容
. 匹配任意字符 b.t 可以匹配bat / but / b#t / b1t等
\w 匹配字母/數(shù)字/下劃線 b\wt 可以匹配bat / b1t / b_t等但不能匹配b#t
\s 匹配空白字符(包括\r矩距、\n、\t等) love\syou 可以匹配love you
\d 匹配數(shù)字 \d\d 可以匹配01 / 23 / 99等
\b 匹配單詞的邊界 \bThe\b
^ 匹配字符串的開(kāi)始 ^The 可以匹配The開(kāi)頭的字符串
$ 匹配字符串的結(jié)束 .exe$ 可以匹配.exe結(jié)尾的字符串
\W 匹配非字母/數(shù)字/下劃線 b\Wt 可以匹配b#t / b@t等但不能匹配but / b1t / b_t等
\S 匹配非空白字符 love\Syou 可以匹配love#you等但不能匹配love you
\D 匹配非數(shù)字 \d\D 可以匹配9a / 3# / 0F等
\B 匹配非單詞邊界 \Bio\B
[] 匹配來(lái)自字符集的任意單一字符 [aeiou] 可以匹配任一元音字母字符
[^] 匹配不在字符集中的任意單一字符 [^aeiou] 可以匹配任一非元音字母字符
* 匹配0次或多次 \w* 可以匹配字母/數(shù)字/下劃線(0次或多次)
+ 匹配1次或多次 \w+ 可以匹配字母/數(shù)字/下劃線(1次或多次)
? 匹配0次或1次 \w? 可以匹配字母/數(shù)字/下劃線(0次或多次)
{N} 匹配N(xiāo)次 \w{3} 可以匹配www
{M,} 匹配至少M(fèi)次 \w{3,} www / wwww
{M,N} 匹配至少M(fèi)次至多N次 \w{3,6} www \ wwwww
| 分支 foo|bar 可以匹配foo或者bar
(?#) 注釋
(exp) 匹配exp并捕獲到自動(dòng)命名的組中
(?<name>exp) 匹配exp并捕獲到名為name的組中
(?:exp) 匹配exp但是不捕獲匹配的文本
(?=exp) 匹配exp前面的位置 \b\w+(?=ing) 可以匹配I'm dancing中的danc
(?<=exp) 匹配exp后面的位置 (?<=\bdanc)\w+\b 可以匹配I love dancing and reading中的第一個(gè)ing
(?!exp) 匹配后面不是exp的位置
(?<!exp) 匹配前面不是exp的位置
*? 重復(fù)任意次怖竭,但盡可能少重復(fù) a.*b
a.*?b 將正則表達(dá)式應(yīng)用于aabab,前者會(huì)匹配整個(gè)字符串a(chǎn)abab陡蝇,后者會(huì)匹配aab和ab兩個(gè)字符串
+? 重復(fù)1次或多次痊臭,但盡可能少重復(fù)
?? 重復(fù)0次或1次,但盡可能少重復(fù)
{M,N}? 重復(fù)M到N次登夫,但盡可能少重復(fù)
{M,}? 重復(fù)M次以上广匙,但盡可能少重復(fù)

說(shuō)明:如果需要匹配的字符是正則表達(dá)式中的特殊字符,那么可以使用"\"進(jìn)行轉(zhuǎn)義處理恼策,例如想匹配小數(shù)點(diǎn)可以寫(xiě)成\.就可以了鸦致,因?yàn)橹苯訉?xiě).會(huì)匹配任意字符潮剪;同理,想匹配圓括號(hào)必須寫(xiě)成\(和\)分唾,否則圓括號(hào)被視為正則表達(dá)式中的分組抗碰。

Python對(duì)正則表達(dá)式的支持

Python提供了re模塊來(lái)支持正則表達(dá)式相關(guān)操作,下面是re模塊中的核心函數(shù)绽乔。

函數(shù) 說(shuō)明
compile(pattern, flags=0) 編譯正則表達(dá)式返回正則表達(dá)式對(duì)象
match(pattern, string, flags=0) 用正則表達(dá)式匹配字符串 成功返回匹配對(duì)象 否則返回None
search(pattern, string, flags=0) 搜索字符串中第一次出現(xiàn)正則表達(dá)式的模式 成功返回匹配對(duì)象 否則返回None
split(pattern, string, maxsplit=0, flags=0) 用正則表達(dá)式指定的模式分隔符拆分字符串 返回列表
sub(pattern, repl, string, count=0, flags=0) 用指定的字符串替換原字符串中與正則表達(dá)式匹配的模式 可以用count指定替換的次數(shù)
fullmatch(pattern, string, flags=0) match函數(shù)的完全匹配(從字符串開(kāi)頭到結(jié)尾)版本
findall(pattern, string, flags=0) 查找字符串所有與正則表達(dá)式匹配的模式 返回字符串的列表
finditer(pattern, string, flags=0) 查找字符串所有與正則表達(dá)式匹配的模式 返回一個(gè)迭代器
purge() 清除隱式編譯的正則表達(dá)式的緩存
re.I / re.IGNORECASE 忽略大小寫(xiě)匹配標(biāo)記
re.M / re.MULTILINE 多行匹配標(biāo)記

說(shuō)明:上面提到的re模塊中的這些函數(shù)弧蝇,實(shí)際開(kāi)發(fā)中也可以用正則表達(dá)式對(duì)象的方法替代對(duì)這些函數(shù)的使用,如果一個(gè)正則表達(dá)式需要重復(fù)的使用折砸,那么先通過(guò)compile函數(shù)編譯正則表達(dá)式并創(chuàng)建出正則表達(dá)式對(duì)象無(wú)疑是更為明智的選擇看疗。

下面我們通過(guò)一系列的例子來(lái)告訴大家在Python中如何使用正則表達(dá)式。

例子1:驗(yàn)證輸入用戶名和QQ號(hào)是否有效并給出對(duì)應(yīng)的提示信息睦授。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
created on 2019/5/11
@user: Keekuun
功能描述:
    驗(yàn)證輸入用戶名和QQ號(hào)是否有效并給出對(duì)應(yīng)的提示信息
    要求:用戶名必須由字母两芳、數(shù)字或下劃線構(gòu)成且長(zhǎng)度在6~20個(gè)字符之間,QQ號(hào)是5~12的數(shù)字且首位不能為0
"""

import re


def main():
    username = input('請(qǐng)輸入用戶名:')
    qq = input('請(qǐng)輸入QQ號(hào):')

    '''
        match函數(shù)的第一個(gè)參數(shù)是正則表達(dá)式字符串或正則表達(dá)式對(duì)象
        第二個(gè)參數(shù)是要跟正則表達(dá)式做匹配的字符串對(duì)象
    '''
    re_name = re.match(r'^\w{6,20}$', username)
    re_qq = re.match(r'^[1-9]([0-9]{4,11})$', qq)

    if not re_name:
        print('請(qǐng)輸入有效的用戶名去枷!')
    if not re_qq:
        print('請(qǐng)輸入有效的QQ號(hào)怖辆!')


if __name__ == '__main__':
    main()

提示:上面在書(shū)寫(xiě)正則表達(dá)式時(shí)使用了“原始字符串”的寫(xiě)法(在字符串前面加上了r),所謂“原始字符串”就是字符串中的每個(gè)字符都是它原始的意義沉填,說(shuō)得更直接一點(diǎn)就是字符串中沒(méi)有所謂的轉(zhuǎn)義字符啦疗隶。因?yàn)檎齽t表達(dá)式中有很多元字符和需要進(jìn)行轉(zhuǎn)義的地方,如果不使用原始字符串就需要將反斜杠寫(xiě)作\\翼闹,例如表示數(shù)字的\d得書(shū)寫(xiě)成\\d斑鼻,這樣不僅寫(xiě)起來(lái)不方便,閱讀的時(shí)候也會(huì)很吃力猎荠。

例子2:從一段文字中提取出國(guó)內(nèi)手機(jī)號(hào)碼坚弱。

國(guó)內(nèi)三家運(yùn)營(yíng)商的號(hào)段分別如下:

電信號(hào)段:133/153/180/181/189/177;
聯(lián)通號(hào)段:130/131/132/155/156/185/186/145/176关摇;
移動(dòng)號(hào)段:134/135/136/137/138/139/150/151/152/157/158/159/182/183/184/187/188/147/178荒叶。
#!/usr/bin/env python
# -*- coding: utf-8 -*-

sentence = """
created on 2019/5/11
@user: Keekuun
功能描述:
    國(guó)內(nèi)三家運(yùn)營(yíng)商的號(hào)段分別如下:

    電信號(hào)段:133/153/180/181/189/177;
    聯(lián)通號(hào)段:130/131/132/155/156/185/186/145/176输虱;
    移動(dòng)號(hào)段:134/135/136/137/138/139/150/151/152/157/158/159/182/183/184/187/188/147/178些楣。
    
    例如:我的手機(jī)號(hào)18666999888 
    
    重要的事情說(shuō)8130123456789遍,我的手機(jī)號(hào)是13512346789這個(gè)靚號(hào)宪睹,
    不是15600998765愁茁,也是110或119,王大錘的手機(jī)號(hào)才是15600998765亭病。
"""

import re


def main():
    # 創(chuàng)建正則表達(dá)式對(duì)象,使用了`前瞻`和`回顧`來(lái)保證手機(jī)號(hào)前后不應(yīng)該出現(xiàn)數(shù)字
    pattern = re.compile(r'(?<=\D)1[34578]\d{9}(?=\D)')

    # 查找所有匹配并保存到一個(gè)列表中

    mylist = re.findall(pattern, sentence)
    print(mylist)
    print('--------華麗的分隔線--------')

    # 通過(guò)迭代器取出匹配對(duì)象并獲得匹配的內(nèi)容
    for temp in pattern.finditer(sentence):
        print(temp.group())
    print('--------華麗的分隔線--------')
    
    # 通過(guò)search函數(shù)指定搜索位置找出所有匹配
    m = pattern.search(sentence)
    while m:
        print(m.group())
        m = pattern.search(sentence, m.end())


if __name__ == '__main__':
    main()

說(shuō)明:上面匹配國(guó)內(nèi)手機(jī)號(hào)的正則表達(dá)式并不夠好鹅很,因?yàn)橄?4開(kāi)頭的號(hào)碼只有145或147,而上面的正則表達(dá)式并沒(méi)有考慮這種情況罪帖,要匹配國(guó)內(nèi)手機(jī)號(hào)促煮,更好的正則表達(dá)式的寫(xiě)法是:(?<=\D)(1[38]\d{9}|14[57]\d{8}|15[0-35-9]\d{8}|17[678]\d{8})(?=\D)邮屁。

例子3:替換字符串中的不良內(nèi)容

import re


def main():
    sentence = '你丫是傻叉嗎? 我操你大爺?shù)? Fuck you.'
    purified = re.sub('[操肏艸]|fuck|shit|傻[比屄逼叉缺吊屌]|煞筆',
                      '*', sentence, flags=re.IGNORECASE)
    print(purified)  # 你丫是*嗎? 我*你大爺?shù)? * you.


if __name__ == '__main__':
    main()

說(shuō)明re模塊的正則表達(dá)式相關(guān)函數(shù)中都有一個(gè)flags參數(shù),它代表了正則表達(dá)式的匹配標(biāo)記菠齿,可以通過(guò)該標(biāo)記來(lái)指定匹配時(shí)是否忽略大小寫(xiě)佑吝、是否進(jìn)行多行匹配、是否顯示調(diào)試信息等泞当。如果需要為flags參數(shù)指定多個(gè)值迹蛤,可以使用按位或運(yùn)算符進(jìn)行疊加,如flags=re.I | re.M襟士。

例子4:拆分長(zhǎng)字符串

import re


def main():
    poem = '窗前明月光盗飒,疑是地上霜。舉頭望明月陋桂,低頭思故鄉(xiāng)逆趣。'
    sentence_list = re.split(r'[,嗜历。, .]', poem)
    while '' in sentence_list:
        sentence_list.remove('')
    print(sentence_list)  # ['窗前明月光', '疑是地上霜', '舉頭望明月', '低頭思故鄉(xiāng)']


if __name__ == '__main__':
    main()

后話

如果要從事爬蟲(chóng)類應(yīng)用的開(kāi)發(fā)宣渗,那么正則表達(dá)式一定是一個(gè)非常好的助手,因?yàn)樗梢詭椭覀冄杆俚膹木W(wǎng)頁(yè)代碼中發(fā)現(xiàn)某種我們指定的模式并提取出我們需要的信息梨州,當(dāng)然對(duì)于初學(xué)者來(lái)收痕囱,要編寫(xiě)一個(gè)正確的適當(dāng)?shù)恼齽t表達(dá)式可能并不是一件容易的事情(當(dāng)然有些常用的正則表達(dá)式可以直接在網(wǎng)上找找),所以實(shí)際開(kāi)發(fā)爬蟲(chóng)應(yīng)用的時(shí)候暴匠,有很多人會(huì)選擇Beautiful SoupLxml來(lái)進(jìn)行匹配和信息的提取鞍恢,前者簡(jiǎn)單方便但是性能較差,后者既好用性能也好每窖,但是安裝稍嫌麻煩帮掉,這些內(nèi)容我們會(huì)在后期的爬蟲(chóng)專題中為大家介紹。

exercise python 100 days from https://github.com/jackfrued/Python-100-Days

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末窒典,一起剝皮案震驚了整個(gè)濱河市蟆炊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瀑志,老刑警劉巖涩搓,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異劈猪,居然都是意外死亡昧甘,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)岸霹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人将饺,你說(shuō)我怎么就攤上這事贡避⊥蠢瑁” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵刮吧,是天一觀的道長(zhǎng)湖饱。 經(jīng)常有香客問(wèn)我,道長(zhǎng)杀捻,這世上最難降的妖魔是什么井厌? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮致讥,結(jié)果婚禮上仅仆,老公的妹妹穿的比我還像新娘。我一直安慰自己垢袱,他們只是感情好墓拜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著请契,像睡著了一般咳榜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上爽锥,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天涌韩,我揣著相機(jī)與錄音,去河邊找鬼氯夷。 笑死唾琼,一個(gè)胖子當(dāng)著我的面吹牛瓢娜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼嘁锯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了袋坑?” 一聲冷哼從身側(cè)響起胜榔,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎寂纪,沒(méi)想到半個(gè)月后席吴,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡捞蛋,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年孝冒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拟杉。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡庄涡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出搬设,到底是詐尸還是另有隱情穴店,我是刑警寧澤撕捍,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站泣洞,受9級(jí)特大地震影響忧风,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜球凰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一狮腿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧呕诉,春花似錦缘厢、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至捶闸,卻和暖如春夜畴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背删壮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工贪绘, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人央碟。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓税灌,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親亿虽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子菱涤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容