python中正則表達(dá)式的使用詳解
從學(xué)習(xí)Python至今,發(fā)現(xiàn)很多時候是將Python作為一種工具。特別在文本處理方面,使用起來更是游刃有余。
說到文本處理,那么正則表達(dá)式必然是一個絕好的工具,它能將一些繁雜的字符搜索或者替換以非常簡潔的方式完成。
我們在處理文本的時候,或是查詢抓取,或是替換.
一.查找
如果你想自己實現(xiàn)這樣的功能模塊,輸入某一個ip地址,得到這個ip地址所在地區(qū)的詳細(xì)信息.
然后你發(fā)現(xiàn)http://ip138.com 可以查出很詳細(xì)的數(shù)據(jù)
但是人家沒有提供api供外部調(diào)用,但是我們可以通過代碼模擬查詢?nèi)缓髮Y(jié)果進(jìn)行抓取.
通過查看這個相應(yīng)頁面的源碼,我們可以發(fā)現(xiàn),結(jié)果是放在三個<li></li>中的
<table width="80%" border="0" align="center" cellpadding="0" cellspacing="0">
<tr>
<td align="center"><h3>ip138.com IP查詢(搜索IP地址的地理位置)</h3></td>
</tr>
<tr>
<td align="center"><h1>您查詢的IP:121.0.29.231</h1></td>
</tr>
<tr>
<td align="center"><ul class="ul1"><li>本站主數(shù)據(jù):浙江省杭州市 阿里巴巴</li><li>參考數(shù)據(jù)一:浙江省杭州市 阿里巴巴</li><li>參考數(shù)據(jù)二:浙江省杭州市 阿里巴巴</li></ul></td>
</tr>
<tr>
<td align="center">如果您發(fā)現(xiàn)查詢結(jié)果不詳細(xì)或不正確,請使用<a href="ip_add.asp?ip=121.0.29.231"><font color="#006600"><b>IP數(shù)據(jù)庫自助添加</b></font></a>功能進(jìn)行修正<br/><br/>
<iframe src="/jss/bd_460x60.htm" frameborder="no" width="460" height="60" border="0" marginwidth="0" marginheight="0" scrolling="no"></iframe><br/><br/></td>
</tr>
<form method="get" action="ips8.asp" name="ipform" onsubmit="return checkIP();">
<tr>
<td align="center">IP地址或者域名:<input type="text" name="ip" size="16"> <input type="submit" value="查詢"><input type="hidden" name="action" value="2"></td>
</tr><br>
<br>
</form>
</table>
如果你了解正則表達(dá)式你可能會寫出
正則表達(dá)式
(?<=<li>).*?(?=</li>)
這里使用了前瞻:lookahead 后顧: lookbehind,這樣的好處就是匹配的結(jié)果中就不會包含html的li標(biāo)簽了.
如果你對自己寫的正則表達(dá)式不是很自信的話,可以在一些在線或者本地的正則測試工具進(jìn)行一些測試,以確保正確.
接下來的工作就是如果用Python實現(xiàn)這樣的功能,首先我們得將正則表達(dá)式表示出來:
r"(?<=<li>).*?(?=</li>)"
Python中字符串前面加上前導(dǎo)r這個字符,代表這個字符串是R aw String(原始字符串),也就是說Python字符串本身不會對字符串中的字符進(jìn)行轉(zhuǎn)義.這是因為正則表達(dá)式也有轉(zhuǎn)義字符之說,如果雙重轉(zhuǎn)義的話,易讀性很差.
這樣的串在Python中我們把它叫做"regular expression pattern"
如果我們對pattern進(jìn)行編譯的話
prog = re.compile(r"(?<=<li>).*?(?=</li>)")
我們便可以得到一個正則表達(dá)式對象regular expression object,通過這個對象我們可以進(jìn)行相關(guān)操作.
比如
result=prog.match(string)
##這個等同于
result=re.match(r"(?<=<li>).*?(?=</li>)",string)
##但是如果這個正則需要在程序匹配多次,那么通過正則表達(dá)式對象的方式效率會更高
接下來就是查找了,假設(shè)我們的html結(jié)果已經(jīng)以html的格式存放在text中,那么通過
result_list = re.findall(r"(?<=<li>).*?(?=</li>)",text)
便可以取得所需的結(jié)果列表.
二.替換
使用正則表達(dá)式進(jìn)行替換非常的靈活.
比如之前我在閱讀Trac這個系統(tǒng)中wiki模塊的源代碼的時候,就發(fā)現(xiàn)其wiki語法的實現(xiàn)就是通過正則替換進(jìn)行的.
在使用替換的時候會涉及到正則表達(dá)式中的Group分組的概念.
假設(shè)wiki語法中使用!表示轉(zhuǎn)義字符即感嘆號后面的功能性字符會原樣輸出,粗體的語法為
寫道
'''這里顯示為粗體'''
那么有正則表達(dá)式為
r"(?P<bold>!?''')"
這里的?P<bold>是Python正則語法中的一部分,表示其后的group的名字為"bold"
下面是替換時的情景,其中sub函數(shù)的第一個參數(shù)是pattern,第二個參數(shù)可以是字符串也可以是函數(shù),如果是字符串的話,那么就是將目標(biāo)匹配的結(jié)果替換成指定的結(jié)果,而如果是函數(shù),那么函數(shù)會接受一個match object的參數(shù),并返回替換后的字符串,第三個參數(shù)便是源字符串.
result = re.sub(r"(?P<bold>!?''')", replace, line)
每當(dāng)匹配到一個三單引號,replace函數(shù)便運(yùn)行一次,可能這時候需要一個全局變量記錄當(dāng)前的三單引號是開還是閉,以便添加相應(yīng)的標(biāo)記.
在實際的trac wiki的實現(xiàn)的時候,便是這樣通過一些標(biāo)記變量,來記錄某些語法標(biāo)記的開閉,以決定replace函數(shù)的運(yùn)行結(jié)果.
--------------------
示例
一. 判斷字符串是否是全部小寫
代碼
# -*- coding: cp936 -*-
import re
s1 = 'adkkdk'
s2 = 'abc123efg'
an = re.search('^[a-z]+$', s1)
if an:
print 's1:', an.group(), '全為小寫'
else:
print s1, "不全是小寫!"
an = re.match('[a-z]+$', s2)
if an:
print 's2:', an.group(), '全為小寫'
else:
print s2, "不全是小寫!"
結(jié)果
究其因
1. 正則表達(dá)式不是python的一部分,利用時需要引用re模塊
2. 匹配的形式為: re.search(正則表達(dá)式, 帶匹配字串)或re.match(正則表達(dá)式, 帶匹配字串)。兩者區(qū)別在于后者默認(rèn)以開始符(^)開始。因此,
re.search('^[a-z]+$', s1) 等價于 re.match('[a-z]+$', s2)
3. 如果匹配失敗,則an = re.search('^[a-z]+$', s1)返回None
group用于把匹配結(jié)果分組
例如
import re
a = "123abc456"
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(0) #123abc456,返回整體
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(1) #123
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(2) #abc
print re.search("([0-9]*)([a-z]*)([0-9]*)",a).group(3) #456
1)正則表達(dá)式中的三組括號把匹配結(jié)果分成三組
group() 同group(0)就是匹配正則表達(dá)式整體結(jié)果
group(1) 列出第一個括號匹配部分,group(2) 列出第二個括號匹配部分,group(3) 列出第三個括號匹配部分。
2)沒有匹配成功的,re.search()返回None
3)當(dāng)然鄭則表達(dá)式中沒有括號,group(1)肯定不對了。
二. 首字母縮寫詞擴(kuò)充
具體示例
FEMA Federal Emergency Management Agency
IRA Irish Republican Army
DUP Democratic Unionist Party
FDA Food and Drug Administration
OLC Office of Legal Counsel
分析
縮寫詞 FEMA
分解為 F*** E*** M*** A***
規(guī)律 大寫字母 + 小寫(大于等于1個)+ 空格
參考代碼
import re
def expand_abbr(sen, abbr):
lenabbr = len(abbr)
ma = ''
for i in range(0, lenabbr):
ma += abbr[i] + "[a-z]+" + ' '
print 'ma:', ma
ma = ma.strip(' ')
p = re.search(ma, sen)
if p:
return p.group()
else:
return ''
print expand_abbr("Welcome to Algriculture Bank China", 'ABC')
結(jié)果
問題
上面代碼對于例子中的前3個是正確的,但是后面的兩個就錯了,因為大寫字母開頭的詞語之間還夾雜著小寫字母詞
規(guī)律
大寫字母 + 小寫(大于等于1個)+ 空格 + [小寫+空格](0次或1次)
參考代碼
import re
def expand_abbr(sen, abbr):
lenabbr = len(abbr)
ma = ''
for i in range(0, lenabbr-1):
ma += abbr[i] + "[a-z]+" + ' ' + '([a-z]+ )?'
ma += abbr[lenabbr-1] + "[a-z]+"
print 'ma:', ma
ma = ma.strip(' ')
p = re.search(ma, sen)
if p:
return p.group()
else:
return ''
print expand_abbr("Welcome to Algriculture Bank of China", 'ABC')
技巧
中間的 小寫字母集合+一個空格,看成一個整體,就加個括號。要么同時有,要么同時沒有,這樣需要用到?,匹配前方的整體。
三. 去掉數(shù)字中的逗號
具體示例
在處理自然語言時123,000,000如果以標(biāo)點(diǎn)符號分割,就會出現(xiàn)問題,好好的一個數(shù)字就被逗號肢解了,因此可以先下手把數(shù)字處理干凈(逗號去掉)。
分析
數(shù)字中經(jīng)常是3個數(shù)字一組,之后跟一個逗號,因此規(guī)律為:***,***,***
正則式
[a-z]+,[a-z]?
參考代碼3-1
import re
sen = "abc,123,456,789,mnp"
p = re.compile("\d+,\d+?")
for com in p.finditer(sen):
mm = com.group()
print "hi:", mm
print "sen_before:", sen
sen = sen.replace(mm, mm.replace(",", ""))
print "sen_back:", sen, '\n'
結(jié)果
技巧
使用函數(shù)finditer(string[, pos[, endpos]]) | re.finditer(pattern, string[, flags]):
搜索string,返回一個順序訪問每一個匹配結(jié)果(Match對象)的迭代器。
參考代碼3-2
sen = "abc,123,456,789,mnp"
while 1:
mm = re.search("\d,\d", sen)
if mm:
mm = mm.group()
sen = sen.replace(mm, mm.replace(",", ""))
print sen
else:
break
結(jié)果
延伸
這樣的程序針對具體問題,即數(shù)字3位一組,如果數(shù)字混雜與字母間,干掉數(shù)字間的逗號,即把“abc,123,4,789,mnp”轉(zhuǎn)化為“abc,1234789,mnp”
思路
更具體的是找正則式“數(shù)字,數(shù)字”找到后用去掉逗號的替換
參考代碼3-3
sen = "abc,123,4,789,mnp"
while 1:
mm = re.search("\d,\d", sen)
if mm:
mm = mm.group()
sen = sen.replace(mm, mm.replace(",", ""))
print sen
else:
break
print sen
結(jié)果
四. 中文處理之年份轉(zhuǎn)換(例如:一九四九年--->1949年)
中文處理涉及到編碼問題。例如下邊的程序識別年份(****年)時
# -*- coding: cp936 -*-
import re
m0 = "在一九四九年新中國成立"
m1 = "比一九九零年低百分之五點(diǎn)二"
m2 = '人一九九六年擊敗俄軍,取得實質(zhì)獨(dú)立'
def fuc(m):
a = re.findall("[零|一|二|三|四|五|六|七|八|九]+年", m)
if a:
for key in a:
print key
else:
print "NULL"
fuc(m0)
fuc(m1)
fuc(m2)
運(yùn)行結(jié)果
可以看出第二個、第三個都出現(xiàn)了錯誤。
改進(jìn)——準(zhǔn)化成unicode識別
# -*- coding: cp936 -*-
import re
m0 = "在一九四九年新中國成立"
m1 = "比一九九零年低百分之五點(diǎn)二"
m2 = '人一九九六年擊敗俄軍,取得實質(zhì)獨(dú)立'
def fuc(m):
m = m.decode('cp936')
a = re.findall(u"[\u96f6|\u4e00|\u4e8c|\u4e09|\u56db|\u4e94|\u516d|\u4e03|\u516b|\u4e5d]+\u5e74", m)
if a:
for key in a:
print key
else:
print "NULL"
fuc(m0)
fuc(m1)
fuc(m2)
結(jié)果
識別出來可以通過替換方式,把漢字替換成數(shù)字。
參考
numHash = {}
numHash['零'.decode('utf-8')] = '0'
numHash['一'.decode('utf-8')] = '1'
numHash['二'.decode('utf-8')] = '2'
numHash['三'.decode('utf-8')] = '3'
numHash['四'.decode('utf-8')] = '4'
numHash['五'.decode('utf-8')] = '5'
numHash['六'.decode('utf-8')] = '6'
numHash['七'.decode('utf-8')] = '7'
numHash['八'.decode('utf-8')] = '8'
numHash['九'.decode('utf-8')] = '9'
def change2num(words):
print "words:",words
newword = ''
for key in words:
print key
if key in numHash:
newword += numHash[key]
else:
newword += key
return newword
def Chi2Num(line):
a = re.findall(u"[\u96f6|\u4e00|\u4e8c|\u4e09|\u56db|\u4e94|\u516d|\u4e03|\u516b|\u4e5d]+\u5e74", line)
if a:
print "------"
print line
for words in a:
newwords = change2num(words)
print words
print newwords
line = line.replace(words, newwords)
return line
相關(guān)文章
django 數(shù)據(jù)庫連接模塊解析及簡單長連接改造方法
今天小編就為大家分享一篇django 數(shù)據(jù)庫連接模塊解析及簡單長連接改造方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-08-08Python應(yīng)用領(lǐng)域和就業(yè)形勢分析總結(jié)
在本篇文章總我們給大家整理了關(guān)于Python應(yīng)用領(lǐng)域和就業(yè)形勢分析以及圖文介紹,需要的朋友們可以參考下。2019-05-05python實現(xiàn)簡易的學(xué)生信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了python實現(xiàn)簡易的學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-05-05python 安裝virtualenv和virtualenvwrapper的方法
下面小編就為大家?guī)硪黄猵ython 安裝virtualenv和virtualenvwrapper的方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01python操作數(shù)據(jù)庫之sqlite3打開數(shù)據(jù)庫、刪除、修改示例
這篇文章主要介紹了python操作sqlite3打開數(shù)據(jù)庫、刪除、修改示例,需要的朋友可以參考下2014-03-03django自帶的權(quán)限管理Permission用法說明
這篇文章主要介紹了django自帶的權(quán)限管理Permission用法說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-05-05