Python 正規表示式

firefule發表於2021-09-09

簡介

正規表示式(regular expression)是可以匹配文字片段的模式。最簡單的正規表示式就是普通字串,可以匹配其自身。比如,正規表示式 ‘hello’ 可以匹配字串 ‘hello’。

要注意的是,正規表示式並不是一個程式,而是用於處理字串的一種模式,如果你想用它來處理字串,就必須使用支援正規表示式的工具,比如 Linux 中的 awk, sed, grep,或者程式語言 Perl, Python, Java 等等。

正規表示式有多種不同的風格,下表列出了適用於 Python 或 Perl 等程式語言的部分元字元以及說明:

Python 正規表示式

re 模組

在 Python 中,我們可以使用內建的 re 模組來使用正規表示式。

有一點需要特別注意的是,正規表示式使用 對特殊字元進行轉義,比如,為了匹配字串 'python.org',我們需要使用正規表示式 'python.org',而 Python 的字串本身也用 轉義,所以上面的正規表示式在 Python 中應該寫成 'python\.org',這會很容易陷入 的困擾中,因此,我們建議使用 Python 的原始字串,只需加一個 r 字首,上面的正規表示式可以寫成:

re 模組提供了不少有用的函式,用以匹配字串,比如:

  • compile 函式
  • match 函式
  • search 函式
  • findall 函式
  • finditer 函式
  • split 函式
  • sub 函式
  • subn 函式

re 模組的一般使用步驟如下:

  • 使用 compile 函式將正規表示式的字串形式編譯為一個 Pattern 物件
  • 通過 Pattern 物件提供的一系列方法對文字進行匹配查詢,獲得匹配結果(一個 Match 物件)
  • 最後使用 Match 物件提供的屬性和方法獲得資訊,根據需要進行其他的操作

compile 函式

compile 函式用於編譯正規表示式,生成一個 Pattern 物件,它的一般使用形式如下:

其中,pattern 是一個字串形式的正規表示式,flag 是一個可選引數,表示匹配模式,比如忽略大小寫,多行模式等。

下面,讓我們看看例子。

在上面,我們已將一個正規表示式編譯成 Pattern 物件,接下來,我們就可以利用 pattern 的一系列方法對文字進行匹配查詢了。Pattern 物件的一些常用方法主要有:

  • match 方法
  • search 方法
  • findall 方法
  • finditer 方法
  • split 方法
  • sub 方法
  • subn 方法

match 方法

match 方法用於查詢字串的頭部(也可以指定起始位置),它是一次匹配,只要找到了一個匹配的結果就返回,而不是查詢所有匹配的結果。它的一般使用形式如下:

其中,string 是待匹配的字串,pos 和 endpos 是可選引數,指定字串的起始和終點位置,預設值分別是 0 和 len (字串長度)。因此,當你不指定 pos 和 endpos 時,match 方法預設匹配字串的頭部

當匹配成功時,返回一個 Match 物件,如果沒有匹配上,則返回 None。

看看例子。

在上面,當匹配成功時返回一個 Match 物件,其中:

  • group([group1, …]) 方法用於獲得一個或多個分組匹配的字串,當要獲得整個匹配的子串時,可直接使用 group()group(0)
  • start([group]) 方法用於獲取分組匹配的子串在整個字串中的起始位置(子串第一個字元的索引),引數預設值為 0;
  • end([group]) 方法用於獲取分組匹配的子串在整個字串中的結束位置(子串最後一個字元的索引+1),引數預設值為 0;
  • span([group]) 方法返回 (start(group), end(group))

再看看一個例子:

search 方法

search 方法用於查詢字串的任何位置,它也是一次匹配,只要找到了一個匹配的結果就返回,而不是查詢所有匹配的結果,它的一般使用形式如下:

其中,string 是待匹配的字串,pos 和 endpos 是可選引數,指定字串的起始和終點位置,預設值分別是 0 和 len (字串長度)。

當匹配成功時,返回一個 Match 物件,如果沒有匹配上,則返回 None。

讓我們看看例子:

再來看一個例子:

執行結果:

findall 方法

上面的 match 和 search 方法都是一次匹配,只要找到了一個匹配的結果就返回。然而,在大多數時候,我們需要搜尋整個字串,獲得所有匹配的結果。

findall 方法的使用形式如下:

其中,string 是待匹配的字串,pos 和 endpos 是可選引數,指定字串的起始和終點位置,預設值分別是 0 和 len (字串長度)。

findall 以列表形式返回全部能匹配的子串,如果沒有匹配,則返回一個空列表。

看看例子:

執行結果:

finditer 方法

finditer 方法的行為跟 findall 的行為類似,也是搜尋整個字串,獲得所有匹配的結果。但它返回一個順序訪問每一個匹配結果(Match 物件)的迭代器。

看看例子:

執行結果:

split 方法

split 方法按照能夠匹配的子串將字串分割後返回列表,它的使用形式如下:

其中,maxsplit 用於指定最大分割次數,不指定將全部分割。

看看例子:

執行結果:

sub 方法

sub 方法用於替換。它的使用形式如下:

其中,repl 可以是字串也可以是一個函式:

  • 如果 repl 是字串,則會使用 repl 去替換字串每一個匹配的子串,並返回替換後的字串,另外,repl 還可以使用 id 的形式來引用分組,但不能使用編號 0;
  • 如果 repl 是函式,這個方法應當只接受一個引數(Match 物件),並返回一個字串用於替換(返回的字串中不能再引用分組)。

count 用於指定最多替換次數,不指定時全部替換。

看看例子:

執行結果:

subn 方法

subn 方法跟 sub 方法的行為類似,也用於替換。它的使用形式如下:

它返回一個元組:

元組有兩個元素,第一個元素是使用 sub 方法的結果,第二個元素返回原字串被替換的次數。

看看例子:

執行結果:

其他函式

事實上,使用 compile 函式生成的 Pattern 物件的一系列方法跟 re 模組的多數函式是對應的,但在使用上有細微差別。

match 函式

match 函式的使用形式如下:

其中,pattern 是正規表示式的字串形式,比如 d+, [a-z]+

而 Pattern 物件的 match 方法使用形式是:

可以看到,match 函式不能指定字串的區間,它只能搜尋頭部,看看例子:

執行結果:

search 函式

search 函式的使用形式如下:

search 函式不能指定字串的搜尋區間,用法跟 Pattern 物件的 search 方法類似。

findall 函式

findall 函式的使用形式如下:

findall 函式不能指定字串的搜尋區間,用法跟 Pattern 物件的 findall 方法類似。

看看例子:

finditer 函式

finditer 函式的使用方法跟 Pattern 的 finditer 方法類似,形式如下:

split 函式

split 函式的使用形式如下:

sub 函式

sub 函式的使用形式如下:

subn 函式

subn 函式的使用形式如下:

到底用哪種方式

從上文可以看到,使用 re 模組有兩種方式:

  • 使用 re.compile 函式生成一個 Pattern 物件,然後使用 Pattern 物件的一系列方法對文字進行匹配查詢;
  • 直接使用 re.match, re.search 和 re.findall 等函式直接對文字匹配查詢;

下面,我們用一個例子展示這兩種方法。

先看第 1 種用法:

再看第 2 種用法:

如果一個正規表示式需要用到多次(比如上面的 d+),在多種場合經常需要被用到,出於效率的考慮,我們應該預先編譯該正規表示式,生成一個 Pattern 物件,再使用該物件的一系列方法對需要匹配的檔案進行匹配;而如果直接使用 re.match, re.search 等函式,每次傳入一個正規表示式,它都會被編譯一次,效率就會大打折扣。

因此,我們推薦使用第 1 種用法。

匹配中文

在某些情況下,我們想匹配文字中的漢字,有一點需要注意的是,中文的 unicode 編碼範圍 主要在 [u4e00-u9fa5],這裡說主要是因為這個範圍並不完整,比如沒有包括全形(中文)標點,不過,在大部分情況下,應該是夠用的。

假設現在想把字串 title = u'你好,hello,世界' 中的中文提取出來,可以這麼做:

注意到,我們在正規表示式前面加上了兩個字首 ur,其中 r 表示使用原始字串,u 表示是 unicode 字串。

執行結果:

貪婪匹配

在 Python 中,正則匹配預設是貪婪匹配(在少數語言中可能是非貪婪),也就是匹配儘可能多的字元

比如,我們想找出字串中的所有 div 塊:

執行結果:

 

由於正則匹配是貪婪匹配,也就是儘可能多的匹配,因此,在成功匹配到第一個

時,它還會向右嘗試匹配,檢視是否還有更長的可以成功匹配的子串。

如果我們想非貪婪匹配,可以加一個 ?,如下:

結果:

小結

  • re 模組的一般使用步驟如下:
    • 使用 compile 函式將正規表示式的字串形式編譯為一個 Pattern 物件;
    • 通過 Pattern 物件提供的一系列方法對文字進行匹配查詢,獲得匹配結果(一個 Match 物件);
    • 最後使用 Match 物件提供的屬性和方法獲得資訊,根據需要進行其他的操作;
  • Python 的正則匹配預設是貪婪匹配。

參考資料

相關文章