目錄 一. 正則表達式 二. 特殊的元字元 三. python3的re模塊方法 四. python3的re模塊練習 五. 第一章課後練習題 六. re模塊綜合應用之計算器 一. 正則表達式 正則表達式是由一堆字元和特殊符號組成的字元串。它可以為我們提供高級的文本搜索,匹配,替換功能。當然,正則表達式 ...
目錄
一. 正則表達式
二. 特殊的元字元
三. python3的re模塊方法
四. python3的re模塊練習
五. 第一章課後練習題
六. re模塊綜合應用之計算器
一. 正則表達式
正則表達式是由一堆字元和特殊符號組成的字元串。它可以為我們提供高級的文本搜索,匹配,替換功能。當然,正則表達式也不是python獨有的一種模式,而是凌駕於語言之上的一種跨平臺的通用標準。當我們學會了正則表達式之後,將會能夠更加容易的處理我們的文本和數據。讓我們開始正則之旅吧。
二. 特殊的元字元
正則表達式本質上就是一堆字元串,只不過構成這個字元串的每一個字元都是有特別意義的,我們想要去真正的去瞭解正則表達式,就必須要清楚的記得特殊字元的含義。下圖是python核心編程中關於元字元的描述。雖然有很多,但是你一定要背會(當然在學的過程中你會發現其實也沒有那麼難背,用著用著就記住了,但是還是提醒,不管是什麼方法,一定要記住)。
在我們真正開始正則表達式之前,我們首先要瞭解一個工具,那就是python的re模塊,快速的瞭解,只需要知道通過這個模塊我們可以查看寫出來的正則表達式是否準確就可以了。之後我們會再去詳細的查看,使用方法如下:
>>> re.search('^s.*$', 'sllsljegleg')# 第一個參數:就是我們寫出來的正則表達式(從錶面上看就是字元串)第二個參數:就是我們匹配的字元串,如果匹配到了就返回值
<_sre.SRE_Match object; span=(0, 11), match='sllsljegleg'> # 最後match後面的就是我們匹配到的字元串
>>> re.search('^s.*$', 'llsljegleg') # 如果沒有匹配到就沒有顯示
>>>
第一類: 位置匹配
位置匹配就是專門用來描述位置的元字元,有四個: 【^,$, \A,\Z】(註意是有大小寫之分的),^ 和\A都表示字元串的開頭,$ 和\Z都表示字元串的結尾,為什麼會有兩個元字元去表示同一個事物呢?這是因為在一些國際鍵盤上是沒有脫字元的,所以設計的時候又設計了\A和\Z來表示。
>>> re.search('^From','From to China') # 以From開頭的字元串
<_sre.SRE_Match object; span=(0, 4), match='From'>
>>> re.search('/bin/tcsh$', 'python /bin/tcsh') # 以/bin/tcsh結尾的字元串
<_sre.SRE_Match object; span=(7, 16), match='/bin/tcsh'>
>>> re.search('^Subject:hi$', 'Subject:hi') # 如果前面有脫字元,後面美元符,就代表只匹配裡面的值,此例中就是只匹配Subject:hi
<_sre.SRE_Match object; span=(0, 10), match='Subject:hi'>
>>>
需要匹配【$】和脫字元【^】的時候怎麼做呢,通過【\】斜杠進行轉義
>>> re.search('\$', '$') 通過\轉義就可以匹配到$ 符了
<_sre.SRE_Match object; span=(0, 1), match='$'>
>>> re.search('\^\$', 'hello ^$')
<_sre.SRE_Match object; span=(6, 8), match='^$'>
>>>
【\A, \Z】的用法和^$的用法是一樣的
>>> re.search('\AFrom','From to China') # 以From開頭的字元串
<_sre.SRE_Match object; span=(0, 4), match='From'>
>>> re.search('/bin/tcsh\Z', 'python /bin/tcsh') # 以/bin/tcsh結尾的字元串
<_sre.SRE_Match object; span=(7, 16), match='/bin/tcsh'>
>>> re.search('\ASubject:hi\Z', 'Subject:hi') # 如果前面有脫字元,後面美元符,就代表只匹配裡面的值,此例中就是只匹配Subject:hi
<_sre.SRE_Match object; span=(0, 10), match='Subject:hi'>
>>>
\A和\Z的使用方法
【\b】匹配任何單詞邊界
>>> s = 'this island is beautiful'
>>> import re
>>> re.search(r'\bis', s) # 此時我們會發現匹配到了索引為5,7的字元,也就是說island這個前面的is匹配到了
<_sre.SRE_Match object; span=(5, 7), match='is'>
>>> re.search(r'\bis\b', s) # 如果加上\b界定上單詞邊界之後就會之匹匹厄後面的is了,因為前面的is並不是一個單詞
<_sre.SRE_Match object; span=(12, 14), match='is'>
>>>
第二類: 重覆匹配
重覆匹配就是將之前匹配的正則表達式重新匹配多少次。重覆匹配是正則表達式中基本上最常用的模式。
【*】匹配前面正則表達式0次或者多次
>>> re.search('\d\d*', '1') # 第一個\d匹配一個數字,第二個\d本來也是匹配一個數字,但是後面加上了一個*,代表前面可以不匹配,也可以匹配一次或者多次
<_sre.SRE_Match object; span=(0, 1), match='1'>
>>> re.search('\d\d*', '12') # 這個就是第二個\d匹配了1次
<_sre.SRE_Match object; span=(0, 2), match='12'>
>>> re.search('\d\d*', '123') # 這個就是第二個\d匹配了2次
<_sre.SRE_Match object; span=(0, 3), match='123'>
>>>
【+】匹配前面正則表達式1次或者多次
>>> re.search('\d\d+', '1') # 第一個\d匹配到了1,但是第二個\d後面有+代表最少匹配一次,但是字元串沒有數字了,所以就沒有匹配到值
>>> re.search('\d\d+', '12') # 這個代表第二個\d匹配了一次
<_sre.SRE_Match object; span=(0, 2), match='12'>
>>> re.search('\d\d+', '123') # 這個代表第二個\d匹配了兩次
<_sre.SRE_Match object; span=(0, 3), match='123'>
>>>
【?】匹配前面正則表達式0次或者1次
>>> re.search('\d\d?', '1') # 第一個\d匹配到了1,但是第二個\d後面有?說明可以匹配0次,也就是不匹配 <_sre.SRE_Match object; span=(0, 1), match='1'> >>> re.search('\d\d?', '12') # 第二個\d匹配了1次 <_sre.SRE_Match object; span=(0, 2), match='12'> >>> re.search('\d\d?', '123') # 雖然匹配到了,但是發現匹配到的值還是12,最多只能匹配一次 <_sre.SRE_Match object; span=(0, 2), match='12'>
>>>
【{n}】匹配前面正則表達式n次
>>> re.search('\d\d{2}', '12')
>>> re.search('\d\d{2}', '1')
>>> re.search('\d\d{2}', '123') # {2}代表前面的\d必須要匹配兩次,所以只能匹配到123
<_sre.SRE_Match object; span=(0, 3), match='123'>
>>> re.search('\d\d{2}', '1234')
<_sre.SRE_Match object; span=(0, 3), match='123'>
>>>
【{n, m}】匹配前面正則表達式n到m次
>>> re.search('\d\d{2,4}', '12')
>>> re.search('\d\d{2,4}', '123') # {2,4}代表匹配前面\d2次到4次,因此我們可以發現最少要匹配兩次,最多要匹配4次
<_sre.SRE_Match object; span=(0, 3), match='123'>
>>> re.search('\d\d{2,4}', '1234')
<_sre.SRE_Match object; span=(0, 4), match='1234'>
>>> re.search('\d\d{2,4}', '12345')
<_sre.SRE_Match object; span=(0, 5), match='12345'>
>>> re.search('\d\d{2,4}', '123456')
<_sre.SRE_Match object; span=(0, 5), match='12345'>
>>>
重覆匹配的例子
# 1. 匹配字元d或b之後跟著一個o最後可以跟著一個t也可以不跟著一個t的例子
>>> re.search('[db]ot?', 'do')
<_sre.SRE_Match object; span=(0, 2), match='do'>
>>> re.search('[db]ot?', 'dot')
<_sre.SRE_Match object; span=(0, 3), match='dot'>
>>> re.search('[db]ot?', 'bo')
<_sre.SRE_Match object; span=(0, 2), match='bo'>
>>> re.search('[db]ot?', 'bot')
<_sre.SRE_Match object; span=(0, 3), match='bot'>
>>>
# 2. 匹配9-16位的信用卡號
>>> re.search('[0-9]{9, 16}', '1234567890') # 註意不能加空格
>>> re.search('[0-9]{9,16}', '1234567890')
<_sre.SRE_Match object; span=(0, 10), match='1234567890'>
>>>
# 3. 匹配全部有效的html標簽
>>> re.search('</?[^>]+>', '</>') #/?代表/可以不出現,代表的是開頭的標簽,也可以出現1次,代表的是結尾的標簽,[^>]代表的是除了>的任何字元,後面跟上+,也就是說除了>的任何字元出現一次或者多次
<_sre.SRE_Match object; span=(0, 3), match='</>'>
>>> re.search('</?[^>]+>', '<hello>')
<_sre.SRE_Match object; span=(0, 7), match='<hello>'>
>>>
第三類: 範圍匹配
範圍匹配是通過一個或者一組特殊的字元代表一個範圍的數據。例如:【\d】代表的是0-9的數字數字。【大寫字母的都是與之相反的】
【.】點代表的是除了換行符之外的任意的字元的匹配
>>> re.search('f.o', 'fao') # 匹配字母f和o之間的任何一個字元
<_sre.SRE_Match object; span=(0, 3), match='fao'>
>>> re.search('f.o', 'f#o')
<_sre.SRE_Match object; span=(0, 3), match='f#o'>
>>> re.search('f.o', 'f9o')
<_sre.SRE_Match object; span=(0, 3), match='f9o'>
>>> re.search('..', 'js') # 匹配任意的兩個字元
<_sre.SRE_Match object; span=(0, 2), match='js'>
>>> re.search('..', 'ss')
<_sre.SRE_Match object; span=(0, 2), match='ss'>
>>>
【\d】表示0-9的任何十進位數字
>>> re.search('\d', '1') # 匹配0-9的任何一個字元
<_sre.SRE_Match object; span=(0, 1), match='1'>
>>> re.search('\d', '2')
<_sre.SRE_Match object; span=(0, 1), match='2'>
>>> re.search('\d', 'z') # z不是0-9所以匹配不到
>>>
【\w】字母數字下劃線 相當於[a-zA-0-9_]的縮寫
>>> re.search('\w', '1') # 字母數字下滑先都可以匹配到,而且也只是匹配一個字元
<_sre.SRE_Match object; span=(0, 1), match='1'>
>>> re.search('\w', 'a')
<_sre.SRE_Match object; span=(0, 1), match='a'>
>>> re.search('\w', '_')
<_sre.SRE_Match object; span=(0, 1), match='_'>
>>> re.search('\w', '#') # 因為#不在範圍之內,所以沒有匹配到
>>>
\d和\w的一些例子
#1. 一個數字字母組成的字元串和一串由數字組成的字元串
>>> re.search('\w+-\d+', 'ab123sejg-123456') # \w代表數字字母,+代表前面的數字字母可以出現一次或者多次,也就是把字母數字組成的字元串表示好了,\d+也是一樣的效果,代表的是一串由數字組成的字元串
<_sre.SRE_Match object; span=(0, 16), match='ab123sejg-123456'>
>>>
#2. 以字母開頭,其餘字元是字母或者數字
>>> re.search('^[A-Za-z]\w*', 'a123lsege')
<_sre.SRE_Match object; span=(0, 9), match='a123lsege'>
>>>
#3. 美國電話號碼的格式 800-555-1212
>>>
>>> re.search('\d{3}-\d{3}-\d{4}', '800-555-1212') # \d代表的是一個數字{3}代表的是前面的數字出現三次,也就是把800給匹配出來了,然後就是單純的一個-,後面的也是一個效果,組合起來就把電話號碼給匹配出來了。
<_sre.SRE_Match object; span=(0, 11), match='800-555-1212'>
>>>
# 4. 電子郵件地址[email protected]
>>> re.search('\w+@\w+\.com', '[email protected]') # 首先\w+匹配一個數字字母下劃線的字元串,然後一個單純的@,然後又是一個字元串, 之後\.因為做了轉義,代表的就是一個單純的.
<_sre.SRE_Match object; span=(0, 10), match='[email protected]'>
>>>
【\s】匹配空格字元
>>> re.search('of\sthe', 'of the') # 就是匹配一個空格字元, 無論是換行符,\t\v\f都是可以匹配到的
<_sre.SRE_Match object; span=(0, 6), match='of the'>
>>>
【[a-b]】中括弧代表a-b的一個範圍,代表的是從a-b的字元之間匹配一個字元
應用一:創建字元串
>>> re.search('[A-Z]', 'D') # 匹配A-Z之間的一個字元,所以D就可以匹配了,之間不能有空格
<_sre.SRE_Match object; span=(0, 1), match='D'>
>>>
>>> re.search('[ab][cd]', 'ac') # 從第一個方框中取出一個值與第二個方框中取出一個值進行組合,註意不能匹配到ab和cd,如果想匹配ab和cd需要通過擇一匹配符號也就是[|]
<_sre.SRE_Match object; span=(0, 2), match='ac'>
>>> re.search('[ab][cd]', 'ad')
<_sre.SRE_Match object; span=(0, 2), match='ad'>
>>> re.search('[ab][cd]', 'ac')
<_sre.SRE_Match object; span=(0, 2), match='ac'>
>>> re.search('[ab][cd]', 'ad')
<_sre.SRE_Match object; span=(0, 2), match='ad'>
>>>
【[^ a -b]】匹配除了a-b之外的任何一個字元
# 1. z後面跟著任何一個字元然後再跟著一個0-9之間的數字
>>> re.search('z.[0-9]', 'z#0')
<_sre.SRE_Match object; span=(0, 3), match='z#0'>
>>> re.search('z.[0-9]', 'z#1')
<_sre.SRE_Match object; span=(0, 3), match='z#1'>
>>>
# 2. 除了aeiou之外的任何字元
>>> re.search('[^aeiou]', 's')
<_sre.SRE_Match object; span=(0, 1), match='s'>
>>>
# 3. 除了製表符或者換行符之外的任何一個字元
>>> re.search('[^\n\t]', 'aljg')
<_sre.SRE_Match object; span=(0, 1), match='a'>
>>>
第四類: 分組匹配
分組匹配嚴格意義上來講並不是一個匹配的元字元,因為它本身並不會影響匹配的結果,只是會把匹配的結果按照括弧分開,然後存儲到一定的位置,便於我們之後使用的。那為什麼要有分組呢?因為在很多的時候我們並不是對於匹配出來的字元感興趣的,有時候我們只是對於匹配字元的某一個塊感興趣,可能還會對這一塊進行一系列的操作。這就需要分組來幫我們做這件事了。
分組相對來說較為簡單,但是卻相當重要,簡單在於它並不會影響我們的匹配結果,重要在數據匹配之後大部分都要用到這個分組。
【 () 】,在之後介紹group和groups的時候會提到
>>> import re
>>> re.search('(\d+)([a-z])(\.)(\w+)', '123c.sleg234') # 這個和下麵匹配的結果是一樣的,意思是加上括弧分組的時候並不會對匹配的結果產生影響
<_sre.SRE_Match object; span=(0, 12), match='123c.sleg234'> # 它只是通過分組給我們返回了一系列的數據,我們可以通過group(s)方法獲得而已
>>> re.search('\d+[a-z]\.\w+', '123c.sleg234')
<_sre.SRE_Match object; span=(0, 12), match='123c.sleg234'>
>>>
第五類:擇一匹配符
【|】豎杠代表的是從幾個正則表達式中得到一個
>>> re.search('ab|cd', 'ab') # 從左邊的ab和cd中匹配相應的數據,但是不會匹配ac,這也是和[]的區別
<_sre.SRE_Match object; span=(0, 2), match='ab'>
>>> re.search('ab|cd', 'cd')
<_sre.SRE_Match object; span=(0, 2), match='cd'>
>>> re.search('ab|cd', 'ac')
>>>
第六類:擴展表示法
擴展表示法等之後真正用到了再回來看吧
三. python3re模塊方法
方法一: compile編譯
程式是我們寫出來的一堆字元串,電腦是看不懂的,因此想要讓我們寫出來的代碼可以正常的電腦中執行,就必須將代碼塊編譯成位元組碼(也就是程式能夠理解的語言)。而complie編譯就是這個原理,也就是我提前將字元串編譯成一個對象,之後你要進行使用的時候不必再進行編譯了,直接調用此對象就可以了。對於只調用一次的正則表達式用不用此方法都是可以的,但是對於那些需要調用多次的正則表達式而言,提前編譯就能夠大大的提升執行性能。complie方法就是為了做這樣的一件事的。
>>> import re
>>> s = '\d+' # 這個是自己寫的正則表達式
>>> int_obj = re.compile(s) # 通過complie方法將s編譯成對象,之後就直接可以通過這個對象直接去調用方法,可以節省性能
>>> print(int_obj.search('123'))
<_sre.SRE_Match object; span=(0, 3), match='123'>
>>> print(int_obj.search('123'))
<_sre.SRE_Match object; span=(0, 3), match='123'>
>>> print(int_obj.search('123'))
<_sre.SRE_Match object; span=(0, 3), match='123'>
>>> print(int_obj.search('123'))
<_sre.SRE_Match object; span=(0, 3), match='123'>
>>>
方法二:group 和 groups方法
(1).group和groups方法都是在正則表達式匹配成功之後調用的方法,如果沒有匹配成功調用此方法會報錯
(2).group方法會得到一個如下的列表【當前匹配的字元串, 分組一,分組二........】沒有分組就只有一個元素
(3).groups方法會得到一個如下的元組 ( 分組一,分組二......), 沒有分組就是空空列表
(4).這兩個方法只能是search和match成功匹配到的對象才可以使用。
以美國電話號碼匹配為例s1 = '\d{3}-\d{3}-\d{4}'
(1).沒有分組的情況下
>>> s1 = '\d{3}-\d{3}-\d{4}' # 美國電話號碼的格式匹配正則表達式
>>> s2 = '(\d{3})-(\d{3})-(\d{4})' # 將美國的電話號碼進行分組
>>> tel = '099-898-2392' # 這是一個電話格式
>>> re.search(s1, tel) # 之前沒有學group的時候我們一直得到的都是下麵的這種對象
<_sre.SRE_Match object; span=(0, 12), match='099-898-2392'>
>>> re.search(s1, tel).group() # 得到匹配的值
'099-898-2392'
>>> re.search(s1, tel).group(0) # 和上面的是一樣的,因為沒有分組得到的列表中就只有一個元素
'099-898-2392'
>>> re.search(s1, tel).group(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: no such group
>>> re.search(s1, tel).groups() # 沒有分組的情況下groups就是空的元組
()
>>>
(2)有分組的情況下
>>> s1 = '\d{3}-\d{3}-\d{4}' # 美國電話號碼的格式匹配正則表達式
>>> s2 = '(\d{3})-(\d{3})-(\d{4})' # 將美國的電話號碼進行分組
>>> tel = '099-898-2392'
>>>
>>> print(re.search(s2, tel).group())
099-898-2392
>>> print(re.search(s2, tel).group(0)) # 獲得的是整個匹配的對象
099-898-2392
>>> print(re.search(s2, tel).group(1)) # 第一個子組099
099
>>> print(re.search(s2, tel).group(2)) # 獲得的第二個子組898
898
>>> print(re.search(s2, tel).group(3)) # 獲得的第三個子組2392
2392
>>> print(re.search(s2, tel).groups()) # 獲得子組的一個列表
('099', '898', '2392')
>>>
方法三: match,search, findall
match: 在字元串的開頭對寫的正則表達式進行匹配。 匹配成功,返回匹配對象,匹配失敗,返回None
search:在整個字元串中對寫的正則表達式進行匹配。 匹配成功,返回第一個被匹配的對象,匹配失敗,返回None
findall: 在整個字元串中對寫的正則表達式進行匹配。只要是匹配成功的就添加到列表中,最後返回一個列表
match:
>>> re.match('foo', 'food on the table').group() # 從字元串開頭開始匹配,匹配到了foo,所以group得到的是匹配到的foo 'foo' >>> re.match('ood', 'food on the table') # 從字元串開頭開始匹配,發現並不是00d,所以沒有匹配到結果,返回一個空 >>>
search:
>>> re.match('foo', 'seafood') # 字元串開頭並不是foo,所以match沒有匹配到 >>> re.search('foo', 'seafood').group() # search是在整個字元串中匹配的,所以 'foo' >>>
findall:
>>> re.match('foo', 'seafood, seafood') # 字元串開頭並不是foo,所以沒有匹配 >>> re.search('foo', 'seafood, seafood').group() # 在字元串匹配到第一個foo的時候就不再進行匹配了 'foo' >>> re.findall('foo', 'seafood, seafood') # 在字元串給中查找到所有匹配到的字元串放在列表中 ['foo', 'foo'] >>>
方法四: finditer, findall
finditer和findall作用其實是一樣的,不同之處在於finditer返回的是一個迭代器(更加節省記憶體),而findall返回的是一個列表。
(1).在沒有分組的情況下,每一個被匹配的元素都會作為列表的元素
(2).在分組的情況下,被匹配的元素會把子組放在一個元組中放在列表中(比較繞,直接上例子)
(1)在沒有分組的情況下
s = 'This and that.' print(re.findall(r'Th\w+ and th\w+', s)) # 會把匹配到的信息一個一個的放在列表中,此處只是匹配了一個 print(re.finditer(r'Th\w+ and th\w+', s).__next__()) # iter返回一個迭代器,可以通過__next__去獲得第一個對象,註意此處是類似於match獲得的對象 print(re.match('s', 's')) # 為了和上面進行對比的 # 結果: # ['This and that'] # <_sre.SRE_Match object; span=(0, 13), match='This and that'> # <_sre.SRE_Match object; span=(0, 1), match='s'>
(2)有分組的情況
s = 'This and that.' print(re.findall(r'(Th\w+) and (th\w+)', s)) # 有分組就會發現列表中其實放的並不是匹配到的值了,而是子組元組 print(re.finditer(r'(Th\w+) and (th\w+)', s).__next__()) #但是iter得到的還是匹配的對象,如果想得到子組可以通過group去獲得 print(re.match('s', 's')) # 為了和上面進行對比的 # 結果: # [('This', 'that')] # <_sre.SRE_Match object; span=(0, 13), match='This and that'> # <_sre.SRE_Match object; span=(0, 1), match='s'>
方法五: sub
sub將搜索匹配到的字元串替換成另外一種字元串。
# 參數一: 正則表達式 # 參數二: 要替換的字元串 # 參數三: 源字元串 # 將替換好的字元串進行返回 s = re.sub('X', 'Mr. Smith', 'attn: X\n\nDear X, \n') print(s) #結果: # attn: Mr. Smith # # Dear Mr. Smith,
# 參數一: 正則表達式 # 參數二: 要替換的字元串 # 參數三: 源字元串 # 將替換好的字元串進行返回 s = re.sub('[ae]', 'X', 'abcdef'