本節知識大綱:
一、正則表達式介紹
正則表達式是什么?正則表達式是一種特殊的字符串模式,用于匹配一組字符串,就好比模具做產品,而正則就是這個模具,定義一種規則去匹配符合規則的字符。
如果我們對字符串有要求,我們就可以通過正則表達式把它表示出來,我們可以用正則表達式去匹配符合規則的字符串;
正則表達式的處理對象是字符串,主要應用正則表達式的操作有:
- 驗證
- 查找
- 替換
1. 正則表達式的匹配流程:
2. 正則表達式的應用流程
match()是用于校驗的函數
案例01:
輸入一個三位數,通過正則表達式輸入的是否滿足要求?
import re
input_number = input("請輸入一個三位數:")
match = re.match("^\d{3}$",input_number)
if match is None:
print("不符合要求")
else:
print("符合要求")
案例02:
判斷輸入的手機號碼是否有效,要求:號碼位數是11位;第一位是數字1;第二位是數字345789;后面是0-9均可
# 步驟一:通過引擎編譯出對象
import re
pattern = re.compile(R"[1][345789]\d{9}") # 編譯出pattern對象
input_mobile = input("請輸入手機號碼:")
# 步驟二:對輸入的內容進行匹配
# print(pattern.match(input_mobile))
if pattern.match(input_mobile):
print("輸入的手機號有效!")
else:
print("輸入的手機號無效!")
3.正則表達式的基本語法
二、預定義字符
如果每次都通過代碼來驗證正則表達式是否正確效率有點低,我們可以通過在線工具來校驗我們的正則表達式是否正確,比如oschina的在線正則表達式測試工具;當然在Windows系統下可以使用RegexBuddy工具進行檢測。
普通字符:字母、數字、下劃線以及沒有特殊意義的符號,都是普通字符。
元字符:這里主要有11個構成正則表達式元字符
. \ | ^ $ * + ? [] {} ()
1. 通配字符
.
是一個能匹配除\n
以外任何字符的通用匹配符,例如,我們想匹配以a開頭的,后面跟3個任意字符的正則表達式可以這樣寫:
re.match("^a...","avfs")
另外三個連續的通配符可以寫成{3}
像這樣:
re.match("^a.{3}","avfs")
這里也可以使用findall()方法,能返回待匹配字符串中所有與正則表達式相匹配的字符串
print(re.findall("a.{3}","avfssssadddadddaef")) # 把匹配開頭的^去掉了
輸出結果:
['avfs', 'addd', 'addd']
2. 反斜杠
反斜杠加字母有時候在轉義字符和正則表達式中功能沖突,通常的解決辦法是使用r
或者R
取消轉義。
三、字符集
1. 系統正則表達式字符集
案例:非數字開頭 + 兩個空格 + 數字/字母/下劃線
import re
print(re.match(R"^\D\s{2}\w$","a _"))
2. 用戶自定義正則表達式字符集
除了使用系統字符集以外,用戶可以自定義字符集
注意:這里一個中括號只能匹配一個字符;
^
在中括號外表示一行開始,在中括號里面表示取反、排除的意思
案例:使用自定義字符集,匹配不區分大小寫以a-f開頭,接接著是三個小寫字母,再后面是以偶數結尾
import re
print(re.findall(R"[a-fA-F][a-z]{3}[02468]","bddf42fbas8"))
注意:除了
^
、-
以外,如果把其它任何特殊符號放到[]
里,那么就自動去掉特殊意義,只表示符號本身的含義,如.
在[]
里只表示.
點號的意思,沒有了通配符的功能。
四、正則表達式中量詞
1. 三種量詞符號
案例:
通過正則表達式匹配英文單詞,要求以na開頭,以e來結尾
方法一:使用
*
號
print(re.findall(R"na[a-z]*e","my name is Alice,nae,nattore"))
輸出結果:
['name', 'nae', 'nattore']
使用*
號匹配name(重復1次),匹配nae(重復0次),匹配nattore(重復多次)
方法二:使用+
號
print(re.findall(R"na[a-z]+e","my name is Alice,nae,nattore"))
輸出結果:
['name', 'nattore']
使用+
號,只能匹配name(重復1次),匹配nattore(重復多次)
方法三:使用?
號
print(re.findall(R"na[a-z]?e","my name is Alice,nae,nattore"))
輸出結果:
['name', 'nae']
使用?
號,只能name(重復1次)和nae(重復0次)
案例:
判斷身份證號是否有效,
特征一:長度18或者15位
特征二:前17位是數字
特征三:最后一位是數字或者x
print(re.match(r"(\d{14}[0-9x]|\d{17}[0-9x])","34262320001218646x"))
2. 花括號表示重復次數
(1){n}表示重復n次
# 正則表達式匹配以na開頭加上4個小寫字母并以e結尾:
print(re.findall(R"na[a-z]{4}e","my name is Alice,nae,nattore"))
輸出結果:
['nattore']
(2){n,m}表示重復n到m次
# 正則表達式匹配以na開頭加上3-10個小寫字母并以e結尾:
print(re.findall(R"na[a-z]{3,10}e","my name is Alice,naicajoe,nattorirce"))
輸出結果:
['naicajoe', 'nattorirce']
(3){n,}表示重復n次到無限次
# 正則表達式匹配以na開頭加上3個以上的小寫字母并以e為結尾:
print(re.findall(R"na[a-z]{3,}e","my name is Alice,naicajoe,nattorighjrce"))
輸出結果:
['naicajoe', 'nattorighjrce']
3. 貪婪模式和非貪婪模式
(1)貪婪模式
貪婪模式是指在Python在默認情況下量詞會盡可能多的匹配
print(re.findall("\d+","12345678888888abc")) # 盡量多的匹配
print(re.findall("\d*","12345678888888abc"))
print(re.findall("\d{3,}","12345678888888abc"))
print(re.findall("\d{3,8}","1234567888888888abc"))
輸出結果:
['12345678888888']
['12345678888888', '', '', '', '']
['12345678888888']
['12345678', '88888888']
(2)非貪婪模式
在表達式的結尾加上問號?
,會切換成非貪婪模式
print(re.findall("\d+?","12345678888888abc"))
print(re.findall("\d{3,}?","12345678888888abc"))
輸出結果:
['1', '2', '3', '4', '5', '6', '7', '8', '8', '8', '8', '8', '8', '8']
['123', '456', '788', '888']
五、字符邊界
Python正則表達式字符邊界主要有四種:
^ 開始位置
$ 結束
\b 單詞邊界
\B 非單詞邊界
1. 字符串的開始和結束
案例:
輸入一個6位數字,必須要以95開頭,以8結尾的數字
print(re.findall(r"^95\d{3}8$","958348"))
輸出結果:
['958348']
2. 字符串的邊界
\b
表示單詞的邊界,指某一個位置前后不都是字母、數字、下劃線(\w
)
案例:
輸入一句英文,找出里面以a、b、c開頭的單詞
str01 = "Use this toggle to the left to manage how your " \
"browser uses BBC’s performance cookies. If you’re " \
"outside the UK you can also use the toggle to set " \
"your preferences for personalised advertising cookies."
pattern = re.compile(r"\b[abcABC][a-z]*\b")
print(pattern.findall(str01))
輸出結果:
['browser', 'cookies', 'can', 'also', 'advertising', 'cookies']
六、邏輯判斷
正則表達式中表示邏輯或的是用符號|
,分為整體或和部分或
(1)整體或
案例:
簡單匹配身份證號,現在的身份證號是18位以前是15位,我們希望兩者都兼容;前面全是數字,最后一位可以是數字或者x
import re
print(re.findall("\d{14}[0-9x]|\d{17}[0-9x]]","34262219971012x"))
輸出結果:
['34262219971012x']
(2)部分或
案例:在一段英文句子中,找出es、er或者ing結尾的單詞
str01 = "Use this toggle to the left to manage how your " \
"browser uses BBC’s performance cookies. If you’re " \
"outside the UK you can also use the toggle to set " \
"your preferences for personalised advertising cookies."
pattern = re.compile(r"\b[a-z]*(es|ing|er)\b")
print(pattern.findall(str01))
輸出結果:
['er', 'es', 'es', 'es', 'ing', 'es']
為什么沒有顯示出完整的單詞呢,這就涉及到正則表達式中分組的知識了;
七、 分組
1. 捕獲組與非捕獲組
分組是我們正則表達式中一個難點,把正則表達式的一部分用括號括起來作為一個組;主要包括捕獲組()
非捕獲組(?:)
如何進行捕獲呢?待捕獲的表達式用小括號括起來,編號從1開始,后面通過反斜杠加數字標號進行調用。
我們以一個案例來進行解釋
案例:
在前一段英文中,匹配這樣的單詞,有5個字符;第一個字母和第五個一樣,第二個和第四個一樣,比如abcba
分析:
因為匹配的是單詞第一個和最后一個都是單詞的邊界,故正則表達式的前后都用\b
,第一個字母和第二個字母后面都要用到所以分別給它們設置捕獲組。用小括號括起來([a-z])
、([a-z])
第三字母后面用不到所以不設置捕獲組,第四個字母和第五個字母調用前面的捕獲組,所以通過反斜杠加數字編號來進行調用,所以主要的正則表達式為:\b([a-z])([a-z])[a-z]\2\1\b
import re
print(re.findall(r"\b([a-z])([a-z])[a-z]\2\1\b","fdadd abcba"))
輸出結果:
[('a', 'b')]
貌似是匹配到了,但是輸出的結果并不滿意,并不是完整的顯示內容這是怎么回事呢?
原因:
如果對正則表達式做了分組,使用findall函數則顯示捕獲組所匹配的內容,不能完整顯示,如果想完整顯示的話有兩個解決辦法:
方法一:使用非捕獲組
如果不需要對捕獲組的內容調用,可以使用非捕獲組,在表達式前加上?:就可以了,表示只捕獲數據了,只用來表達條件。回到我們前面的案例,英文句子中匹配單詞,怎樣才能完整顯示呢?對于后面不需要調用的正則表達式分組,我們使用非捕獲組的方式,就是表達式前加上問號和冒號即可,則前面的正則表達式\b[a-z]*(es|ing|er)\b
可以寫成\b[a-z]*(?:es|ing|er)\b
,這樣我們的輸出結果就是完整的單詞了。
方法二:使用迭代函數finditer
將findall函數改為迭代函數finditer
import re
match_result = re.finditer(r"\b([a-z])([a-z])[a-z]\2\1\b","fdadd abcba")
match_list = []
for i in match_result:
match_list.append(i.group(0))
print(match_list)
輸出結果:
['abcba']
這樣就能完成顯示匹配結果了
2. 分組的命名
捕獲組默認是從數字1開始編號的,但是如果捕獲組數量多的話,最好還是能給捕獲組命名方便調用,那么怎么給捕獲組命名呢?
命名的方法:加問號加P跟著尖括號里寫上名稱(?P<名稱>)
;調用的方法:加問號加P等于號號碼跟上名稱(?P=name)
前面的正則表達式\b([a-z])([a-z])[a-z]\2\1\b
命名可以寫成\b(?P<number01>[a-z])(?P<number02>[a-z])[a-z](?P=number02)(?P=number01)\b
實際代碼可以寫成:
print(re.findall(r"\b(?P<number01>[a-z])(?P<number02>[a-z])[a-z](?P=number02)(?P=number01)\b","fdadd abcba"))
八、零寬斷言
Python正則表達式的零寬斷言有著不同的稱呼:零寬度斷言、預搜索、環視等等,它是干嘛的呢?它是用來匹配一個位置
零寬的意思是不占用字符寬度、位置,比如\b
表示單詞起始或者結束的位置,^
表示正則表達式的開始;$
表示正則表達式的結束;
零寬斷言的特征:
(1)做位置的匹配,不占寬度
(2)匹配的內容不計入最終的結果
(3)主要用作判斷某個位置的前后字符
1. 語法格式
2. 解釋
有一段字符串我們根據它前面的表達式來找到匹配的字符串,比如要在www.baidu.com
中查找名稱baidu
,我們可以根據條件聲明根域名是以www.
開頭的,.com
結尾的。那么零寬斷言的表達式就可以寫成(?<=www[.])[a-z]+(?=[.]com)
代碼示例:
import re
print(re.findall(r"(?<=www[.])[a-z]+(?=[.]com)","www.baidu.com"))
輸出結果:
['baidu']
注意:匹配輸出的內容是零寬斷言括號外面的部分