程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式師世界 >> 編程語言 >  >> 更多編程語言 >> Python

史上最易懂的Python正則表達式教程

編輯:Python

本文是速通教程,僅會介紹最基礎的知識。如需了解更多,請參考官方文檔或其他文章。 \textcolor{red}{\text{本文是速通教程,僅會介紹最基礎的知識。如需了解更多,請參考官方文檔或其他文章。}} 本文是速通教程,僅會介紹最基礎的知識。如需了解更多,請參考官方文檔或其他文章。

目錄

  • 前言
  • 一、re.match
    • 1.1 常用特殊字符
    • 1.2 常用特殊序列
    • 1.3 一些例子
      • 1.3.1 十六進制RGB顏色值匹配
      • 1.3.2 1-100之間的所有整數匹配
      • 1.3.3 郵箱格式匹配
      • 1.3.4 IPV4地址匹配
    • 1.4 修飾符
  • 二、其他函數
    • 2.1 re.fullmatch
    • 2.2 re.search
    • 2.3 re.findall
    • 2.4 re.sub
    • 2.5 re.split
  • 三、貪婪匹配與非貪婪匹配
  • 四、推薦網站

前言

什麼是正則表達式?正則表達式(regular expression)是一種特殊的字符串,它能幫助你方便的檢查一個字符串是否與給定的模式匹配。Python 中的 re 模塊提供了正則表達式的全部功能,在接下來的幾章,我們將詳細介紹 re 中最為常用的功能。

import re

一、re.match

re.match 嘗試從字符串的起始位置匹配一個模式,如果匹配成功,則會返回一個 re.Match 對象;如果匹配失敗,則返回 None。具體格式如下:

re.match(pattern, string, flags=0)
  • pattern:正則表達式;
  • string:要匹配的字符串;
  • flags:修飾符,用於控制匹配模式。

正則表達式既可以包含普通字符也可以包含特殊字符。如果僅包含普通字符,那就是匹配某個特定的字符串:

""" 嘗試從字符串 abcde 的起始位置匹配字符串 abc """
s = re.match('abc', 'abcde')
print(s)
# <re.Match object; span=(0, 3), match='abc'>

字符串 abc 在字符串 abcde 中的起始位置和終止位置分別為 02,根據左閉右開原則,span(0, 3)

如果要獲得匹配的結果,則可使用 group 方法:

print(s.group())
# abc

注意,以下這樣的匹配會失敗,因為 match 是從字符串的起始位置進行匹配的:

s = re.match('bcd', 'abcde')
print(s)
# None

1.1 常用特殊字符

常用特殊字符(special characters)列在下表中:

特殊字符作用.匹配除了換行符 \n 以外的任意單個字符^匹配起始位置$匹配終止位置(換行符之前)*表示 * 前的一個字符可以出現 0 次或任意多次+表示 + 前的一個字符可以出現 1 次或任意多次?表示 ? 前的一個字符可以出現 0 次或 1 次{m}表示 {m} 前的一個字符出現 m 次{m,}表示 {m,} 前的一個字符可以出現 m 次及以上{,n}表示 {,n} 前的一個字符最多出現 n 次{m,n}表示 {m,n} 前的一個字符出現 m 次到 n 次[]匹配 [] 中列舉出的字符()匹配 () 內的表達式,表示一個分組|

為簡便起見,我們定義一個 match 函數用來更為直觀地展示匹配結果:

def match(pattern, list_of_strings):
for string in list_of_strings:
if re.match(pattern, string):
print('匹配成功!結果為:', res.group())
else:
print('匹配失敗!')

.

match('.', ['a', 'ab', 'abc'])
# 匹配成功!結果為: a
# 匹配成功!結果為: a
# 匹配成功!結果為: a

因為我們是從頭開始匹配單個字符的,所以結果均為 a


^$

match('^ab', ['ab', 'abc', 'adc', 'bac'])
# 匹配成功!結果為: ab
# 匹配成功!結果為: ab
# 匹配失敗!
# 匹配失敗!
match('cd$', ['cd', 'acd', 'adc', 'cdcd'])
# 匹配成功!結果為: cd
# 匹配失敗!
# 匹配失敗!
# 匹配失敗!

看似是匹配以 cd 為結尾的字符串,但實際上別忘了 match 是從字符串的起始位置開始匹配的,因此上述語句實際上就是匹配字符串 cd


*+?

match('a*', ['aa', 'aba', 'baa', 'aaaa'])
# 匹配成功!結果為: aa
# 匹配成功!結果為: a
# 匹配成功!結果為: 
# 匹配成功!結果為: aaaa
match('a+', ['aa', 'aba', 'baa'])
# 匹配成功!結果為: aa
# 匹配成功!結果為: a
# 匹配失敗!
match('a?', ['aa', 'ab', 'ba'])
# 匹配成功!結果為: a
# 匹配成功!結果為: a
# 匹配成功!結果為: 

注意,a* 代表 a 可以出現 0 次或任意多次,因此 baa 去匹配會得到空字符串。


{m}{m,}{,n}{m,n}(注意沒有空格):

match('a{3}', ['abaa', 'aaab', 'baaa'])
# 匹配失敗!
# 匹配成功!結果為: aaa
# 匹配失敗!
match('a{3,}', ['aaab', 'aaaab', 'baaa'])
# 匹配成功!結果為: aaa
# 匹配成功!結果為: aaaa
# 匹配失敗!
match('a{,3}', ['aaab', 'aaaab', 'baaa'])
# 匹配成功!結果為: aaa
# 匹配成功!結果為: aaa
# 匹配成功!結果為: 
match('a{3,5}', ['a' * i for i in range(2, 7)])
# 匹配失敗!
# 匹配成功!結果為: aaa
# 匹配成功!結果為: aaaa
# 匹配成功!結果為: aaaaa
# 匹配成功!結果為: aaaaa

[]

match('[123]', [str(i) for i in range(1, 5)])
# 匹配成功!結果為: 1
# 匹配成功!結果為: 2
# 匹配成功!結果為: 3
# 匹配失敗!

注意,我們可以將 [123] 簡寫為 [1-3],這意味著若要匹配單個數字,則可以采用 [0-9] 這樣的正則表達式:

match('[0-9]', ['a', 'A', '1', '3', '_'])
# 匹配失敗!
# 匹配失敗!
# 匹配成功!結果為: 1
# 匹配成功!結果為: 3
# 匹配失敗!

更進一步,如果我們要想匹配區間 [ 1 , 35 ] [1, 35] [1,35] 內的所有整數,該如何做呢?很自然的一個想法是使用 [1-35],但仔細觀察下面的例子:

match('[1-35]', ['1', '2', '3', '4'])
# 匹配成功!結果為: 1
# 匹配成功!結果為: 2
# 匹配成功!結果為: 3
# 匹配失敗!

會發現數字 4 4 4 匹配失敗了。這是因為 - 只能連接相鄰的兩個數字,所以 [1-35] 實際上代表數字 1235。也就是說,除了這四個數字以外的數全部都會匹配失敗。

我們分三種情形考慮:十位數為 3 3 3,十位數為 1 1 1 或 2 2 2,只有個位數(需要使用運算 |

pattern = '3[0-5]|[12][0-9]|[1-9]'

該正則表達式的確能夠全部正確匹配 [ 1 , 35 ] [1,35] [1,35] 內的所有整數,但是:

match('3[0-5]|[12][0-9]|[1-9]', ['36', '350'])
# 匹配成功!結果為: 3
# 匹配成功!結果為: 35

我們會發現區間之外的數也能夠匹配成功。因此需要使用 $ 來防止誤判,正確做法是:

pattern = '(3[0-5]|[12][0-9]|[1-9])$'

其中 () 的作用之後會提及(這裡可以粗略地理解成視為一個整體)。

除此之外,我們還可以判斷給定的字符是否是字母,相應的正則表達式為 [a-zA-Z]

match('[a-zA-Z]', ['-', 'a', '9', 'G', '.'])
# 匹配失敗!
# 匹配成功!結果為: a
# 匹配失敗!
# 匹配成功!結果為: G
# 匹配失敗!

如果我們想要匹配非數字字符,則需要使用 ^,它表示取補集

match('[^0-9]', ['-', 'a', '3', 'M', '9', '_'])
# 匹配成功!結果為: -
# 匹配成功!結果為: a
# 匹配失敗!
# 匹配成功!結果為: M
# 匹配失敗!
# 匹配成功!結果為: _

()

""" 匹配多個 ab """
match('(ab)+', ['ac', 'abc', 'abbc', 'abababac', 'adc'])
# 匹配失敗!
# 匹配成功!結果為: ab
# 匹配成功!結果為: ab
# 匹配成功!結果為: ababab
# 匹配失敗!

注意 ab+ 這樣的正則表達式是無效的,它代表只有一個字符 a 和一個及以上的字符 b。因此我們必須用 () 將其括起來視為一個整體,也稱作一個分組。

1.2 常用特殊序列

\ 開頭並僅連一個字符的稱為特殊序列(special sequences),常用特殊序列列在下表中:

特殊序列作用\d等價於 [0-9],即所有數字(巧記:digit)\D等價於 [^\d],即所有非數字\s空格字符(巧記:space)\S等價於 [^\s],即所有非空格字符\w等價於 [a-zA-Z0-9_],即所有單詞字符,包括字母、數字和下劃線(巧記:word)\W等價於 [^\w],即所有非單詞字符
""" 示例一 """
match('\d', ['1', 'a', '_', '-'])
# 匹配成功!結果為: 1
# 匹配失敗!
# 匹配失敗!
# 匹配失敗!
match('\D', ['1', 'a', '_', '-'])
# 匹配失敗!
# 匹配成功!結果為: a
# 匹配成功!結果為: _
# 匹配成功!結果為: -
""" 示例二 """
match('\s', ['1', 'a', '_', ' '])
# 匹配失敗!
# 匹配失敗!
# 匹配失敗!
# 匹配成功!結果為: 
match('\S', ['1', 'a', '_', ' '])
# 匹配成功!結果為: 1
# 匹配成功!結果為: a
# 匹配成功!結果為: _
# 匹配失敗!
""" 示例三 """
match('\w', ['1', 'a', '_', ' ', ']'])
# 匹配成功!結果為: 1
# 匹配成功!結果為: a
# 匹配成功!結果為: _
# 匹配失敗!
# 匹配失敗!
match('\W', ['1', 'a', '_', ' ', ']'])
# 匹配失敗!
# 匹配失敗!
# 匹配失敗!
# 匹配成功!結果為: 
# 匹配成功!結果為: ]

1.3 一些例子

接下來我們通過一些例子來進一步鞏固之前所學的概念。

1.3.1 十六進制RGB顏色值匹配

十六進制的顏色值的格式通常為 #XXXXXX,其中 X 的取值可以為數字,也可以為 A-F 中的任意字符(假設這裡不考慮小寫情形)。

regex = '#[A-F0-9]{6}$'
colors = ['#00', '#FFFFFF', '#FFAAFF', '#00HH00', '#AABBCC', '#000000', '#FFFFFFFF']
match(regex, colors)
# 匹配失敗!
# 匹配成功!結果為: #FFFFFF
# 匹配成功!結果為: #FFAAFF
# 匹配失敗!
# 匹配成功!結果為: #AABBCC
# 匹配成功!結果為: #000000
# 匹配失敗!

1.3.2 1-100之間的所有整數匹配

我們不考慮前綴0的情形,例如對於數字 835,諸如 08008035 這樣的形式是排除在外的。

只需分別考慮三位數、兩位數、一位數的情形:

regex = '(100|[1-9]\d|[1-9])$'
numbers = ['0', '5', '05', '005', '12', '012', '89', '100', '101']
match(regex, numbers)
# 匹配失敗!
# 匹配成功!結果為: 5
# 匹配失敗!
# 匹配失敗!
# 匹配成功!結果為: 12
# 匹配失敗!
# 匹配成功!結果為: 89
# 匹配成功!結果為: 100
# 匹配失敗!

1.3.3 郵箱格式匹配

這裡我們自創一個郵箱,假設域名為 sky.com。在創建新用戶時,要求用戶名只能由數字、字母及下劃線組成,且不能以下劃線開頭,郵箱名長度在6-18位。我們該如何用正則表達式來判斷用戶輸入的郵箱是否符合規范呢?

regex = '[a-zA-Z0-9][\w]{5,17}@sky\.com$'
emails = [
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
]
match(regex, emails)
# 匹配失敗!
# 匹配失敗!
# 匹配失敗!
# 匹配失敗!
# 匹配成功!結果為: [email protected]
# 匹配失敗!
# 匹配失敗!

如果 sky 郵箱允許用戶開通vip,開通後郵箱域名變為了 vip.sky.com,且普通用戶和vip用戶均屬於 sky 郵箱用戶。該情形下的正則表達式需要改寫為:

regex = '[a-zA-Z0-9][\w]{5,17}@(vip\.)?sky\.com$'
emails = [
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
]
match(regex, emails)
# 匹配成功!結果為: [email protected]
# 匹配成功!結果為: [email protected]
# 匹配失敗!
# 匹配失敗!

1.3.4 IPV4地址匹配

IPV4的格式通常為 X.X.X.X,其中 X 的范圍為 0-255。這裡依然不考慮前綴0的情形。

regex = '((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$'
ipv4s = [
'0.0.0.0',
'0.0.255.0',
'255.0.0',
'127.0.0.1.0',
'256.0.0.123',
'255.255.255.255',
'012.08.0.0',
]
match(regex, ipv4s)
# 匹配成功!結果為: 0.0.0.0
# 匹配成功!結果為: 0.0.255.0
# 匹配失敗!
# 匹配失敗!
# 匹配失敗!
# 匹配成功!結果為: 255.255.255.255
# 匹配失敗!

1.4 修飾符

修飾符即 re.match 函數中的 flags 參數,常用的修飾符列在下表中:

修飾符作用re.I匹配時忽略大小寫re.S使得 . 能夠匹配任意單個字符(包括換行符 \n
s = re.match('a', 'A', flags=re.I)
print(s.group())
# A
s = re.match('.', '\n', flags=re.S)
print(s)
# <re.Match object; span=(0, 1), match='\n'>

二、其他函數

2.1 re.fullmatch

re.match 是從字符串的起始位置開始匹配,即只要匹配成功,則無需管字符串的剩余位置。而 re.fullmatch 則是匹配整個字符串。

格式如下:

re.fullmatch(pattern, string, flags=0)

例如:

print(re.match('\d', '3a'))
# <re.Match object; span=(0, 1), match='3'>
print(re.fullmatch('\d', '3a'))
# None

2.2 re.search

re.search 從左到右掃描整個字符串並返回第一個成功的匹配。如果匹配失敗,則返回 None

格式如下:

re.search(pattern, string, flags=0)

例如:

print(re.search('ab', 'abcd'))
# <re.Match object; span=(0, 2), match='ab'>
print(re.search('cd', 'abcd'))
# <re.Match object; span=(2, 4), match='cd'>

2.3 re.findall

re.findall 是在給定的字符串中找到所有匹配正則表達式的子串,並以列表的形式返回,格式如下:

re.findall(pattern, string, flags=0)

例如:

res = re.findall('\d+', 'ab 13 cd- 274 .]')
print(res)
# ['13', '274']
res = re.findall('(\w+):(\d+)', 'Xiaoming:16, Xiaohong:14')
print(res)
# [('Xiaoming', '16'), ('Xiaohong', '14')]

2.4 re.sub

re.sub 用於將匹配到的子串替換為另一個子串,執行時從左向右進行替換。格式如下:

re.sub(pattern, repl, string, count=0, flags=0)

repl 代表替換後的字符串,count 是最大替換次數,0表示替換所有。

例如:

res = re.sub(' ', '', '1 2 3 4 5')
print(res)
# 12345
res = re.sub(' ', '', '1 2 3 4 5', count=2)
print(res)
# 123 4 5

2.5 re.split

re.split 將根據匹配切割字符串(從左向右),並返回一個列表。格式如下:

re.split(pattern, string, maxsplit=0, flags=0)

count 是最大切割次數,0表示切割所有位置。

例如:

res = re.split(' ', '1 2 3 4 5')
print(res)
# ['1', '2', '3', '4', '5']
res = re.split(' ', '1 2 3 4 5', maxsplit=2)
print(res)
# ['1', '2', '3 4 5']

三、貪婪匹配與非貪婪匹配

所有的量詞*+?{m}{m,}{,n}{m,n}默認采取貪婪匹配的原則,即在匹配成功的情況下盡可能多地匹配。

* 為例,顯然 \d* 是匹配任意長度的數字:

match('\d*', ['1234abc'])
# 匹配成功!結果為: 1234

根據貪婪原則,最終匹配結果一定是 1234

有些時候,我們不想盡可能多地匹配,而是盡可能少地匹配。這時候我們可以在量詞後面加上 ?,它表示非貪婪匹配:

match('\d*?', ['1234abc'])
# 匹配成功!結果為: 

在非貪婪模式下,\d 應該出現0次(盡可能少匹配),於是最終返回空字符。

我們再來看一下其他量詞的情況:

match('\d+?', ['1234abc'])
# 匹配成功!結果為: 1
match('\d??', ['1234abc'])
# 匹配成功!結果為: 
match('\d{2,}?', ['1234abc'])
# 匹配成功!結果為: 12
match('\d{2,5}?', ['1234abc'])
# 匹配成功!結果為: 12
match('\d{,5}?', ['1234abc'])
# 匹配成功!結果為: 

四、推薦網站

regex101 可用於練習正則表達式。


  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式師世界 All Rights Reserved