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

花樣早餐展位