作者:zhbzz2007 出處:http://www.cnblogs.com/zhbzz2007 歡迎轉載,也請保留這段聲明。謝謝!
1 模塊簡介
正則表達式是一門小語言,你可以在Python中或者其他編程語言中使用。你經常可以看到正則表達式可以寫為“regex”,“regexp”或者就是“RE”。一些語言,例如Perl或者Ruby,語言本身直接支持正則表達式。Python通過一個庫來支持正則表達式,因此你需要引入這個庫。正則表達式的主要用途就是匹配字符串。你先通過正則表達式來創建字符串匹配規則,然后將其應用到字符串,觀察是否存在匹配。
正則表達式作為一門小語言,你不可能使用它來滿足所有的字符串匹配需求。另外,盡管有些任務你可以使用正則表達式來完成,但是它可能非常復雜,以至於很難去調試。這種情況下,你應該只使用Python。值得注意的是,Python在文本分析方面是一門優秀的語言,它可以完成你通過正則表達式所做的任何事情。但是,它或許需要寫很多代碼來完成,並且會比正則表達式的速度慢,因為正則表達式是在C語言下編譯和執行的。
2 模塊使用
2.1 匹配字符
當你想匹配字符串中的一個字符,大部分情況下,你可以僅僅使用那個字符或者子字符串。如果我們想匹配“dog”,我們就使用字母“dog”。當然,正則表達式保留了一些字符。這些就是元字符。下面就是Python中正則表達式所支持的元字符,
.^$*+?{}[]|()
下面我們將會花費一些時間來了解它們是如何工作的。你將會遇到的一個最常見的元字符對就是方括號:[和]。它們經常被用於創建字符類,就是你想匹配的一個字符集合。你或許單獨列出每個字符,如[xyz]。這個就會匹配括號中任意一個字符。你也可以使用破折號來表示一個字符范圍,例如[a-g]。在這個例子中,我們將會匹配從a到g中任意一個字符。
為了執行一個實際的搜索過程,我們需要增加一個起始字符來進行查找,另外還需要一個結束字符。為了讓這個過程更容易,我們使用星號,它允許重復,而不是匹配*, *將會告訴正則表達式,先前的字符可以匹配0次或者多次。
一個簡單的例子如下所示,
a[b-f]*f
這個正則表達式意味着,我們首先查找字符a,0個或者多個[b-f]中的字符,需要以字符f結束。讓我們在Python中使用這段代碼,
>>> import re
>>> text = "abcdfghijk"
>>> parser = re.search('a[b-f]*f',text)
>>> parser
<_sre.SRE_Match object at 0x106b30b90>
>>> parser.group()
'abcdf'
這段代碼中,表達式會首先查找我們傳入的字符串,在這里,字符串就是“abcdfghijk”。它將會發現開始位置的字母a會被匹配上。有一個末尾是星號的字符類,它將會讀取剩余的字符串,觀看是否匹配。如果不匹配,它就會回退一個字符來嘗試找到一個匹配。
最神奇的地方就是我們調用re模塊的search函數。如果我們沒有找到一個匹配,就會返回None。否則,我們就獲得一個Match對象,你可以在上述代碼中看到。為了看到實際匹配上什么,你需要調用group方法。
還有另外一個類似於 * 的重復元字符。它就是 + ,將會匹配一次或者多次。這與 * 有所不同, * 只匹配0次或者多次。 + 需要查找的字符至少出現一次。
最后兩個重復的元字符也有所不同。?,只匹配一次或者0次,意味着它前面的字符是可選的。一個簡單的例子就是“co-op”,它就會匹配“coop”和“co-op”。
最后一個重復元字符就是{a,b},這里a和b都是十進制整數。這就意味着,它必須至少重復a次,最多重復b次。你可以按照如下方式來書寫,
xb{1,4}z
這是一個很簡單的例子,但是它將會匹配xbz,xbbz,xbbbz和xbbbbz,xz不會匹配上,因為它沒有字符b。
下一個元字符就是^。這個字符允許我們匹配沒有出現在字符類中羅列出的字符,它是字符類的補集。只有我們將^放在字符類里面,它才會生效。如果它在字符類的外面,我們將會匹配字符^。一個很好的例子就是:[^a]。這個將會匹配除字符a之外的任意字符。
字符^作為錨,另外一個作用是用於匹配字符串的起始位置,相對應的字符串結尾的錨就是$。
我們已經花費了相當多的時間來介紹很多正則表達式的概念。下面幾節,我們將會深入幾個實際的代碼例子。
2.2 使用搜索進行模式匹配
讓我們先學習一下基礎的模式匹配。當你使用Python來查找一個字符串中的模式,你可以像上一節那樣,使用search函數,代碼如下:
import re
text = "Teh ants go marching one by one"
strings = ["the","one"]
for strig in strings:
match = re.search(string,text)
if match:
print('Found "{}" in "{}"'.format(string,text))
text_pos = match.span()
print(text[match.start(),match.end()])
else:
print('Did ot find "{}"'.format(string))
這個例子中,我們首先引入re模塊,並創建一個簡單的字符串。然后我們創建包含兩個要在主字符串進行查找的字符串。下一步,我們在要查找的字符串上進行遍歷,並進行搜索。如果匹配上,我們將其打印出來。否則,我們告訴用戶,字符串沒有找到。
還有其他的函數需要解釋一下。你或許注意到我們調用了span函數,這個會告訴我們待匹配的字符串的起始位置和結束位置。如果你將我們賦值的范圍給text_pos打印出來,你將會得到一個元組,類似於(21,24)。另外你還可以調用一些其它方法,就是我們接下來要做的。我們使用start和end來獲取匹配的起始位置和結束位置,它們就是我們所獲取范圍的兩個數字。
2.3 轉義字符
還有一些你在Python中搜索時需要的轉義字符,下面就是一個轉義字符列表以及相應的解釋。
d 匹配數字
D 匹配非數字
s 匹配空格
S 匹配非空格
w 匹配數字字母
W 匹配非數字字母
你可以在字符類中使用轉義字符,例如[\d],這就允許我們查找任何一個數字,等價於[0-9]。我非常推薦你使用它們。
2.4 編譯
re模塊允許你編譯頻繁搜索的表達式。這就允許你將表達式轉換為SRE_Pattern對象。你可以在搜索函數中使用這個對象。讓我們使用之前的代碼,並將其修改一下用於編譯,
import re
text = "The ants go marching one by one"
strings = ['the','one']
for string in strings:
regex = re.compile(string)
match = re.search(regex,text)
if match:
print('Found "{}" in "{}" '.format(string,text))
text_pos = match.span()
print(text[match.start():match.end()])
else:
print('Did not find "{}"'.format(string))
你將會注意到在這里,我們通過在列表中每個字符串調用編譯來創建我們的模式對象,並將結果賦值給變量regex。我們將regex傳遞給搜索函數。剩余的代碼都是相同的。主要的原因就是使用編譯用於保存以便在后續代碼中繼續使用。當然,編譯也可以使用標志位,這樣可以使能不同特殊的功能。
當你編譯模式時,他們將會自動獲得緩存,你就不需要在代碼中大量使用正則表達式,你不需要將已編譯的對象保存到變量中。
2.5 編譯標志位
Python 3中有7個編譯標志位,可以用於改變編譯模式行為。讓我們看看這些標志位,並觀察如何使用這些標志位。
2.5.1 re.A/re.ASCII
當遇到如下的編碼:w、W、b、B、d、D、s和S,ASCII標志位告訴Python只匹配ASCII,而不是全部的Unicode。re.U/re.UNICODE標志位主要是為了后向兼容性,現在這些標志位已經廢棄,因為Python 3默認匹配Unicode。
2.5.2 re.DEBUG
這個將會顯示關於編譯表達式的調試信息。
2.5.3 re.I/re.IGNORECASE
如果你想執行大小寫不敏感匹配,那么這個標志位就可以起到這個作用。如果你的表達式是[a-z],如果你用這個標志位進行編譯,你的模式也會匹配大寫字符。這個也會對Unicode編碼有效,並不會受到當前區域的影響。
2.5.4 re.L/re,LOCALE
w、W、b、B、d、D、s和S依賴當前區域。但是,Python官方文檔說明你不應該依賴這個標志位,因為區域機制本身不可靠。取而代之,僅僅使用Unicode匹配。官方文檔表示這個標志位僅僅對字節匹配有意義。
2.5.5 re.M/re.MULTILINE
當你使用這個標志位,你在告訴Python使得^模式字符匹配字符串的開始以及每行的開始。它也告訴Python使得$模式字符匹配字符串的末尾和每行的末尾,這個與默認的有些區別,具體可以查看官方文檔。
2.5.6 re.S/re.DOTALL
這個標志位使得.元字符匹配任何字符。沒有這個標志位,它將會匹配除了換行符之外的任何字符。
2.5.7 re.X/re.VERBOSE
如果你發現正則表達式很難閱讀,這個標志位就會滿足你的需要。它允許你將正則表達式中邏輯獨立部分可視化出來,甚至添加注釋。模式中的空格將會被忽略,除了在字符類中或者當空格在未綁定的反斜杠的前面。
2.5.8 使用編譯標志位
讓我們來看一個使用VERBOSE編譯標志位的簡單例子。一個例子就是郵箱地址查找正則表達式,例如,r'[w.-]+@[w.-]+',然后使用VERBOSE標志位來添加一些注視。代碼如下所示,
re.compile('''
[\w\.-]+ # the user name
@
[\w\.-]+ # the domain
''',
re.VERBOSE
)
讓我們來學習如何查找多個匹配。
2.6 查找多個實例
我們之前所看到是如何查找字符串中的第一個匹配。但是如果你有一個多個匹配的字符串,該如何處理?讓我們先回顧一下如何查找一個單獨的匹配:
import re
silly_string = "the cat in the hat"
pattern = "the"
match = re.search(pattern,silly_string)
print match.group()
現在,你能夠看到單詞“word”有兩個實例,但是我們僅僅找出一個。找出所有實例共有兩個方法。首先,我們看一下findall函數:
import re
silly_string = "the cat in the hat"
pattern = "the"
print re.findall(pattern,silly_string)
findall函數將會搜索整個字符串,並將每個匹配添加到列表中。一旦它結束在字符串中的搜索,它就會返回匹配列表。另一種查找多匹配的方法是使用finditer函數。
import re
silly_string = "the cat in the hat"
pattern = "the"
for match in re.finditer(pattern,silly_string):
s = "Found '{group}' at {begin}:{end}".format(
group = match.group(),begin=match.start(),
end = match.end())
print(s)
正如你所猜測,finditer方法返回匹配實例的迭代器而非findall函數得到的字符串。我們需要做的就是將結果格式化,然后再打印出來。
2.7 反斜杠
反斜杠在Python中正則表達式有些復雜。原因就是正則表達式使用反斜杠來表示特殊形式或者允許其成為可搜索的特殊字符而非直接調用,例如,我們想搜索美元符號:$。如果我們不使用反斜杠,我們僅僅是創建一個錨。原因就是Python將反斜杠字符作為字面字符使用。讓我們看看類似於“python”字符串的搜索結果。
為了在正則表達式中進行搜索,你需要去除反斜杠,但是由於Python也使用反斜杠,反斜杠也需要被去除,於是你的搜索模式就是“\python”。幸運的事,Python通過在字符串開始處添加字母‘r’就可以支持原始字符串。所以我們可以以r“\python”格式使得更加可讀。
如果你需要使用反斜杠進行搜索,請確認使用了原始字符串,否則將會遇到未知的結果。
2.8 總結
這篇博文大致總結了正則表達式可以做的任務。實際上,還有很多超出模塊本身的任務。有一本關於正則表達式的書,這里主要告訴你基本的知識。當你正在使用正則表達式時,可能需要你查找更多的例子並閱讀官方文檔。當你需要它時,它確實是一個好用的工具。