文章目錄
Python正則表達式
- 正則表達式是一個特殊的字符序列,它能幫助你方便的檢查一個字符串是否與某種模式匹配。
- re 模塊使 Python 語言擁有全部的正則表達式功能。
- compile 函數根據一個模式字符串和可選的標誌參數生成一個正則表達式對象。該對象擁有一系列方法用於正則表達式匹配和替換。
- re 模塊也提供了與這些方法功能完全一致的函數,這些函數使用一個模式字符串做爲它們的第一個參數。
正則表達式元字符
.(點)
- 點是匹配除換行符以外的任何單個字符
例子:
content = '''
蘋果,是綠色的
橙子,是橙色的
草莓,是紅色的
烏鴉,是黑色的
'''
import re
p = re.compile(r'.色')
for one in p.findall(content):
print(one)
圖形化正則:
運行結果(我這裏字符編碼出了些問題…):
[](方括號)
- 常用來指定一個字符集,例如:[abc]或[a-z]
- 元字符在字符集中不起作用,例如:[asd$]
- 補集匹配不在區間範圍內的字符,例如:[^5]
寫了這些理論,不如來一個例子體會一下這個[]元字符是什麼效果:
content = '''
蘋果,是綠色的
橙子,是橙色的
草莓,是紅色的
烏鴉,是黑色的
'''
import re
p = re.compile(r'[綠橙紅黑]色')
for one in p.findall(content):
print(one)
圖形化正則:
運行結果:
它還可以這樣玩:
content = '''
小李,156888z7894,21
小張,18866716789,23
小王,13512342233,26
小崔,y3552342133,18
小劉,g3510348233,35
小唐,a3211232243,42
'''
import re
p = re.compile(r'[a-z]\d{10}')
for one in p.findall(content):
print(one)
圖形化正則:
運行結果:
如果在方括號中使用 ^ , 表示 非 方括號裏面的字符集合,例如:
content = 'a1b2c3d4e5'
import re
p = re.compile(r'[^\d]' )
for one in p.findall(content):
print(one)
圖形化正則:
運行結果:
^(插入符)
- 匹配行首,除非設置MULTILINE標誌,他只是匹配字符串的開始。
- 在MULTILINE模式裏,它也可以匹配每個字符串中的換行
正則表達式可以設定 單行模式 和 多行模式
如果是 多行模式 ,表示匹配 文本每行 的開頭位置。
如果是 單行模式 ,表示匹配 整個文本 的開頭位置。
比如,下面的文本中,每行最前面的數字表示水果的編號,最後的數字表示價格
001-蘋果價格-60,
002-橙子價格-70,
003-香蕉價格-80,
例子:
content = '''
001-蘋果價格-60,
002-橙子價格-70,
003-香蕉價格-80,
'''
import re
p = re.compile(r'^\d+',re.M )
for one in p.findall(content):
print(one)
圖形化正則:
運行結果可以看到,^只匹配行首的所有數字,這裏的re.M是指定當前爲多行模式:
$(dollar符)
- 效果同^,之不過它匹配的是行尾的字符集
- 匹配行尾,要麼是一個字符串的行尾,要麼是換行符後面的任何位置。
例子:
content = '''
001-蘋果價格-60
002-橙子價格-70
003-香蕉價格-80
'''
import re
p = re.compile(r'\d+$',re.M )
for one in p.findall(content):
print(one)
圖形化正則:
運行結果可以看到,它把所有的價格都打印出來了:
\(反斜槓)
- 把元字符轉換爲普通符號的時候用\來轉移
簡單點說,就跟輸出字符串前加一個r,取消字符串裏的轉義效果是一樣的,上一個例子就明白了:
import re # 導入re包
a = 'tp,top$,toop,top' # 定義一個字符串a
c = r'top\$' # 這裏加了\
print re.findall(c,a) # 用規則c去匹配字符a並輸出
運行結果:
再來一個沒加 \ 的
import re # 導入re包
a = 'tp,top$,toop,top' # 定義一個字符串a
c = r'top$' # 定義c的正則規則
print re.findall(c,a) # 用規則c去匹配字符a並輸出
對比一下,就知道這個 \ 是幹嗎用的了:
|(豎線)
- 豎線表示 匹配 其中之一 。
特別要注意的是, 豎線在正則表達式的優先級是最低的, 這就意味着,豎線隔開的部分是一個整體
比如 綠色|橙 表示 要匹配是 綠色 或者 橙 ,
而不是 綠色 或者 綠橙
例如:
其他
\d:匹配數字,效果同[0-9]
\d 表示匹配任意一個數字,這裏可以不寫[],直接使用\d
[^\d] 表示匹配所有的非數字字符,效果同[^0-9]
例子:
import re
content = '123,456,789'
p = re.compile(r'\d')
for one in p.findall(content):
print(one)
運行結果:
\D:匹配非數字字符,效果同[^0-9]
例子:
import re # 導入re包
a = 'tpt 123 456 789' # 定義一個字符串a
c = r'\D' # 定義c的正則規則
print re.findall(c,a) # 用規則c去匹配字符a並輸出
運行結果:
\w:匹配數字、字母和下劃線,效果同[0-9a-zA-Z]
例子:
import re # 導入re包
a = 'tpt 123 456 789' # 定義一個字符串a
c = r'\w' # 定義c的正則規則
print re.findall(c,a) # 用規則c去匹配字符a並輸出
運行結果:
\W:匹配非數字、字母和下劃線,效果同[^0-9a-zA-Z]
例子:
import re # 導入re包
a = 'tpt 123 456 789' # 定義一個字符串a
c = r'\W' # 定義c的正則規則
print re.findall(c,a) # 用規則c去匹配字符a並輸出
運行結果:
\s:匹配任意的空白符(空格、換行、回車、換頁、製表符),效果同[ \f\n\r\t]
例子:
import re # 導入re包
a = 'tpt 123 789 \n' # 定義一個字符串a
c = r'\s' # 定義c的正則規則
print re.findall(c,a) # 用規則c去匹配字符a並輸出
運行結果:
\S:匹配任意的非空白符(空格、換行、回車、換頁、製表符),效果同[^ \f\n\r\t]
例子:
import re # 導入re包
a = 'tpt 123 789 \n' # 定義一個字符串a
c = r'\S' # 定義c的正則規則
print re.findall(c,a) # 用規則c去匹配字符a並輸出
運行結果:
正則特性
重複
例子:
import re # 導入re包
a = 'tpt 010-88888888 abc 123' # 定義一個字符串a
c = r'\d{3}-\d{8}' # 重複匹配三次數字-重複匹配八次數字
print re.findall(c,a) # 用規則c去匹配字符a並輸出
運行結果:
*(星號)
- 星號的作用就是指定前一個字符可以被匹配0次或更多次,匹配引擎會試着重複(貪婪匹配,儘可能多的去匹配)
例子:
content = '''
蘋果,是綠色色色色色的
橙子,是橙色的
草莓,是紅色的
烏鴉,是黑色的
'''
import re
p = re.compile(r'綠色*')
for one in p.findall(content):
print(one)
圖形化正則:
運行結果:
+(加號)
- 加號表示匹配至少一次或多次,也是貪婪匹配(儘可能多的去匹配)
這裏的加號,一定要跟星號對比起來,搞清楚兩者的區別,星號是可以出現0次的,但加號至少要出現一次,這就是兩者的區別,這樣說很模糊,我上兩個例子大家體會一下
星號例子:
content = '''
蘋果,是綠色色色色色色色的
橙子,是橙色的
草莓,是紅色的
烏鴉,是綠色的
猴子,是綠
'''
import re
p = re.compile(r'綠色*')
for one in p.findall(content):
print(one)
運行結果:
加號例子(代碼就不貼了):
因爲星號包括0次,所以綠後面即使沒有字符,也可以被輸出,但是加號至少包含一次,所以單個綠就不會被輸出,因爲它後面是0個色,要求是至少一個色,我們可以藉助兩者的圖形化正則來對比,就很容易明白他們的區別了:
加號:
星號:
?(問號)
- 問號就是最小匹配它前面的字符,匹配一次或零次
例子:
content = '''
蘋果,是綠色色色色色色色的
橙子,是橙色的
草莓,是紅色的
烏鴉,是綠色的
猴子,是綠
'''
import re
p = re.compile(r'是.色?')
for one in p.findall(content):
print(one)
圖形化正則:
因爲它允許匹配零次,所以最後一句即使後面沒有色,是綠也會被輸出,看一下運行結果:
{}(花括號)
正則的一個特性是你可以匹配任意長度字符集,另一個就是你可以指定表達式的一部分重複次數
這裏提到的這個{},作用是指定重複次數,用它搭配\d來實現一個過濾手機號碼的腳本:
content = '''
小李,15688887894,21
小張,18866716789,23
小王,13512342233,26
小崔,13552342133,18
小劉,13510348233,35
小唐,13211232243,42
'''
import re
p = re.compile(r'\d{11}')
for one in p.findall(content):
print(one)
圖形化正則:
運行結果:
貪婪匹配與非貪婪匹配
假如我們現在定義了一個字符集,要把下面的標籤全部都取出來
content = '<html><title></title></html>'
要這種效果:
['<html>', '<title>', '</title>', '</html>']
思路大概已經有了,就是匹配尖括號裏的內容即可:
content = '<html><title></title></html>'
import re
p = re.compile(r'<.*>')
print(p.findall(content))
但是這樣輸出的結果是這樣的:
['<html><title></title></html>']
這就不符合題意了,這也就是所謂的“貪婪匹配”,就是儘可能多的去匹配,兩個尖括號中間的,那麼我就取整個字符集好了,剛好就是兩個尖括號組成的,所以我們想要符合題意,就要對星號進行限制,就是給它加一個問號
content = '<html><title></title></html>'
import re
p = re.compile(r'<.*?>')
print(p.findall(content))
這樣運行後就符合題意了,可以看一下加問號與不加問號的圖形化正則:
加問號:
不加:
所以在日後的編程中要注意星號與加號,如果想要儘可能少,就加問號,反之不加。
正則中的組選擇
()(括號)
-
括號稱之爲 正則表達式的 組選擇。
-
組 就是把 正則表達式 匹配的內容 裏面 其中的某些部分 標記爲某個組。
-
我們可以在 正則表達式中 標記 多個 組
爲什麼要有組的概念呢?因爲我們往往需要提取已經匹配的 內容裏面的 某些部分的信息。 前面,我們有個例子,從下面的文本中,選擇每行逗號前面的字符串,也包括逗號本身 。
蘋果,蘋果是綠色的 橙子,橙子是橙色的 香蕉,香蕉是黃色的
代碼可以這樣寫:
content = '''
蘋果,蘋果是綠色的
橙子,橙子是橙色的
香蕉,香蕉是黃色的
'''
import re
p = re.compile(r'.*,')
for one in p.findall(content):
print(one)
但是運行後輸出的結果帶着逗號,我們又不希望逗號出現,所以我們在這裏使用()把前面的規則括起來,表示我們只取括號內的信息:
content = '''
蘋果,蘋果是綠色的
橙子,橙子是橙色的
香蕉,香蕉是黃色的
'''
import re
p = re.compile(r'(.*),')
for one in p.findall(content):
print(one)
圖形化正則:
運行結果:
那麼這是一組的,如果我們要提取多組呢?比如我們現在從網上抓取了一組數據,要求只輸出姓名+電話號碼
張三,手機號13388881234
李四,手機號15612348821
王五,手機號17456136666
那麼代碼可以這樣寫:
content = '''
張三,手機號13388881234
李四,手機號15612348821
王五,手機號17456136666
'''
import re
p = re.compile(r'(^.+),手機號(\d{11})',re.M)
for one in p.findall(content):
print(one)
圖形化正則:
運行結果:
做一個練習
現在我們從網上抓取了一則招聘信息,要求只輸出薪資數字:
Python3 高級開發工程師 上海互教教育科技有限公司上海-浦東新區2萬/月02-18滿員
測試開發工程師(C++/python) 上海墨鵾數碼科技有限公司上海-浦東新區2.5萬/每月02-18未滿員
Python3 開發工程師 上海德拓信息技術股份有限公司上海-徐彙區1.3萬/每月02-18剩餘11人
測試開發工程師(Python) 赫裏普(上海)信息科技有限公司上海-浦東新區1.1萬/每月02-18剩餘5人
那麼表達式可以這麼寫:
([\d.]+)萬/每?月
[\d.]+ 表示 匹配 數字或者點的多次出現 這就可以匹配像: 3 33 33.33 這樣的 數字
萬/每?月 是後面緊接着的,如果沒有這個,就會匹配到別的數字, 比如 Python3 裏面的3。
其中 每?月 這部分表示匹配 每月 每 這個字可以出現 0次或者1次。
所以代碼這麼寫:
content = '''
Python3 高級開發工程師 上海互教教育科技有限公司上海-浦東新區2萬/月02-18滿員
測試開發工程師(C++/python) 上海墨鵾數碼科技有限公司上海-浦東新區2.5萬/每月02-18未滿員
Python3 開發工程師 上海德拓信息技術股份有限公司上海-徐彙區1.3萬/每月02-18剩餘11人
測試開發工程師(Python) 赫裏普(上海)信息科技有限公司上海-浦東新區1.1萬/每月02-18剩餘5人
'''
import re
p = re.compile(r'([\d.]+)萬/每{0,1}月',re.M)
for one in p.findall(content):
print(one)
圖形化正則:
運行結果:
使用正則表達式切割字符串
- 字符串對象的split()方法只適用於簡單的字符串分割,有時你需要更加靈活的字符串切割。
比如,我們需要從下面字符串中提取武將的名字:
names = '關羽; 張飛, 趙雲,馬超, 黃忠 李逵'
我們發現這些名字之間, 有的是分號隔開,有的是逗號隔開,有的是空格隔開, 而且分割符號周圍還有不定數量的空格
這時,可以使用正則表達式裏面的 split 方法:
import re
names = '關羽; 張飛, 趙雲,馬超: 黃忠 李逵'
namelist = re.split(r'[;,:\s]\s*',names)
print(namelist)
\s表示空格,它後面爲什麼加*而不加+呢?是因爲 可以匹配0到任意次,而+至少匹配一次,題中的“趙雲,馬超”中間沒有空格,所以\s就出現了0次,爲了能夠分別正確輸出他們,所以採用\s,即使沒有出現空格,也要給我匹配上。
運行結果: