133 python|第六部分:正則表示式

語言: CN / TW / HK

點選關注 「緩緩而行」, 讓我們一起探索 python 學習之路~

前言:本篇文章是Python資料管理中的第二部分內容——正則表示式。

目錄如下:

01 概述

1.1 定義

1.2 原理

02 元字元使用

2.1 匹配字元

2.2 匹配重複

2.3 匹配位置

2.4 其他

03 匹配規則

     3.1 特殊字元匹配

3.2 正則表示式分組

3.3 貪婪和非貪婪模式

3.4 正則表示式匹配原則

04 re模組

4.1 re模組概述 

    4.2 得到match物件的函式 finditer、search、 match 

4.3 查詢多個匹配項 findall finditer

4.4 分割 split

4.5 替換 sub

引 言

我們日常在office、wps辦公軟體、在python的整合開發環境pycharm等軟體中,通過ctrl f /ctrl r 可以實現對文字的搜尋、定位和提取功能,大大提高了工作效率,其中的底層原理和正則表示式是有關聯的。那讓我們來看看什麼是正則表示式呢?

01 概述

        

1.1 定 義

  

正則表示式(Regular Expression),通常縮寫成 regex、RE 。單數有regexp、regex,複數有regexps、regexes、regexen,是電腦科學的一個概念。 

正則表示式是 使用單個字串來描述、匹配一系列匹配某個句法規則的字元 。在很多文字編輯器裡,通常被用來 檢索、替換那些匹配某個模式的文字 。許多程式設計語言都支援利用正則表示式進行字串操作。

例如,在Perl中就內建了一個功能強大的正則表示式引擎。最早起的RE源於程式語言Perl,大多數程式語言都支援RE。 

維基百科

1.2 原 理

正則表示式是一種文字模式,包括 普通字元 (例如,a 到 z 之間的字母)和 特殊字元 (稱為"元字元"),用以 描述一定的字串規則 ,比如重複、位置等,來表達某類特定的字串,進而匹配。

可以檢視本文參考資料[1]詳細瞭解。

正則表示式的執行過程

圖源:豬哥66

正則表示式的兩種引擎:

DFA (Deterministic finite automaton) 確定型有窮自動機  

NFA(Non-deterministic finite automaton) 非確定型有窮自動機

正則表示式的引擎執行圖

圖源:豬哥66

02 元字元使用

2.1 匹配字元

01.元字元使用

                                     

類別:匹配字元
元字元 作用 匹配規則
. 匹配單個字元 匹配除換行外任意一個字元
[...] 匹配字符集 匹配字符集中任意一個字元
[^...] 匹配字符集反集 匹配除了字符集以外的任意一個字元

\d

匹配任意數字字元

\D

匹配任意非數字字元

\w

匹配任意普通字元 注:普通字元指數字、字母、下劃線、漢字

\W

匹配任意非普通字元

\s

匹配任意空字元 注:空字元指\r \n \t \v \f

\S

匹配任意非空字元

02.例項

# 先匯入re模組,re模組的詳細內容在本文第四部分進行詳細梳理
import re


#普通字元
result = re.findall('ab','abcdabcdcdjs')
# findall函式是re模組的函式之一,表示匹配目標字串所有內容,在本文第四部分進行梳理
print(result) #['ab', 'ab']


# . 匹配單個字元
result = re.findall('.角大王','金角大王,銀角大王,銅角大王')
print(result)#['金角大王', '銀角大王', '銅角大王']


# [...] 匹配字符集
result = re.findall('[A-Z]','Time is Money')
print(result)#['T', 'M']


# [^...] 匹配字符集
result = re.findall('[^A-Z]','Time is Money')
print(result)#['i', 'm', 'e', ' ', 'i', 's', ' ', 'o', 'n', 'e', 'y']


# \d 匹配任意數字字元
result = re.findall('\d{1,6}','郵編:231078, 地址: 快樂星球52號')
print(result)#['231078', '52']


# \D 匹配任意非數字字元
result = re.findall('\D+','https://blog.csdn.net/u014044812')
print(result)#['https://blog.csdn.net/u']


# \w 匹配任意普通字元
result = re.findall('\w+','1+1>2嗎')
print(result)#['1', '1', '2嗎']


# \W 匹配任意非普通字元
result = re.findall('\W+','1+1>2嗎')
print(result)#['+', '>']


# \s 匹配任意空字元
result = re.findall('\w+\s+\w+','叫你一聲 你敢答應嗎')
print(result)#['叫你一聲 你敢答應嗎']


# \S 匹配任意非空字元
result = re.findall('\S+','叫你一聲 你敢答應嗎')
print(result)#['叫你一聲', '你敢答應嗎']


result = re.findall('\S+\s+\S+','叫你一聲 你敢答應嗎')
print(result)#['叫你一聲 你敢答應嗎']

2.2 匹配重複

01.元字元使用

                                     

類別: 匹配重複
元字元 匹配規則
* 匹配前面的字元出現0次或多次
+ 配前面的字元出現1 次或多次
配前面的字元出現0 次或1次
{n}

配前面的字元出現n

{m,n}

配前面的字元出現m-n

寫法: { m , n } { m ,}{, n }

注:匹配重複的元字元(除{n}外)會有貪婪模式和非貪婪模式之分,在下文中進行介紹。

02.例項

import re # 匯入re模組


1. * 匹配前面的字元出現0次或多次
例項1
result = re.findall('go*','goooooo-g')
print(result)#['goooooo', 'g']


例項2 匹配大寫字母開頭的單詞
result = re.findall('[A-Z][a-z]*','Out of Sight , out of Mind')
print(result)#['Out', 'Sight', 'Mind']


2. + 匹配前面的字元出現1次或多次
例項1
result = re.findall('[0-9]+','2022-01-18')
print(result)#['2022', '01', '18']


例項2
result = re.findall('go+','goooooo-g')
print(result)#['goooooo']


3. ? 前面的字元出現0次或者1
例項1
result = re.findall('go?','goooooo-g')
print(result)#['go', 'g']


例項2 匹配出正負數
result = re.findall('-?[0-9]+','南方的豔陽20°,北方的冰棍-5°')
print(result)#['20', '-5']


4. {n} 匹配出{}前面的字元出現n次
例項1 匹配手機號碼(非精細匹配,精細匹配可查閱本文參考資料[3])
result = re.findall('[1][3-9][0-9]{9}','18345678911')
print(result)#['18345678911']


5. {m,n} 匹配前面的字元出現m-n次
例項1 匹配qq號
result = re.findall('[1-9][0-9]{4,}','5678990')
print(result)#['5678990']

2.3 匹配位置

01.元字元使用

                                     

類別:匹配位置
元字元 作用 匹配規則
^ 匹配目標字串的開頭位置
$ 匹配目標字串的結束位置
\b 匹配單詞的邊界位置 注:單詞邊界指的是數字、字母、漢字、下劃線和其他字元的交界位置
\B 匹配非單詞的邊界 位置

02.例項

import re 


1.匹配位置 開頭^
result = re.findall('^hi','hi , pei qi')
print(result)#['hi']


2.匹配位置 結尾$
result = re.findall('qi$','hi , pei qi')
print(result)#['qi']


#開頭^ 結尾$ 判斷使用者名稱是否為 6-12位數字字母下劃線構成
result = re.findall('^[_0-9a-zA-Z]{6,12}$','60_peiqi')
print(result)#['60_peiqi']


3.匹配單詞邊界位置 \b
result = re.findall('is','This is a girl')
print(result) #['is', 'is']


例項1 \b是轉義字元,需要在前面加r去掉特殊含義,表示字元本身,和加/同理
result = re.findall(r'\bis','This is a girl')
print(result) #['is'] 表示第2個is


例項2 兩邊都是單詞邊界,可以更精確匹配,這個例子就把電話和非電話長度的數字區分開了
result = re.findall(r'\b[1][3-9][0-9]{9}\b','18345678990,1386253429473048324')
print(result)#['18345678990']


4.匹配非單詞邊界位置 \B不是轉義字元 不用加r
result = re.findall('\Bis','This is a girl')
print(result) #['is'] 表示第1個is

2.4 其他

01.元字元使用

                                     

類別:匹配其他
元字元 作用 匹配規則
| 匹配|兩側任意的正則表示式
\ 轉義字元 表示正則表示式中的特殊字元原本的含義
( ) 正則表示式分組 下文細講

02. 或關係例項

import re 


| 或 匹配|兩側任意的正則表示式,如果左側已經匹配到,就不會繼續判斷右側
result = re.findall('http|https','https://www.runoob.com')
print(result)#['http']

03 匹配規則

3.1 特殊字元匹配

如果匹配的目標字串中 包含正則表示式特殊字元 ,而在表示式中元字元 只想表示其本身含義 時需要進行 \處理

特殊字元有:. * + ? ^ $  / [ ] ( ) |

操作方法:在正則表示式元字元前加\使元字元失去其特殊含義,只表示字元本身。

import re 


\ 表示\後元字元本身含義
# 匹配小數 \.表示小數點,而非任意一個單個字元
result = re.findall('\d+\.?\d*','2.33 1024 0.1')
print(result) #['2.33', '1024', '0.1']


#匹配每天工資 \$表示$,而非字串的結束位置
result = re.findall('\$\d+','日薪:$267')
print(result)

3.2 正則表示式分組

在正則表示式中,可以 通過( )建立正則表示式的子組 ,改變元字元的操作物件, 進行整體操作

import re 
# +原來只匹配+前面一個字元,加()後,匹配的是()裡的所有內容,即ab
result = re.search('(ab)+','abababab')
# search函式是re模組的函式之一,表示匹配目標字串第一個符合內容,在本文第四部分進行梳理
print(result.group())#abababab


# 如果不加(),|後的 豬 和 狗\w{1,} 會被分別看做兩個整體,
# 本題的需求是匹配豬\w{1,} 或 狗\w{1,}
result = re.search('(豬|狗)\w{1,}','豬爸爸,豬媽媽')
print(result.group())#豬爸爸 search只匹配目標字串第一個符合的內容


#匹配IP地址 把IP分成四段,通過()表示每一段的操作方式都相同
result = re.search('(\d{1,3}\.?){4}','IP 192.168.50.12')
print(result.group()) #192.168.50.12


#匹配身份證(簡單匹配)
result = re.search('\d{17}(\d|x)','12345678912345678x')
print(result.group())#12345678912345678x

注:

一個正則表示式可以包含多個子組;子組可以巢狀,但內容不宜過於複雜;子組序列號一般從外到內,從左到右計數。

捕獲組:也屬於子組,可以通過一個名稱來表達子組的意義。

格式:(?P<name>pattern)

import re
#捕獲組,對功能沒有影響,kind在這裡表示動物的種類
result = re.search('(?P<kind>豬|狗)\w{1,}','豬爸爸,豬媽媽')
print(result.group()) # 豬爸爸

3.3 貪婪模式&非貪婪模式

貪婪模式 非貪婪模式
定義 預設情況下,匹配重複的元字元總是 儘可能多地 向後匹配內容 又稱為 懶惰模式 ,讓匹配重複的元字元 儘可能少地 向後匹配內容
轉換
??

貪婪模式→非貪婪模式,只需要在元字元後加?即可。

import re
# 貪婪模式 -->非貪婪
result = re.findall('ab*?','abbbbbbbbc')
print(result)#['a'] *匹配的是目標字串出現0次或多次,預設匹配多次,加?之後從貪婪轉換成非貪婪,匹配0次


#+匹配的是目標字串出現1次或多次,預設匹配多次,加?之後從貪婪轉換成非貪婪,匹配1次
result = re.findall('ab+?','abbbbbbbbc')
print(result)#['ab']


#?匹配的是目標字串出現0次或1次,預設匹配1次,加?之後從貪婪轉換成非貪婪,匹配0次
result = re.findall('ab??','abbbbbbbbc')
print(result)#['a']


#{m,n}匹配的是目標字串出現m~n次,預設匹配多次,加?之後從貪婪轉換成非貪婪,匹配m次
result = re.findall('ab{1,3}?','abbbbbbbbc')
print(result)#['ab']


#非貪婪模式也要在符合規則的前提下進行匹配(+變成非貪婪後,應匹配1次,但還要和c進行匹配,所以繼續匹配下去)
result = re.findall('ab+?c','abbbbbbbbc')
print(result)#['abbbbbbbbc']


# 匹配書名
books = "《大明王朝~~ 1566》,《後宮·甄嬛傳》,《琅琊榜——風起長林》"
#['《大明王朝~~ 1566》,《後宮·甄嬛傳》,《琅琊榜——風起長林》']
result = re.findall('《.+》',books) # 貪婪模式下,所有書一起組成列表的第一個元素


#['《大明王朝~~ 1566》', '《後宮·甄嬛傳》', '《琅琊榜——風起長林》']
result = re.findall('《.+?》',books)# 非貪婪模式下,每本書單獨作為列表的一個元素
print(result)

3.4 正則表示式匹配原則

4

  1. 正確性,能夠正確匹配出目標字串

  2. 排他性,除了目標字串之外,儘可能少地匹配其他內容

  3. 全面性,儘可能匹配所有目標字串,不遺漏

04 python re模組使用

4.1 re模組概述

python的模組分為 四種類型 ,分別是 內建模組,標準庫模組,第三方模組和使用者自己編寫的模組 。在 128 python|第三部分:python高階(一)程式結構和異常處理 中對標準庫模組之一的time模組進行了梳理,在 132 python|第五部分:檔案管理 中也對標準庫模組之一的os模組進行了簡單梳理。

re模組也是python的標準庫模組之一。re模組一共有12個函式,本文就其中的 search、match、findall、finditer、split、sub 六個函式進行梳理,剩下6個函式可以在本文參考資料[6]進行查閱。

圖源:豬哥66

re模組的6個函式

查詢一個匹配項
函式名 功能 語法 返回值
search 匹配目標字串 第一個 符合內容 re.search(pattern,string) 匹配內容 match object
  match  匹配目標字串 開始位置 re.match(pattern,string) 匹配內容 match object
查詢多個匹配項
findall 匹配目標字串 所有內容 re.findall(pattern,string) 匹配到的內容列表,如果正則表示式有子組,則只能獲得子組對應的內容
finditer 匹配目標字串 所有內容 re.finditer(pattern,string)
分割
split 切割 目標字串 re.split(pattern,string,max) 切割後的列表
替換
sub 使用一個字串 替換 匹配到的內容 re.sub(pattern,replace,string,count) 替換後的字串

注:

pattern:匹配的正則表示式

string:要匹配的字串

max:最多切割幾部分

replace:要替換的字串

count:最多替換幾處,預設全部替換

其中 finditer、 search、   match 3個函式返回的是 match物件 ,即除了匹配正則表示式對應的目標字串,還會獲取更豐富的匹配資訊,比如位置資訊。

4.2 得到match物件的函式

01.finditer

finditer 的返回值是迭代器,通過for迴圈遍歷,直接列印的結果是三個物件,並非正則表示式直接匹配到的結果。

為了獲取match物件更具體的匹配內容,需要用到兩個方法:

span() 獲取匹配內容的起始位置;

group(n=0)獲取match物件的匹配內容

預設值為0表示獲取整個match物件內容,如果是序列號或組名錶示獲取對應子組內容。

返回值:匹配字串

02.search

#search  第一個符合內容
import re
result = re.search('\d+',"農夫山泉:5,依雲:9")
print(result.group())#5


result = re.search('\w+:\d+',"農夫山泉:5,依雲:9")
print(result.group())#農夫山泉:5


result = re.search('(\w+):(?P<brand>\d+)',"農夫山泉:5,依雲:9")
print(result.group(1))#農夫山泉 通過組序號獲取對應內容
print(result.group('brand'))#5 通過組名獲取對應內容

02.match

import re


#match 開始位置
result = re.match('\w+','農夫山泉,有點甜')
print(result.group())#農夫山泉

4.2 查詢多個匹配項

findall函式例項

需求:列印李玉的身高


import re
data = "李玉:188 ,俞風城:187 ,趙錦辛:189"


#操作1:列印結果為所有人的身高
# result = re.findall("\d{3}",data) #['188', '187', '189']

#操作2:列印結果為李玉的名字和身高
# result = re.findall("李玉:\d{3}",data) #['李玉:188']


#操作3:列印結果為李玉的身高(符合需求)
#如果正則表示式有一個子組,雖然能夠匹配到複合目標的整體內容,即李玉:188,但只能返回組中的一部分,即188
# result = re.findall("李玉:(\d{3})",data) # ['188']


#操作4:列印結果為
#如果正則表示式有多個子組,返回的是元組列表,每個子組構成一個元組
result = re.findall("(\w+):(\d{3})",data)
print(result) #[('李玉', '188'), ('俞風城', '187'), ('趙錦辛', '189')]

4.3 分割

split例項

#讓字串更靈活地被分割
datetime = "2022-1-17 11:34:20"
result = re.split('\W+',datetime) # \W+表示非普通字元,本題對應的是- :
print(result)#['2022', '1', '17', '11', '34', '20']

4.4 替換

sub例項

# 檔案整理 多空格替換成1個
data = """Stray birds of summer come to my window to sing and fly away.
And yellow leaves of autumn, which have no songs, flutter and fall
there with a sign.
"""
result = re.sub("\s+"," ",data)
print(result)
# Stray birds of summer come to my window to sing and fly away. And yellow leaves of autumn, which have no songs, flutter and fall there with a sign.

下篇文章預告:資料庫

                                                                    

參考資料

[1] 正則表示式引擎執行原理

https://blog.csdn.net/u014044812/article/details/104250147

[2] Python正則表示式,這一篇就夠了!

https://blog.csdn.net/u014044812/article/details/104822722

[3]  正則表示式線上測試

http://c.runoob.com/front-end/854

[4]  Python3 正則表示式

https://www.runoob.com/python3/python3-reg-expressions.html

[5] 正則表示式 - 教程

https://www.runoob.com/regexp/regexp-tutorial.html

[6]  常用的python標準庫模組

https://www.cnblogs.com/xixi18/p/8406301.html

花樣早餐展位