Python正則表達式用法總結

2020-08-13 14:06:23

寫在前面

記錄一下前階段學的正則表達式(Regular Expression,一稱RegExp、regex、RE),算是個複習+總結。我主要是從Python的Re庫開始學正則的,不過正則的一些用法大同小異,學了一種就差不多明白了。想深入瞭解RE的話可以看下面 下麪的參考資料,寫得很詳細。

P.S. 前面幾項都可以通用,只不過在Python中有內建的Re正則表達式庫,裏面的函數跟其他語言不太一樣。

測試環境

Windows10 x64

Python 3.6.8

特殊字元

匹配單字元的字元

列出常用的單字元匹配以及對應表達的註記,感覺程式設計的東西對應英文會記得更牢些。

字元 匹配項 註記
· 匹配任意1個字元(除特殊字元,如\n) -
\d 匹配0-9中的任一數位字元,等價於[0-9] digit,單個數字
\D 匹配任一非數位字元,等價於[^0-9] 大寫取反
\s 匹配任一空白字元,如空格、製表符(Tab)、換行符(\n space,空格
\S 匹配任一非空白字元 -
\w 匹配任一單詞字元(可理解爲變數命名規定的字元)
a-zA-Z0-9、下劃線_,等價於[a-zA-z0-9_]
word,單詞
\W 匹配任一非單詞字元,等價於[^a-zA-z0-9_] -
[] 匹配方括號中列舉出的任一字元 -
[^ ] 匹配除了括號內列舉以外的所有字元 -

表示匹配次數的字元

字元 含義
{n} 匹配字元恰好出現n次
{n,} 匹配字元至少出現n次
{n,m} 匹配字元出現n到m
* 匹配字元出現0次或無限次(即字元出現任意次,.*表示匹配任意長度且除\n的字串)
+ 匹配字元出現1次或無限次(即字元出現至少一次)
? 匹配0次或1次的字元(多用於非貪婪模式)

定界字元

字元 用法
\b 匹配一個單詞字串的邊界(boundary),即單詞和空格間的位置
\B 匹配非單詞邊界,即若某字串出現的單詞字串未以空格分割,則不能匹配
^ 匹配字串的第一個字元(首字元)
$ 匹配字串的最後一個字元(末尾字元)

其他常用字元

字元 用法
r" " Python中置於字串之前用於返回原始(raw)字串,在正則表達式編寫中常用
\ 用於*?等特殊字元的跳脫
\n,\t 匹配換行符、製表符,也可由\s進行匹配

Re庫常用函數(方法)

# 匯入內建庫`re`
import re

re.compile()編譯正則表達式

compile(pattern, flags=0)

用於生成一個正則表達式物件,方便正則表達式的遷移,供re.match()re.search()使用。

re.match()匹配字串

match(pattern, string, flags=0)

用於匹配以某字串開頭的字串。通常與.group()聯合使用,用於返回匹配到的字串,若匹配爲空則報錯:AttributeError: 'NoneType' object has no attribute 'group'

  • .group()返回字串

預設參數爲0,表示返回所有匹配到的字串;

參數爲1表示返回()分組的第一個字串,以此類推。

re.search()re.findall()查詢字串

search(pattern, string, flags=0)

findall(pattern, string, flags=0)

單個查詢與全域性查詢。search()match()的區別在於前者進行全域性的查詢,並返回首個匹配的結果,而後者僅進行從首個字元開始的查詢匹配。

re.sub()替換字串

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

按匹配規則進行部分(或全部)字串的替換,第二個參數(被替換成的字元)可以爲函數。

re.split()分割字串

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

按匹配規則分割字串,並返回一個列表。

貪婪模式與非貪婪模式

這塊內容比較少,主要就是"?"的使用,在設定字元匹配次數的時候應儘量使用非貪婪模式,即在*+?的後面再加上一個?,構成*?+???,這樣可以只匹配符合條件的最少字元,而不至於增加不必要的字元或是漏掉該匹配的字元。

{n,m}後面也可以加上?進入非貪婪模式。

\bigstar括號分組

分組是Python正則表達式的常用方法,不過要注意分組間的對應關係。

分組字元

字 符 用法與備註
| 分隔兩側的正則表達式
() 將括號內的模式(字元)作爲一個分組
\1-\9 參照分組\i(i=1,2,,9)(i=1,\,2,\,\cdots,\,9)匹配到的字串
(?P<name>) 分組起別名(name),注意P要大寫,參照時亦然
(?P=name) 參照別名爲name的分組匹配到的字串,多用於匹配成對的HTML標籤
(?<=abc) 肯定性回顧斷言,即若括號內容匹配,則.group()返回括號後面匹配到的字元
(?<!abc) 否定性回顧斷言,即若括號內容不匹配,則.group()返回括號後面匹配到的字元
(?=abc) 肯定性前瞻斷言,即若括號內容匹配,則.group()返回括號前面匹配到的字元
(?!abc) 否定性前瞻斷言,即若括號內容不匹配,則.group()返回括號前面匹配到的字元

注意: 上面的後四個匹配斷言僅用於.search().findall(),不可用於.match().

範例

In [1]: import re
    
In [2]: re.search(r"(?<=abc)\d+", r"abc123").group()
Out[2]: '123'
    
In [3]: re.search(r"(?<!abc)\d+", r"bca123").group()
Out[3]: '123'
    
In [4]: re.search(r"\d+(?=abc)", r"123abc").group()
Out[4]: '123'
    
In [5]: re.search(r"\d+(?!abc)", r"123bca").group()
Out[5]: '123'
    
# 設定分組別名後可以以字典形式展示匹配到的分組

In [6]: re.match(r"(?P<group1>[a-z]+)(?P<group2>\d+)", r"bca123").groupdict() 
Out[6]: {'group1': 'bca', 'group2': '123'}
    
In [7]: re.match(r"(?P<group1>[a-z]+)(?P<group2>\d+)", r"bca123").group("group1")
Out[7]: 'bca'
    
In [8]: re.match(r"<(?P<group1>[a-z]*?)>(.*?)</(?P=group1)>", r"<div>你好世界!</div>").group()
Out[8]: '<div>你好世界!</div>'

In [9]: re.match(r"<(?P<group1>[a-z]*?)>(.*?)</(?P=group1)>", r"<div>你好世界!</div>").group(2)
Out[9]: '你好世界!'
    
# 使用"反斜槓+數位"進行分組的參照更爲方便

In [10]: re.match(r"<([a-z]*?)>(.*?)</\1>", r"<div>你好世界!</div>").group()
Out[10]: '<div>你好世界!</div>'

In [11]: re.match(r"<([a-z]*?)>(.*?)</\1>", r"<div>你好世界!</div>").group(2)
Out[11]: '你好世界!'

一些技巧

正難則反

需要匹配的字串規則不好找時候可以選擇容易找到規則的其他字元,再進行分組處理得到需要的字元。

例如,網址「https://docs.python.org/3/library/re.html」需要替換成「https://docs.python.org/」,在進行匹配的時候由於待替換的部分其規律無法找到,所以採取如下方法進行替換:

In [1]: import re

In [2]: s = r"https://docs.python.org/3/library/re.html"

In [3]: re.sub(r"(https://.+?/).+", lambda x:x.group(1), s)
Out[3]: 'https://docs.python.org/'

函數活用

有時候解決同一個問題可以有多種思路,例如提取單詞,可以採用空格分割或者全域性查詢兩種方法,所得到的結果相同。

In [4]: s = r"I have a dream"

In [5]: re.split(r" ", s)
Out[5]: ['I', 'have', 'a', 'dream']

In [6]: re.findall(r"\b[a-zA-z]+?\b", s)
Out[6]: ['I', 'have', 'a', 'dream']

主要參考

[1] re — Regular expression operations.

[2] 菜鳥教學:Python 正則表達式.