註:以下文章原文來自於Dr Charles Severance 的 《Python for Informatics》 11.3 組合查詢和抽取 如果我們想以“X-”字元串開頭的行中找到數字,就像下麵兩行字元串: X-DSPAM-Confidence: 0.8475X-DSPAM-Probabilit ...
註:以下文章原文來自於Dr Charles Severance 的 《Python for Informatics》
11.3 組合查詢和抽取
如果我們想以“X-”字元串開頭的行中找到數字,就像下麵兩行字元串:
X-DSPAM-Confidence: 0.8475
X-DSPAM-Probability: 0.0000
但我們不只是要任意行中的任意浮點數,而是具備上面格式的行中的數字。
我們可以創建以下正則表達式來選擇這樣的行:
^X-.*: [0-9.]+
這個表達式的含義是以“X-”兩個字元開頭,後面跟了任意個字元“.*",接著是一個冒號":"和空格" ",在空格之後呢是一個及以上的數字或小數點“[0-9.]+”。大家要註意的是方括弧中的"[.]"不是匹配任何字元,而是匹配真正的".",這與方括弧外的"."要予以區分。
這是一個非常緊湊的表達式,它將非常匹配我們感興趣的行:
import re hand = open('mobx-short.txt') for line in hand: line = line.rstrip() if re.search('^X-.*: [0-9.]+', line) print(line)
當我們運行這個程式,我們可以看到我們想要的數據被完美的過濾顯示。
X-DSPAM-Confidence: 0.8475
X-DSPAM-Probability: 0.0000
X-DSPAM-Confidence: 0.6178
X-DSPAM-Probability: 0.0000
但是我們必須使用split解決提取數字的問題。然而當這個問題簡單到能用split解決時,我們可以使用正則表達式的另一特點,一步達到查找和解析功能。
圓括弧()是正則表達式中的另一特殊字元。當我們添加圓括弧至表達式中,在字元串的匹配過程中它們將被忽略,但是當你使用findall()時,圓括弧表示你想整個正則表達式被匹配,但是你只抽取位於圓括弧內你感興趣的那部分字元串。
所以我們對程式修改如下:
import re hand = open('mbox-short.txt') for line in hand: line = line.rstrip() x = re.findall('^X-.*: ([0-9.]+)', line) if len(x) > 0 : print(x)
我們在正則表達式中對匹配浮點數字部分添加圓括弧,並且用findall()代替search(),返回我們想要的浮點數字部分。這個程式的輸出如下:
['0.8475']
['0.0000']
['0.6178']
['0.0000']
['0.6961']
['0.0000']
..
雖然這些在列表中的數字還需要從字元串轉換為浮點數,但是我們應用正則表達式的能力同時查找和抽取了我們感興趣的的信息。
下麵是使用這個技巧的另一個案例。如果你查看文件,你會發現有許多行是這樣的格式:
Details: http://source.sakaiproject.org/viewsvn/?view=rev&rev=39772
如果我們想用同樣的技巧抽取所有修訂號(行末尾的整數),我們可以這樣編寫代碼:
import re hand = open('mbox-short.txt') for line in hand: line = line.rstrip() x = re.findall('^Details:.*rev=([0-9]+)', line) if len(x) > 0 : print(x)
我們的正則表達式的是這樣的,以"Details:"開頭,之後可以是任意字元”.*",然後是"rev=",最後是一個以上的數字。我們希望行是匹配整個正則表達式,但我們只需要圓括弧中"[0-9]+"的數字。當我們運行程式時,將得到以下輸出:
['39772']
['39771']
['39770']
['39769']
...
記住,"[0-9]+"是貪婪的,它將嘗試抽取任何可能的數字,所以我們得到的每個字元串都有五個數字。正則表達式庫在行的開頭和結尾兩個方向進行擴展,只到它數到一個非數字的字元。
我們可以用正則表達式重做本書先前的一個練習。在這個練習中我們對每個郵件的時間感興趣,我們尋找的行的格式如下:
From [email protected] Sat Jan 5 09:14:16 2008
並且我們想抽取每一行中日期中的小時信息。先前我們通過兩次調用split實現。第一次我們將行分離成單詞,然後我們對第五個單詞基於冒號再次分離,拉出我們感興趣的兩個字元。
假定要查找的行是良好格式化的,那麼只要想到少的代碼就可以實現。但是當你為確保程式中碰到不具備這樣格式而失效,而添加必要的錯誤檢驗(或者一個try/except塊)時,這個代碼將會膨脹到10-15行,並且難以讀懂。
我們可以用下麵的正則表達式使工作更簡單:
^From .* [0-9][0-9]:
這個表達式的含義是以"From "開頭(註意空格),然後跟著任意個字元".*",接著又是一個空格,然後是兩個數字"[0-9][0-9]",再接著是一個冒號。我們要找的就是具備這樣格式的行。
為了在findall中只抽出表示小時的兩位數字,我們將表達式修改如下:
^From .* ([0-9][0-9]):
最後這個程式是這樣的:
import re hand = open('mbox-short.txt') for line in hand: line = line.rstrip() x = re.findall('ˆFrom .* ([0-9][0-9]):', line) if len(x) > 0 : print(x)
程式運行結果如下:
['09']
['18']
['16']
['15']
...