編程入門14:Python模式匹配

上一篇:編程入門13:Python文本處理

我們有時需要判斷一段文本是否符合特定的“模式”(Pattern)豪筝,這稱為文本模式匹配——例如手機號的模式可以描述為“1再加上任意10個數(shù)字”絮蒿,你可以寫一個實現(xiàn)此功能的函數(shù):如果字符串長度為11碗硬,首個字符為1吝秕,其他字符均為數(shù)字,就返回真值,否則返回假值课蔬;而如果需要從一大段文本中找出所有的手機號囱稽,你就得從第一個字符開始循環(huán)截取長度為11的字符串進行判斷——這顯然十分笨拙。

更為靈活高效的做法是使用“正則表達式”(Regular Expression)二跋,這是一種專門描述文本模式規(guī)則的字符串战惊,例如“1\d{10}”就表示“1再加上任意10個數(shù)字”。以下Python代碼使用標準庫提供的re模塊實現(xiàn)正則表達式匹配扎即,從來自“選號網(wǎng)”的一段文本中找出所有的手機號——這顯然優(yōu)雅多了:

In [1]: s = "1881001118835400188101888992360018801392999236001881010005523600"

In [2]: import re

In [3]: re.findall(r"1\d{10}", s)
Out[3]: ['18810011188', '18810188899', '18801392999', '18810100055']

findall()函數(shù)的兩個參數(shù)分別指定正則表達式和目標文本吞获,因為正則表達式里經(jīng)常包含反斜杠,所以推薦使用加r前綴的原始字符串來表示——手機號的首個字符一定是1谚鄙,正則表達式里直接用1來匹配各拷;之后的“\d”表示匹配任一數(shù)字,以下是常用的正則轉義碼:

代碼 轉義說明
\d 數(shù)字類字符闷营,默認也包括全角數(shù)字
\D 非數(shù)字類字符
\w 單詞類字符烤黍,默認也包括漢字等
\W 非單詞類字符
\s 空白類字符,即空格/制表/換行等
\S 非空白類字符
\b 單詞邊界粮坞,用于精確匹配單詞
\B 非單詞邊界

“\d”后用花括號指定匹配10次——這類特殊功能符號如下所示(單純作為文本來匹配時就要加反斜杠):

符號 功能說明 正則示例 目標示例
. 匹配任意字符蚊荣,換行符\n除外 a.c abc acc
\ 轉義 a\.c a.c
* 匹配前一字符0至任意次 abc* ab abccc
+ 匹配前一字符1至任意次 abc+ abc abccc
? 匹配前一字符0至1次 abc? ab abc
{m,n} 匹配前一字符m至n次,省略n則無上限 ab{1,2}c abc abbc
^ 匹配字符串開頭 ^abc abc
$ 匹配字符串末尾 abc$ abc
| abc|def abc def
[] 指定字符集如[abc] a[bc]e abe ace
() 分組 (ab){2}a(12|34)c ababa34c

指定字符集的方括號之內還有更多寫法莫杈,例如[a-z]表示從a到z即任意小寫字母互例,[^abc]表示除abc外的任意字符。

re模塊的常用函數(shù)如下:

  • compile() 編譯正則表達式筝闹,返回模式對象——如果某個正則表達式要在程序中多次使用媳叨,就應先編譯并使用模式對象的相應方法來匹配文本以提高運行效率(以下函數(shù)去掉正則參數(shù)就是模式對象的方法)
  • findall() 在字符串內查找所有匹配文本,返回字符串列表
  • split() 用匹配文本拆分字符串关顷,返回字符串列表
  • sub() 將字符串內匹配文本替換為指定文本糊秆,返回替換后的字符串
  • subn() 將字符串內匹配文本替換為指定文本,返回替換的次數(shù)
  • match() 從字符串開頭匹配文本议双,返回匹配對象
  • search() 在字符串內查找匹配文本痘番,返回匹配對象
  • finditer() 在字符串內查找所有匹配文本,返回匹配對象迭代器

注意match()平痰、search()和finditer()返回的是匹配對象汞舱,匹配對象有下列方法:

  • group() 返回匹配的字符串,如定義了多個分組可以指定分組號
  • start() 返回匹配開始位置
  • end() 返回匹配結束位置
  • span() 返回匹配開始和結束位置(元組類型)
  • groups() 返回匹配的所有分組字符串(元組類型)

以下代碼將文本拆分為單詞(對于中文則是句子)宗雇,注意findall()返回列表而finditer()返回生成迭代器——每次迭代返回一個單詞昂芜,這樣更省內存。

In [4]: po = re.compile(r"\w+")

In [5]: po.findall("Life is short, you need Python.")
Out[5]: ['Life', 'is', 'short', 'you', 'need', 'Python']

In [6]: mo = po.finditer("道可道赔蒲,非常道泌神;名可名良漱,非常名。")

In [7]: for i in mo:
   ...:     print(i.group(), end=" ")
   ...:     
道可道 非常道 名可名 非常名 

添加圓括號可以在正則表達式中創(chuàng)建分組欢际,假如你在匹配手機號的同時還想分別提取其中的“號段”和“地區(qū)碼”母市,就可以使用分組功能:

In [8]: po = re.compile(r"(1\d{2})(\d{4})(\d{4})")

In [9]: mo = po.search("手機號碼:13366669999")

In [10]: mo.group(0)  # 參數(shù)為0與無參數(shù)都返回整個匹配
Out[10]: '13366669999'

In [11]: mo.group(1)  # 參數(shù)為1返回第一個分組,以下依次類推
Out[11]: '133'

In [12]: mo.group(2)
Out[12]: '6666'

In [13]: mo.group(3)
Out[13]: '9999'

In [14]: mo.groups()  # 此方法返回所有分組
Out[14]: ('133', '6666', '9999')

替換類方法如果需要將匹配文本的一部分放入替換文本中损趋,也是通過添加分組窒篱,在替換文本中用反斜杠加組號表示即可:

In [15]: mo = re.compile(r"特工(\w)\w")

In [15]: mo.sub(r"特工\1某", "特工趙大告訴特工錢二:特工孫三將與特工李四接頭。")
Out[15]: '特工趙某告訴特工錢某:特工孫某將與特工李某接頭舶沿。'

正則表達式默認采用最長匹配(也叫“貪婪”匹配),只要規(guī)則允許就匹配盡可能多的字符配并;有時我們需要采用最短匹配括荡,那就在多次匹配符號(*、+溉旋、})后再加一個?號:

In [16]: s = "子曰:“君子坦蕩蕩”畸冲。子曰:“見賢思齊焉”。"

In [17]: re.findall(r"“(.*)”", s)
Out[17]: ['君子坦蕩蕩”观腊。子曰:“見賢思齊焉']

In [18]: re.findall(r"“(.*?)”", s)
Out[18]: ['君子坦蕩蕩', '見賢思齊焉']

以下網(wǎng)絡爬蟲程序使用正則表達式找出網(wǎng)頁中的圖片鏈接并批量下載:

"""webcrawler.py 百度圖片搜索并批量下載
"""
from urllib.request import urlopen, urlretrieve
from urllib.parse import quote
import re
url = "https://image.baidu.com/search/flip?tn=baiduimage&word="
keyword = "高清動漫"
path = "D:/Test/img/"


def main():
    try:
        html = urlopen(url + quote(keyword)).read().decode()
        links = re.findall(r'"objURL":"(.+?)"', html)
        for i in links:
            urlretrieve(i, path + i.split("/")[-1])  # 原文件名保存
    except Exception as e:
        print(repr(e))


if __name__ == "__main__":
    main()
14_img.jpg

學會正則表達式能讓你省下許多寶貴的時間邑闲。

——編程原來是這樣……

編程小提示:在線正則表達式工具

下面是一些在線工具,可以方便地測試正則表達式:

下一篇:編程入門15:Python迭代機制

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末梧油,一起剝皮案震驚了整個濱河市苫耸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌儡陨,老刑警劉巖褪子,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異骗村,居然都是意外死亡嫌褪,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進店門胚股,熙熙樓的掌柜王于貴愁眉苦臉地迎上來笼痛,“玉大人,你說我怎么就攤上這事琅拌∮б粒” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵财忽,是天一觀的道長倘核。 經(jīng)常有香客問我,道長即彪,這世上最難降的妖魔是什么紧唱? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任活尊,我火速辦了婚禮,結果婚禮上漏益,老公的妹妹穿的比我還像新娘蛹锰。我一直安慰自己,他們只是感情好绰疤,可當我...
    茶點故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布铜犬。 她就那樣靜靜地躺著,像睡著了一般轻庆。 火紅的嫁衣襯著肌膚如雪癣猾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天余爆,我揣著相機與錄音纷宇,去河邊找鬼。 笑死蛾方,一個胖子當著我的面吹牛像捶,可吹牛的內容都是我干的。 我是一名探鬼主播桩砰,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拓春,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了亚隅?” 一聲冷哼從身側響起硼莽,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤枢步,失蹤者是張志新(化名)和其女友劉穎沉删,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體醉途,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了震桶。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹲姐。...
    茶點故事閱讀 40,015評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡逢净,死狀恐怖哥放,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情爹土,我是刑警寧澤甥雕,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站胀茵,受9級特大地震影響犀农,放射性物質發(fā)生泄漏。R本人自食惡果不足惜宰掉,卻給世界環(huán)境...
    茶點故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赁濒。 院中可真熱鬧轨奄,春花似錦、人聲如沸拒炎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽击你。三九已至玉组,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間丁侄,已是汗流浹背惯雳。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鸿摇,地道東北人石景。 一個月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像拙吉,于是被迫代替她去往敵國和親潮孽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內容

  • python的re模塊--細說正則表達式 可能是東半球最詳細最全面的re教程,翻譯自官方文檔,因為官方文檔寫的是真...
    立而人閱讀 22,883評論 4 46
  • 第5章 引用類型(返回首頁) 本章內容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的JavaScript類型 使用基本類型...
    大學一百閱讀 3,237評論 0 4
  • #首先筷黔,python中的正則表達式大致分為以下幾部分: 元字符 模式 函數(shù) re 內置對象用法 分組用法 環(huán)視用法...
    mapuboy閱讀 1,610評論 0 51
  • Day 2 V. 語法填空: 用所給單詞的適當形式填空往史,未提供單詞的根據(jù)上下文填入適當?shù)脑~。 England no...
    補習天后閱讀 170評論 0 0
  • 感冒第三天佛舱,依舊沒有好轉的跡象椎例。記憶中好像每一次生病過后總會有一些說不出來的變化挨决。想來小時候的自己總愛生病卻又不喜...
    空城舊夢_閱讀 228評論 0 1