Python知識小結------正則表示式
一、前言
在平日的的文字查詢中,我們只要按下Crtl-F就可以進行內容查詢。而正則表示式則是將你要查詢的內容固定成為一個“模式”,我們在做查詢時會十分方便。
例如:我們要判斷一串數字是否為電話號碼時,使用正則表示式和不使用正則表示式的情況。我們知道電話號碼的模式是:400-555-4567
不使用正則表示式:
def isPhoneNumber(text): if len(text)!=12: #判斷長度是否為12位 return False for i in range(0,3): if not text[i].isdecimal(): #判斷前三位是否為數字 return False if text[3]!='-': #判斷第四位是否為‘-’ return False for i in range(4,7): if not text[i].isdecimal(): #判斷5到7位是否為數字 return False if text[7]!='-': #判斷第八位是否為‘-’ return False for i in range(8,12): if not text[i].isdecimal(): #判斷9到12位是否為數字 return False return True
使用正則表示式:
import re
phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
mo = phoneNumRegex.search("400-555-4567")
if mo ==None:
print("沒有找到")
else:
print("匹配成功!"+mo.group())
首先從程式碼量來看,使用正則表示式的程式碼很簡潔,執行效率是很快的。而傳統程式碼需要對每一個字元進行判斷,其中多次使用if判斷和for迴圈,只要有一處有問題就會輸出fasle。如果資料量過大的話,其執行效率就會變低,因此使用正則表示式是十分有效的。
二、re模組簡介
聊到Python正則表示式的支援,首先肯定會想到 re 庫,這是一個Python處理文字的 標準庫。
標準庫 的意思表示這是一個 Python內建模組 ,不需要額外下載,目前Python內建模組大概有300個。可以在這裡檢視Python所有的內建模組:https://docs.python.org/3/py-modindex.html#cap-r
因為re是內建模組,所以不需要再下載,使用時直接引入即可:
import re
re模組官方文件:https://docs.python.org/zh-cn/3.8/library/re.html
三、常用的正則字元和規則
### 常用元字元 ### 1 .:匹配任何一個字元; 2 ^:匹配除去所列首個字元外的所有字元; ^\d表示必須以數字開頭。 3 $:匹配字串的尾部字元 \d$表示必須以數字結束 4 []:由一對方括號括起來的字元,表明一個字元集合,能夠匹配包含在其中的任意一個字元。’-‘ 減號來指定一個字元集合的範圍。例子:[a-zA-Z][^a-zA-Z] 5 | 將兩個規則並列起來,注意是匹配兩邊所有的規則 要匹配 ‘I have a dog’或’I have a cat’,需要寫成r’I have a (?:dog|cat)’ ,而不能寫成 r’I have a dog|cat’ 5 (?: ) 如果想限定它的有效範圍,必需使用一個無捕獲組 ‘(?: )’包起來 6 \d 匹配數字,這是一個以’\’開頭的轉義字元,’\d’表示匹配一個數字,即等價於[0-9] 7 \D 匹配非數字 這個是上面的反集,即匹配一個非數字的字元,等價於[^0-9]。注意它們的大小寫 下面我們還將看到Python的正則規則中很多轉義字元的大小寫形式,代表互補的關係。 8 \w 匹配字母和數字 匹配所有的英文字母和數字,即等價於[a-zA-Z0-9]。 \W 等價 [^a-zA-Z0-9] 9 \s 匹配間隔符 即匹配空格符、製表符、回車符等表示分隔意義的字元,它等價於[ \t\r\n\f\v]。(注意最前面有個空格) 補集: \S 10 \A 匹配字串開頭 匹配字串的開頭。它和’^’的區別是,’\A’只匹配整個字串的開頭,即使在’M’模式下,它也不會匹配其它行的行首。 11 \Z 匹配字串結尾 匹配字串的結尾。它和’$’的區別是,’\Z’只匹配整個字串的結尾,即使在’M’模式下,它也不會匹配其它各行的行尾。 12 \b’ 匹配單詞邊界 它匹配一個單詞的邊界,比如空格等,不過它是一個‘0’長度字元,它匹配完的字串不會包括那個分界的字元。 而如果用’\s’來匹配的話,則匹配出的字串中會包含那個分界符 13 \B 匹配非邊界 它同樣是個0長度字元。re.findall( r’\Bbc\w+’ , s ) #匹配包含’bc’但不以’bc’為開頭的單詞 ['bcde'] #成功匹配了’abcde’中的’bcde’,而沒有匹配’bcd’ 14 (?# ) 註釋 Python允許你在正則表示式中寫入註釋 ### 重複 規則 ### 15 * 0或多次匹配 16 + 1次或多次匹配 表示匹配前面的規則至少1次,可以多次匹配 17 ? 0或1次匹配 只匹配前面的規則0次或1次 ### 精確匹配和最小匹配 ### 18 {m} 精確匹配m次 {m,n} 匹配最少m次,最多n次。(n>m) 指定最少3次:{3,} 最大為5次:{,5} 例子: re.findall( r’\b\d{3}\b’ , s ) # 尋找3位數 19 ‘*?’ ‘+?’ ‘??’ 最小匹配 ‘*’ ‘+’ ‘?’通常都是儘可能多的匹配字元(貪婪匹配)。有時候我們希望它儘可能少的匹配。 #例子 re.match(r'^(\d+)(0*)$', '102300').groups() #('102300', '') # re.match(r'^(\d+?)(0*)$', '102300').groups() #('1023', '00') ### 前向界定與後向界定 ### 20 (?<=…) 前向界定 括號中’…’代表你希望匹配的字串的前面應該出現的字串。 前向界定括號中的表示式必須是常值,也即你不可以在前向界定的括號裡寫正則式 re.findall( r’(?<=[a-z]+)\d+(?=[a-z]+)' , s ) # 錯誤的用法 21 (?=…) 後向界定 不過如果你只要找出後面接著有字母的數字,你可以在後向界定寫正則式: re.findall( r’\d+(?=[a-z]+)’, s ) 22 (?<!...) 前向非界定 只有當你希望的字串前面不是’…’的內容時才匹配 23 (?!...) 後向非界定 只有當你希望的字串後面不跟著’…’內容時才匹配。 ### 使用組 ### 24 () 包含在’()’中的內容,而雖然前面和後面的內容都匹配成功了,卻並不包含在結果中, 用group()或group(0)返回匹配的所有結果,用 group(1),(2)...返回第1,2...個()裡面的內容 25 (?(id/name)yes-pattern|no-pattern) 判斷指定組是否已匹配,執行相應的規則 這個規則的含義是,如果id/name指定的組在前面匹配成功了,則執行yes-pattern的正則式,否則執行no-pattern的正則式。
四、簡單例項說明
首先明確建立正則表示式的步驟:
1.用import re 匯入正則表示式模組
2.用re.compile()函式建立一個Regex物件
3.向Regex物件的search()方法傳入想要查詢的字串。它會返回一個Match物件
4.呼叫Match物件的group方法,返回實際匹配文字的字串
1.普通正則表示式
import re
#定義一個變數來建立正則表示式
tmp = re.compile(r'hello')
s = tmp.search('goodbye bihao,hellhelowahaha')
if s == None:
print("麼找到")
else:
print("找到了\n"+s.group())
2.用管道匹配多個分組
字元|稱為“管道”。希望匹配許多個表示式中的一個時,就可以使用它。例如:正則表示式r'1234|yang 中將會匹配出'1234'或者'yang',如果1234和yang都出現在被查詢的字串中,那麼第一次匹配出來的文字將會作為Match物件返回。
import re
#管道匹配多個分組
src = re.compile(r'1234|yang')
mo1 = src.search(' yanghuaixu 1236712346534')
mo2 = src.search('1236712346534 yanghuaixu ')
if mo1 == None:
print("mo1沒找到")
else:
print("mo1找到了:"+mo1.group())
if mo2 == None:
print("mo2中沒有找到")
else:
print("mo2找打了:"+ mo2.group())
3.用問號實現可選匹配
有時候,想匹配的模式是可選的,此時需要使用到的符號是?,正則表示式中(wo)?部分表明,wo是可選擇的分組,如果要匹配的字串中有wo,則就匹配出來,如果沒有,就不匹配。所以次正則表示式既會匹配policeman,也會匹配policewoman 。
import re
#問號實現可選擇匹配
batRegx = re.compile(r'police(wo)?man')
img = batRegx.search('i am a policeman')
if img == None:
print("沒找到img")
else:
print("img找到了:"+img.group())
img1 = batRegx.search('i am a policewoman')
if img1 == None:
print("img1沒找到")
else:
print("img1找到了:"+img1.group())
4.用星號匹配零次或者多次
*意味著正則表示式“匹配零次或者多次”。即星號之前的分組,可以在文字中出現任意次。它可以完全不存在,或一次又一次的重複。則是wo在字串中出現幾次,就匹配幾次進行返回。
import re
#用星號匹配零次或者多次
emc = re.compile(r'(wo)*man')
mo2 = emc.search('hello everyone i am a policewoman')
mo3 = emc.search('helloeveryone i am a policewowowowowoman')
if mo2 ==None:
print("沒找到")
else:
print("mo2找到了:"+mo2.group())
if mo3 == None:
print("沒找到")
else:
print("mo3找到了:"+mo3.group())
5.用加號匹配一次或者多次
+意味著正則表示式“匹配一次或者多次,但至少要出現一次”。
import re
#用加號匹配一次或者多次
efc = re.compile(r'(wo)+man')
mo3 = efc.search('sdfsfdsadad policeman jiakwokak')
if mo3 == None:
print("mo3沒找到")
else:
print("mo3找到了:"+mo3.group())
mo4 = efc.search('adiaheiadhi am a policewowowoman hahahahhello')
if mo4 == None:
print("mo4沒找見")
else:
print("mo4找見了:"+ mo4.group())
6.findall()方法
除了search方法外,Regex物件也有一個findall()方法。search()方法將返回一個Match物件,包含被查詢字串中的"第一次"匹配的文字,而findall()方法將返回一組字串,包含所有符合匹配的字串。
import re
#findall方法
arg = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
print(arg.findall('400-200-3000 111-111-1111 456-890-0000 666-666-6666'))
7.字元分類
\d可以代表任何數字,也就是說\d是正則表示式(0|1|2|3|4|5|6|7|8|9)的縮寫。常見的字元分類如下表:
縮寫字元分類 | 表示 |
\d | 0到9的任何數字 |
\D | 除0到9的數字以外的任何字元 |
\w | 任何字母、數字或下劃線字元(可以認為是匹配"單詞"字元) |
\W | 除字母、數字和下劃線以外的任何字元 |
\s | 空格、製表符和換行符(可以認為是匹配"空白"字元) |
\S | 除空格、製表符和換行符以外的任何字元 |
import re
#字元分類
mmf = re.compile(r'\d+\s\w+') #\d+表示匹配一個或多個數字,\s表示匹配一個空白字元,\w+表示匹配一個或多個 字母數字或下劃線
print(mmf.findall('1 hello,2,3 world,4aaa,5 _hello,6 HELLO,7 h_ello,8_heello,9 AA9AA'))
8.通配字元
.(句點)表示“萬用字元”。它可以匹配除了換行之外的所有字元。點-星萬用字元可以匹配所有的字串。
import re
#通配字元
mtr = re.compile(r'楊..')
print(mtr.findall('楊樹 楊梅 陽光 楊海明 洋洋得意'))
#用點-星匹配所有字元
mtt = re.compile(r'FirstName(.*) LastName(.*)')
mo6 = mtt.search('FirstName: Star LastName: Yang')
print(mo6.group(1))
print(mo6.group(2))
還有點-星萬用字元的“貪心”模式與“非貪心”模式:貪心模式會匹配到最後一個">"出現所包含的字串,非貪心模式會匹配到第一次">"出現所包含的字串
import re
#點-星貪心與非貪心模式:
#非貪心:
notanxin = re.compile(r'<.*?>')
mo7 = notanxin.search('<to server for> dinner>')
print(mo7.group())
#貪心:
tanxin = re.compile(r'<.*>')
mo8 = tanxin.search('<to server for> dinner> hello> 你好>')
print(mo8.group())