正則表達式一般用來在文本中查找和替換字符串,再簡單的文本中我們可以直接使用字符串查找,但是在大量數據和複雜結構中查找指定字符串不方便並且效率低,因此學習正則表達式顯得非常有必要。
文章目錄
正則表達式
元字符
正則表達式字符串是有普通字符和元字符組成。
· 普通字符,是指按照字面意義表示的字符,比如abcd。
· 元字符,是預先定義好的一些特定字符,比如下面正則表達式中的 \w 以及 . ,都屬於元字符。
rep = r'\w+@mail2\.sysu\.edu\.cn' # 匹配域名爲mail2.sysu.edu.cn的郵箱
元字符是用來描述其他字符的特殊字符,它是由基本元字符+普通元字符構成。
基本元字符:
字符 | 說明 |
---|---|
\ | 轉義字符 |
. | 表示任意一個字符 |
+ | 表示重複一次或多次 |
* | 表示出現零次或多次 |
| | 表示或關係,比如A |
{ } | 定義量詞 |
[ ] | 定義字符類 |
( ) | 定義分組 |
^ | 表示取反,或匹配一行的開始 |
$ | 匹配一行的結束 |
- | 表示區間 |
\ 轉義符: 比如上面的 \. ,實際上是爲了匹配單個 . 符號,但是 . 本身又作爲元字符表示任意一個字符,因此遇到這種情況需要轉義。
^ 和 $: 這個時候不僅要求匹配字符串,而且要求字符串出現在文本的開始或者結尾,例如:
text = 'My email address is [email protected]'
rep1 = r'\w+@mail2\.sysu\.edu\.cn' # r表示原始字符,避免在正則表達式中輸入轉義
rep2 = r'^\w+@mail2\.sysu\.edu\.cn$' # 添加了^和$,對開頭結尾有要求
# \w表示任意一個字母或數字 +表示出現一次或多次
像上面這段文本,需要將郵箱提取出來的話,只能使用rep1,因爲rep2要求匹配的字符串是一行的開頭和結尾,只有在郵箱是單獨一行才能提取出來。
字符類:字符類用中括號括起來,只要其中一個字符滿足條件即可,例如:
text1 = 'I like MATLAB and Python.'
text2 = 'I like MATLAB and python.'
rep = r'[Pp]ython' # 等效於 python1|Python
# 上述兩個文本中的python都可以用下面的正則表達式匹配出來。
^,取反符:有時候,就不需要某些字符出現,可以用取反符,例如我們需要找到非數字的字符:rep = r'^[0123456789]'
,這個正則表達式就可以匹配一個非數字的字符。
-,表示區間:比如上面的[0123456789],可以直接寫成[0-9],這個區間可以是分段的,比如[0-35-9],這個就表示除4以外的數字。
預定義字符
字符 | 說明 |
---|---|
\. | 匹配 . |
\ | 匹配\ |
\n | 匹配換行 |
\r | 匹配回車 |
\f | 匹配翻頁符 |
\t | 匹配水平製表符 |
\v | 匹配垂直製表符 |
\s | 匹配一個空格符,等價於[\t\n\r\f\v] |
\S | 匹配一個非空格符,等價於[^\s] |
\d | 匹配一個數字,等價於[0-9] |
\D | 匹配一個非數字,等價於[^\d] |
\w | 匹配任何語言單詞字符、數字、下劃線等,編碼爲ASCII的時候只能是英語 |
\W | 等價於[^\w] |
使用量詞
字符 | 說明 |
---|---|
? | 出現零次或一次 |
* | 出現零次或多次\ |
+ | 出現一次或多次 |
{n} | 出現n次 |
{n , } | 至少出現n次 |
{n , m} | 出現n次到m次之間 |
貪婪量詞
默認情況都是匹配的字符串越多越好,有時候要儘可能少的匹配,就需要在後面加?,表示懶惰匹配,我們具體寫一個例子:
import re
text = '123456789'
rep1 = r'\d{5,8}' # 匹配12345678
m = re.search(rep1,text)
print(m)
rep2 = r'\d{5,8}?' # 匹配12345
m = re.search(rep2,text)
print(m)
字符分組
字符分組就是將一個子字符串可看成一個整體,進行匹配,舉個例子:
import re
text = '12312311aa'
rep = r'(123)+' # 123出現一次或多次
rep1 = r'123+' # 3出現一次或多次
m = re.search(rep1,text)
print(m)
m = re.search(rep2,text)
print(m)
這樣有什麼好處呢?我們舉一個具體的例子,提取一個電話號碼的區號和號碼:
import re
tel = '0715-53566663'
rep = r'(\d{3,5})-(\d{6,9})'
m = re.search(rep,tel)
print(m)
print(m.group()) # 返回匹配的所有字符串
print(m.groups()) # 返回匹配所有字符串,用元組裝起來
這樣,可以將區號和電話號碼有效分開,除此之外還可以給分組命名:
import re
tel = '0715-53566663'
rep = r'(?P<area_code>\d{3,5})-(?P<Phone_Number>\d{6,9})'
m = re.search(rep,tel)
print(m)
print(m.group()) # 返回匹配的所有字符串
print(m.groups()) # 返回匹配所有字符串,用元組裝起來
print(m.group('area_code')) # 通過分組名稱來引用
print(m.group('Phone_Number'))
反向引用分組
我們在爬取網頁的時候,常常會出現標籤,標籤的開始和結束應該是一致的,下面舉例:
import re
text1 = '<title>Python Code</title>'
text2 = '<title>Python Code</text>' # 這種情況是我們不想要的
rep1 = r'<(\w+)>.*</(\w+)>'
m = re.search(rep1,text1)
print(m)
m = re.search(rep1,text2) # 同樣匹配了
print(m)
對上面的代碼進行改進:
import re
text1 = '<title>Python Code</title>'
text2 = '<title>Python Code</text>'
rep1 = r'<(\w+)>.*</\1>' # 反向引用在這裏,用 \1 代替了(\w+)這個分組,表示要和前面匹配的分組相同,1代表的是分組序號
m = re.search(rep1,text1)
print(m)
m = re.search(rep1,text2)
print(m)
非捕獲分組
前面講都是捕獲分組,這些匹配的結果都被暫時存放在內存中,但是有時候我們只需要一些分組進行輔助匹配,但是不想保存,就可以使用非捕獲分組,舉一個例子,匹配.jpg文件名:
import re
text = 'im1.jpg,im2.jpg,im3.png'
rep = r'\w+(.jpg)' (.jpg)爲捕獲分組
m = re.findall(rep,text) # 找出所有匹配的結果
print(m)
rep = r'\w+(?:.jpg)' (?:.jpg)爲非捕獲分組
m = re.findall(rep,text)
print(m)
使用捕獲分組的時候,只會講捕獲分組裏面的打印出來,這並不是我們所希望的。
re模塊介紹
re 模塊是python提供的關於正則表達式的模塊,可以直接使用,下面介紹裏面的主要函數。
search()和match()函數
search()和match()函數在使用上非常相似,區別如下:
- search從字符串中查找,返回第一個匹配對象
- match從字符串開頭開始匹配內容,返回匹配對象
import re
text = 'My email address is [email protected].'
rep = r'\w+@mail2\.sysu\.edu\.cn' # 匹配域名爲mail2.sysu.edu.cn的郵箱
m = re.search(rep,text)
print(m)
m = re.match(rep,text)
print(m)
match與我們之前提到的 ^ 和 $ 的作用相似,一般用於驗證結果。
match對象的方法
匹配結果返回的都是一個match對象,它常用的方法包括:group(),groups(),start(),end(),span().
import re
text = 'My email address is [email protected].'
rep = r'(\w+@mail2\.sysu\.edu\.cn)' # 匹配域名爲mail2.sysu.edu.cn的郵箱
m = re.search(rep,text)
print(m)
print(m.groups())
print(m.group())
print(m.start())
print(m.end())
print(m.span())
findall()和finditer()
字符串分割和替換
分割 split()
re.split(pattern, string, count(可選) , flags (可選))
pattern是正則表達式 , string是字符串, count是最大分割次數, flags是編譯標誌
import re
p = r'\d+' # 表示至少一個數字
text = 'AB12CD23EF'
m = re.split(p,text)
print(m)
替換 sub()
re.sub(pattern, sym, text, count, flags)
pattern是正則表達式, sym是替換的字符串, text是查找的文本, count替換的次數, flags編譯標誌
import re
p = r'\d+' # 表示至少一個數字
text = 'AB12CD23EF'
m = re.sub(p, '#', text)
print(m)
編譯正則表達式
爲了能夠重複使用正則表達式,並且提高程序運行速度,可以使用編譯後正則表達式,返回一個regex對象。
import re
p = r'\d+' # 表示至少一個數字
rep = re.compile(p) # 注意
text = 'AB12CD23EF'
m = rep.sub('#', text) # 注意,雖然函數名一樣,但是一個是rep對象的方法,一個是re模塊中的函數
# 輸入參數也變化了
print(m)
其使用方法都是一樣的,效果也相同。