一、正則表達式的概念
正則表達式 "一行勝千言"
regular expression regex RE
是用來簡潔表達一組字符串的表達式。
'PN'
'PYN'
'PYTN' <===========>正則表達式:P(Y|YT|YTH|YTHO)?N
'PYTHN'
'PYTHON'
通用的字符串表達框架
是用來簡潔表達一組字符串的表達式。
針對字符串表達“簡潔”和“特征”思想的工具
判斷某字符串的特征歸屬
正則表達式在文本處理中十分常用
表達文本類型的特征(病毒、入侵等)
同時查找或替換一組字符串
匹配字符串的全部或部分
主要應用在字符串匹配中
正則表達式的使用
編譯:將符合正則表達式語法的字符串轉換成正則表達式特征
我們可以說正則表達式是某一種語法格式,但是在程序中我們必須用字符串的形式來表達他,但是字符串就是字符串,他不是一組字符串,所以我們需要通過編譯的形式,將一個字符串變成一個特征,而這個特征可以表達一組字符串,這就是編譯的作用。我們也可以認為編譯后的特征與一組字符串是對應的,而編譯之前的正則表達式只是一個符合正則表達式語法的單一字符串,但他並不是真正意義上的正則表達式。
二、正則表達式的語法
正則表達式語法由字符和操作符構成
正則表達式的常用操作符
操作符 說明 實例
. 表示任何單個字符,它可以代表字符表上所有出現的一個字符
[ ] 字符集,對單個字符給出取值范圍 [abc]表示a、b、c,[a-z]表示a到z單個字符
[^ ] 非字符集,對單個字符給出排除范圍 [^abc]表示非a或b或c的單個字符,(出現一個字符,但這個字符不是a,不是b,也不是c)
* 前一個字符0次或無限次擴展 abc*表示ab、abc、abcc、abccc等
+ 前一個字符1次或無限次擴展 abc+表示abc、abcc、abccc等
? 前一個字符0次或1次擴展 abc?表示ab、abc
| 左右表達式任意一個 abc|def表示abc、def
{m} 擴展前一個字符m次 ab{2}c表示abbc
注意,大括號只對大括號前的一個字符進行擴展
{m,n} 擴展前一個字符m至n次(含n) ab{1,2}c表示abc,abbc
^ 匹配字符串開頭 ^abc表示abc且在一個字符串的開頭
$ 匹配字符串結尾 abc$表示abc且在一個字符串的結尾
() 分組標記,內部只能使用|操作符 (abc)表示abc,(abc|def)表示abc、def
\d 數字,等價於[0-9]
\w 單詞字符,等價於[A-Za-z0-9_]
經典正則表達式實例
^[A-Za-z]+$ 由26個字母組成的字符串
^[A-Za-z0-9]+$ 由26個字母和數字組成的字符串
^-?\d+$ 整數形式的字符串
^[0-9]*[1-9][0-9]*$ 正整數形式的字符串
[1-9]\d{5} 中國境內郵政編碼,6位
[\u4e00-\u9fa5] 匹配中文字符 采用utf-8編碼來約定了中文字符的取值范圍
\d{3}-\d{8}|\d{4}-\d{7} 國內電話號碼,010-68913536
匹配IP地址的正則表達式
IP地址字符串形式的正則表達式
(IP地址分4段,每段0-255)由4段由.分隔的數字構成,每段取值0-255
\d+.\d+.\d+.\d+ 不考慮每一段的取值范圍和空間,只考慮他們之前出現的.來分隔
\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}中間的每一串數字出現的范圍都是1個,2個或到3個的字符串的長度
不精確300.300.300.300 ✘
精確寫法
0-99:[1-9]?\d 100-199:1\d{2} 200-249:2[0-4]\d 250-255:25[0-5]
0-255這樣一個空間事實上是由0-99,100-199,200-249,250-255這四段正則表達式組合而成,我們可以采用|操作符以及()形式將他們組織起來
([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])
再表達了0-255之后,我們可以使用{}和.的形式來構成IP地址的正則表達式
(([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5]).){3}([1-9]?\d|1\d{2}|2[0-4]\d|25[0-5])
三、Re庫的基本使用
RE庫是Python的標准庫,主要用於字符串匹配。
調用方式:import re
正則表達式的表示類型
raw string類型(原生字符串類型):不包含轉義符的字符串,原生字符串中的\不會被解釋成轉義符
re庫采用raw string類型表示正則表達式,表示為:r'text'
例如: r'[1-9]\d{5}'
r'\d{3}-\d{8}|\d{4}-\d{7}'
原生字符串類型和字符串類型所不同 的是,只需要在字符串的表示前加一個小寫的字符r
string類型,更繁瑣。:string類型中將\理解為轉義符
例如: '[1-9]\\d{5}'
'\\d{3}-\\d{8}|\\d{4}-\\d{7}'
當【正則表達式】包含《轉義符》時,使用raw string
RE庫主要功能函數
函數 說明
re.search() 在一個字符串中搜索匹配正則表達式的第一個位置,返回match對象(在一個字符串中完整的搜索可能匹配正則表達式的位置)
re.match() 從一個字符串的開始位置起匹配正則表達式,返回match對象(從頭開始匹配)
re.findall() 搜索字符串,以列表類型返回全部能匹配的子串(發現里面所有子串,並以列表形式返回)
re.split() 將一個字符串按照正則表達式匹配結果進行分割,返回列表類型(根據正則表達式進行分割,返回相關列表)
re.finditer() 搜索字符串,返回一個匹配結果的迭代類型,每個迭代元素是match對象(返回一個迭代類型,每個類型都是每次匹配的一個match對象)
re.sub() 在一個字符串中替換所有匹配正則表達式的子串,返回替換后的字符串(用正則表達式匹配的地方替換為另外一個字符串,並返回替換后的字符串)
re.search(pattern,string,flags=0)
pattern:正則表達式的字符串或原生字符串表示
string:待匹配字符串
flags:正則表達式使用時的控制標記
flags:正則表達式使用時的控制標記
常用標記 說明
re.I re.IGNORECASE 忽略正則表達式的大小寫,[A-Z]能夠匹配小寫字符
re.M re.MULTILINE 正則表達式中的^操作符 能夠將給定字符串的每行當做匹配開始
re.S re.DOTALL 正則表達式中的.操作符 能夠匹配所有字符,默認匹配除換行外的所有字符
>>> import re
>>> match = re.search(r'[1-9]\d{5}','BIT 100081')
>>> if match:
... print(match.group(0))
...
100081
re.match(pattern,string,flags=0)
>>> match = re.match(r'[1-9]\d{5}','BIT 100081')
>>> if match:
... match.group(0)
...
>>> match.group(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
>>> match = re.match(r'[1-9]\d{5}','100081 BIT')
>>> if match:
... match.group(0)
...
'100081'
re.findall(pattern,string,flags=0)
>>> ls = re.findall(r'[1-9]\d{5}','BIT100081 TSU100084')
>>> ls
['100081', '100084']
re.split(pattern,string,maxsplit=0,flags=0)
maxsplit:最大分割數,剩余部分作為最后一個元素輸出
>>> re.split(r'[1-9]\d{5}','BIT100081 TSU100084')
['BIT', ' TSU', '']
split函數是將一個正則表達式去匹配字符串,匹配的部分去掉,去掉之后的部分分別做為分割的字符串元素放到一個列表里
>>> re.split(r'[1-9]\d{5}','BIT100081 TSU100084',maxsplit=1)
['BIT', ' TSU100084']
maxsplit=1在整個字符串中只匹配第一個位置,匹配之后,將BIT分割出來一個部分,剩下的所有部分作為一個完整的字符串,不再進行正則表達式的匹配,作為列表的最后一個變量輸出出來
re.finditer(pattern,string,flags=0)
>>> for m in re.finditer(r'[1-9]\d{5}','BIT100081 TSU100084'):
... if m:
... print(m.group(0))
...
100081
100084
通過finditer這個函數,能夠迭代的獲得每一次正則表達式匹配的結果,並對這個結果單獨進行處理
re.sub(pattern,repl,string,count=0,flags=0)
用一個新的字符串替換正則表達式匹配上的那些字符串,並與原來的字符串進行組合,返回一個新的字符串
repl:替換匹配字符串的字符串(當pattern匹配上某一個字符串后,替換的那個字符串)
count:匹配的最大替換次數
>>> re.sub(r'[1-9]\d{5}',':zipcode','BIT100081 TSU100084')
'BIT:zipcode TSU:zipcode'
Re庫的另一種等價用法
>>> rst = re.search(r'[1-9]\d{5}','BIT 100081')
函數式用法:一次性操作
||
||
面向對象用法:編譯后的多次操作
另外一種用法:面向對象的方式來使用正則表達式,它包含兩個部分,第一個部分首先使用re.compile()將一個正則表達式的字符串編譯成為一個正則表達式類型,叫pat也叫pattern類型
然后就可以用這個pattern對象直接調用match,search等6個方法來獲得相關結果
>>> pat = re.compile(r'[1-9]\d{5}')
>>> rst = pat.search('BIT 100081')
這種方法的好處是,經過一次編譯,當我們需要多次對正則表達式進行匹配和使用時,可以使用這種方式,加快整個程序的運行
regex = re.compile(pattern,flags=0)
將正則表達式的字符串形式編譯成正則表達式對象
pattern:正則表達式的字符串或原生字符串表示
flags:正則表達式使用時的控制標記
字符串或原生字符串表示並不是正則表達式,他只是一種表示,如果通過compile編譯生成了一個對象regex,這個regex才是正則表達式,它代表了一組字符串,所以我們可以通過這樣的函數來實現正則表達式、表示之間的對應,而這種對應,使得我們能夠更好的理解正則表達式對象的這種使用方式,經過了compile之后的正則表達式,就可以使用它的對象的方法,而這個對象的方法與RE庫提供的6個操作方法是一致的
regex.search()
regex.match()
regex.findall()
regex.split()
regex.finditer()
regex.sub()
在這6個函數使用的過程中,需要注意,正是由於前面已經給了regex正則表達式對象,所以在調用這些函數的時候,需要將其中的正則表達式那個參數去掉,因為我們已經不再需要正則表達式的參數,只需要直接給出相關的需要匹配的字符串就可以了
四、Re庫的match對象
match對象就是一次匹配的結果,它包含了很多匹配的相關信息
>>> match = re.search(r'[1-9]\d{5}','BIT 100081')
>>> if match:
... print(match.group(0))
...
100081
>>> type(match)
<class '_sre.SRE_Match'>
Match對象的屬性
屬性 說明
.string 待匹配的文本
.re 匹配時使用的pattern對象(正則表達式)
.pos 正則表達式搜索文本的開始位置
.endpos 正則表達式搜索文本的結束位置
Match對象的方法
方法 說明
.group(0) 獲得匹配后的字符串
.start() 匹配字符串在原始字符串的開始位置
.end() 匹配字符串在原始字符串的結束位置
.span() 返回(.start(),.end())
>>> m = re.search(r'[1-9]\d{5}','BIT100081 TSU100084')
>>> m.string
'BIT100081 TSU100084'
>>> m.re
re.compile('[1-9]\\d{5}')
>>> m.pos
0
>>> m.endpos
19
>>> m.group(0)
'100081'
>>> m.start()
3
>>> m.end()
9
>>> m.span()
(3, 9)
五、Re庫的貪婪匹配和最小匹配
>>> match = re.search(r'PY.*N','PYANBNCNDN') #以PY字母開頭,以N結尾,中間可以有若干個字符串的這樣的字符串
>>> match.group()
'PYANBNCNDN'
同時匹配長短不同的多項,返回哪一個呢?
貪婪匹配
Re庫默認采用貪婪匹配,即輸出匹配最長的子串。
最小匹配
如何輸出最短的子串呢?
>>> match = re.search(r'PY.*?N','PYANBNCNDN')
>>> match.group(0)
'PYAN'
最小匹配操作符
操作符 說明
*? 前一個字符0次或無限次擴展,最小匹配
+? 前一個字符1次或無限次擴展,最小匹配
?? 前一個字符0次或1次擴展,最小匹配
{m,n}? 擴展前一個字符m至n次(含n),最小匹配
總結: