Python正則表達式,看這一篇就夠了

Python正則表達式,看這一篇就夠了
Python正則表達式,看這一篇就夠了

作者 | 豬哥

來源 | 裸睡的豬(ID: IT--Pig)

大多數編程語言的正則表達式設計都師從Perl,所以語法基本相似,不同的是每種語言都有自己的函數去支持正則,今天我們就來學習 Python中關於 正則表達式的函數。

Python正則表達式,看這一篇就夠了

re模塊主要定義了9個常量、12個函數、1個異常,每個常量和函數豬哥都會通過實際代碼案例講解,讓大家能更直觀的瞭解其作用!

注:為避免出現代碼格式錯亂,豬哥儘量使用代碼截圖演示哦。

Python正則表達式,看這一篇就夠了

re模塊簡介

聊到Python正則表達式的支持,首先肯定會想到<code>re/<code>庫,這是一個Python處理文本的標準庫

標準庫的意思表示這是一個Python內置模塊,不需要額外下載,目前Python內置模塊大概有300個。可以在這裡查看Python所有的內置模塊:
https://docs.python.org/3/py-modindex.html#cap-r

因為re是內置模塊,所以不需要再下載,使用時直接引入即可:

<code>import re/<code>

re模塊官方文檔:
https://docs.python.org/zh-cn/3.8/library/re.html

re模塊庫源碼:
https://github.com/python/cpython/blob/3.8/Lib/re.py

Python正則表達式,看這一篇就夠了

re模塊常量

常量即表示不可更改的變量,一般用於做標記。

re模塊中有9個常量,常量的值都是int類型!

Python正則表達式,看這一篇就夠了

上圖我們可以看到,所有的常量都是在RegexFlag枚舉類來實現,這是在Python 3.6做的改版。在Python 3.6以前版本是直接將常量寫在re.py中,使用枚舉的好處就是方便管理和使用!

Python正則表達式,看這一篇就夠了

下面我們來快速學習這些常量的作用及如何使用他們,按常用度排序!

1. IGNORECASE

語法:re.IGNORECASE 或簡寫為 re.I

作用:進行忽略大小寫匹配。

代碼案例:

Python正則表達式,看這一篇就夠了

在默認匹配模式下大寫字母B無法匹配小寫字母b,而在 忽略大小寫 模式下是可以的。

2. ASCII

語法:re.ASCII 或簡寫為 re.A

作用:顧名思義,ASCII表示ASCII碼的意思,讓<code>\w/<code>,<code>\W/<code>,<code>\b/<code>,<code>\B/<code>,<code>\d/<code>,<code>\D/<code>,<code>\s/<code>和<code>\S/<code>只匹配ASCII,而不是Unicode。

代碼案例:

Python正則表達式,看這一篇就夠了

在默認匹配模式下<code>\w+/<code>匹配到了所有字符串,而在ASCII模式下,只匹配到了a、b、c(ASCII編碼支持的字符)。

注意:這隻對字符串匹配模式有效,對字節匹配模式無效。

3. DOTALL

語法:re.DOTALL 或簡寫為 re.S

作用:DOT表示<code>./<code>,ALL表示所有,連起來就是<code>./<code>匹配所有,包括換行符<code>\n/<code>。默認模式下<code>./<code>是不能匹配行符<code>\n/<code>的。

代碼案例:

Python正則表達式,看這一篇就夠了

在默認匹配模式下<code>./<code>並沒有匹配換行符<code>\n/<code>,而是將字符串分開匹配;而在re.DOTALL模式下,換行符<code>\n/<code>與字符串一起被匹配到。

注意:默認匹配模式下<code>./<code>並不會匹配換行符<code>\n/<code>。

4. MULTILINE

語法:re.MULTILINE 或簡寫為 re.M

作用:多行模式,當某字符串中有換行符<code>\n/<code>,默認模式下是不支持換行符特性的,比如:行開頭 和 行結尾,而多行模式下是支持匹配行開頭的。

代碼案例:

Python正則表達式,看這一篇就夠了

正則表達式中<code>^/<code>表示匹配行的開頭,默認模式下它只能匹配字符串的開頭;而在多行模式下,它還可以匹配 換行符<code>\n/<code>後面的字符。

注意:正則語法中<code>^/<code>匹配行開頭、<code>\A/<code>匹配字符串開頭,單行模式下它兩效果一致,多行模式下<code>\A/<code>不能識別<code>\n/<code>。

5. VERBOSE

語法:re.VERBOSE 或簡寫為 re.X

作用:詳細模式,可以在正則表達式中加註解!

代碼案例:

Python正則表達式,看這一篇就夠了

默認模式下並不能識別正則表達式中的註釋,而詳細模式是可以識別的。

當一個正則表達式十分複雜的時候,詳細模式或許能為你提供另一種註釋方式,但它不應該成為炫技的手段,建議謹慎考慮後使用!

6.LOCALE

語法:re.LOCALE 或簡寫為 re.L

作用:由當前語言區域決定<code>\w/<code>,<code>\W/<code>,<code>\b/<code>,<code>\B/<code>和大小寫敏感匹配,這個標記只能對byte樣式有效。這個標記官方已經不推薦使用,因為語言區域機制很不可靠,它一次只能處理一個 “習慣”,而且只對8位字節有效。

注意:由於這個標記官方已經不推薦使用,而且豬哥也沒使用過,所以就不給出實際的案例!

7.UNICODE

語法:

re.UNICODE 或簡寫為 re.U

作用:與 ASCII 模式類似,匹配unicode編碼支持的字符,但是 Python 3 默認字符串已經是Unicode,所以有點冗餘。

8. DEBUG

語法:re.DEBUG

作用:顯示編譯時的debug信息。

代碼案例:

Python正則表達式,看這一篇就夠了

雖然debug模式下確實會打印編譯信息,但豬哥並不理解這是什麼語言 以及表達的含義,希望瞭解的朋友能不吝賜教。

9.TEMPLATE

語法:re.TEMPLATE 或簡寫為 re.T

作用:豬哥也沒搞懂TEMPLATE的具體用處,源碼註釋中寫著:disable backtracking(禁用回溯),有了解的同學可以留言告知!

Python正則表達式,看這一篇就夠了

10. 常量總結

9個常量中,前5個(IGNORECASE、ASCII、DOTALL、MULTILINE、VERBOSE)有用處,兩個(LOCALE、UNICODE)官方不建議使用、兩個(TEMPLATE、DEBUG)試驗性功能,不能依賴。

常量在re常用函數中都可以使用,查看源碼可得知。

Python正則表達式,看這一篇就夠了

常量可疊加使用,因為常量值都是2的冪次方值,所以是可以疊加使用的,疊加時請使用 <code>|/<code>符號,請勿使用<code>+/<code>符號!

Python正則表達式,看這一篇就夠了

最後來一張思維導圖總結一下re模塊中的常量吧,需要高清圖或者xmind文件的同學可在文章末尾獲取。

Python正則表達式,看這一篇就夠了Python正則表達式,看這一篇就夠了

re模塊函數

re模塊有12個函數,豬哥將以功能分類來講解;這樣更具有比較性,同時也方便記憶。

1.查找一個匹配項

查找並返回一個匹配項的函數有3個:search、match、fullmatch,他們的區別分別是:

  1. search: 查找任意位置的匹配項

  2. match: 必須從字符串開頭匹配

  3. fullmatch: 整個字符串與正則完全匹配

我們再來根據實際的代碼案例比較:

案例1:

Python正則表達式,看這一篇就夠了

案例1中search函數是在字符串中任意位置匹配,只要有符合正則表達式的字符串就匹配成功,其實有兩個匹配項,但search函數值返回一個。

match函數是要從頭開始匹配,而字符串開頭多了個字母<code>a/<code>,所以無法匹配,fullmatch函數需要完全相同,故也不匹配!

案例2:

Python正則表達式,看這一篇就夠了

案例2刪除了text最開頭的字母a,這樣match函數就可以匹配啦,而fullmatch函數依然不能完全匹配!

案例3:

Python正則表達式,看這一篇就夠了

案例3中,我們只留下一段文字,並且與正則表達式一致;這時fullmatch函數終於可以匹配了。

完整案例:

Python正則表達式,看這一篇就夠了

注意:查找 一個匹配項 返回的都是一個匹配對象(Match)。

2.查找多個匹配項

講完查找一項,現在來看看查找多項吧,查找多項函數主要有:findall函數finditer函數

  1. findall: 從字符串任意位置查找,
    返回一個列表
  2. finditer:從字符串任意位置查找,返回一個迭代器

兩個方法基本類似,只不過一個是返回列表,一個是返回迭代器。我們知道列表是一次性生成在內存中,而迭代器是需要使用時一點一點生成出來的,內存使用更優。

Python正則表達式,看這一篇就夠了

如果可能存在大量的匹配項的話,建議使用

finditer函數,一般情況使用findall函數基本沒啥影響。

3.分割

re.split(pattern, string, maxsplit=0, flags=0)函數:用pattern分開 string ,maxsplit表示最多進行分割次數,flags表示模式,就是上面我們講解的常量!

Python正則表達式,看這一篇就夠了

注意:str模塊也有一個 split函數 ,那這兩個函數該怎麼選呢?str.split函數功能簡單,不支持正則分割,而re.split支持正則。

關於二者的速度如何? 豬哥實際測試了一下,在相同數據量的情況下使用<code>re.split/<code>函數與<code>str.split/<code>函數執行次數執行時間對比圖:

Python正則表達式,看這一篇就夠了

通過上圖對比發現,1000次循環以內<code>str.split/<code>函數更快,而循環次數1000次以上後<code>re.split/<code>函數明顯更快,而且次數越多差距越大!

所以結論是:在 不需要正則支持 且 數據量和數次不多 的情況下使用<code>str.split/<code>函數更合適,反之則使用<code>re.split/<code>函數

注:具體執行時間與測試數據有關!

4.替換

替換主要有sub函數subn函數,他們功能類似!

先來看看sub函數的用法:

re.sub(pattern, repl, string, count=0, flags=0)函數參數講解:repl替換掉string中被pattern匹配的字符, count表示最大替換次數,flags表示正則表達式的常量。

值得注意的是:sub函數中的入參:repl替換內容既可以是字符串,也可以是一個函數哦!如果repl為函數時,只能有一個入參:Match匹配對象。

Python正則表達式,看這一篇就夠了

re.subn(pattern, repl, string, count=0, flags=0)函數與re.sub函數功能一致,只不過返回一個元組 (字符串, 替換次數)。

Python正則表達式,看這一篇就夠了

5.編譯正則對象

compile函數 與 template函數將正則表達式的樣式編譯為一個 正則表達式對象 (正則對象Pattern),這個對象與re模塊有同樣的正則函數(後面我們會講解Pattern正則對象)。

Python正則表達式,看這一篇就夠了

template函數compile函數類似,只不過是增加了我們之前說的re.TEMPLATE

模式,我們可以看看源碼。

Python正則表達式,看這一篇就夠了

6.其他

re.escape(pattern) 可以轉義正則表達式中具有特殊含義的字符,比如:<code>./<code>或者<code>*/<code>,舉個實際的案例:

Python正則表達式,看這一篇就夠了

re.escape(pattern)看似非常好用省去了我們自己加轉義,但是使用它很容易出現轉義錯誤的問題,所以並不建議使用它轉義,而建議大家自己手動轉義!

re.purge 函數作用就是清除 正則表達式緩存,具體有什麼緩存呢?我們來看看源碼就知道它背地裡幹了 什麼:

Python正則表達式,看這一篇就夠了

看方法大概是清除緩存吧,我們再來看看具體的案例:

Python正則表達式,看這一篇就夠了

豬哥在兩個案例之間使用了re.purge函數清除緩存,然後分別比較前後案例源碼裡面的緩存,看看是否有變化!

Python正則表達式,看這一篇就夠了

7.總結

同樣,最後來一張思維導圖總結一下re模塊中的函數吧,需要高清圖或者xmind文件的同學可在末尾獲取。

Python正則表達式,看這一篇就夠了
Python正則表達式,看這一篇就夠了

re模塊異常

re模塊還包含了一個正則表達式的編譯錯誤,當我們給出的正則表達式是一個無效的表達式(就是表達式本身有問題)時,就會raise一個異常!

我們來看看具體的案例吧:

Python正則表達式,看這一篇就夠了

上圖案例中我們可以看到,在編寫正則表達式中我們多寫了一個後括號,這導致執行結果報錯;而且是在其他所有案例執行之前,所以說明是在正則表達式編譯時期就報錯了。

注意:這個異常一定是 正則表達式 本身是無效的,與要匹配的字符串無關!

Python正則表達式,看這一篇就夠了

正則對象Pattern

關於<code>re/<code>模塊的常量、函數、異常我們都講解完畢,但是完全有必要再講講

正則對象Pattern

1. 與re模塊 函數一致

在<code>re/<code>模塊的函數中有一個重要的函數compile函數,這個函數可以預編譯返回一個正則對象,此正則對象擁有與<code>re/<code>模塊相同的函數,我們來看看Pattern類的源碼。

Python正則表達式,看這一篇就夠了

既然是一致的,那到底該用re模塊

還是正則對象Pattern

而且,有些同學可能看過<code>re/<code>模塊的源碼,你會發現其實compile函數與 其他re函數(search、split、sub等等) 內部調用的是同一個函數,最終還是調用正則對象的函數!

Python正則表達式,看這一篇就夠了

也就是說下面 兩種代碼寫法底層實現 其實是一致的:

 

<code># re函數re.search(pattern, text)# 正則對象函數compile = re.compile(pattern)compile.search(text)/<code>

那還有必要使用compile函數得到正則對象再去調用search函數嗎?直接調用re.search 是不是就可以?

2. 官方文檔怎麼說

關於到底該用re模塊還是正則對象Pattern,官方文檔是否有說明呢?

Python正則表達式,看這一篇就夠了

官方文檔推薦:在多次使用某個正則表達式時推薦使用正則對象Pattern以增加複用性,因為通過re.compile(pattern)編譯後的模塊級函數會被緩存!

3. 實際測試又如何?

上面官方文檔推薦我們在 多次使用某個正則表達式時使用正則對象,那實際情況真的是這樣的嗎?

我們再實測一下吧

Python正則表達式,看這一篇就夠了

豬哥編寫了兩個函數,一個使用re.search函數另一個使用compile.search函數,分別(不同時)循環執行count次(count從1-1萬),比較兩者的耗時!

得出的結果豬哥繪製成折線圖:

Python正則表達式,看這一篇就夠了

得出的結論是:100次循環以內兩者的速度基本一致,當超出100次後,使用 正則對象Pattern的函數 耗時明顯更短,所以比

re模塊要快!

通過實際測試得知:Python 官方文檔推薦 多次使用某個正則表達式時使用正則對象函數 基本屬實!

Python正則表達式,看這一篇就夠了

注意事項

Python 正則表達式知識基本講解完畢,最後稍微給大家提一提需要注意的點。

1.字節串 與 字符串

模式和被搜索的字符串既可以是 Unicode 字符串 (str) ,也可以是8位字節串 (bytes)。但是,Unicode 字符串與8位字節串不能混用!

2.r 的作用

正則表達式使用反斜槓(’\’)來表示特殊形式,或者把特殊字符轉義成普通字符。

而反斜槓在普通的 Python 字符串裡也有相同的作用,所以就產生了衝突。

解決辦法是對於正則表達式樣式使用 Python 的原始字符串表示法;在帶有 ‘r’ 前綴的字符串字面值中,反斜槓不必做任何特殊處理。

3.正則查找函數 返回匹配對象

查找一個匹配項(search、match、fullmatch)的函數返回值都是一個 匹配對象Match,需要通過match.group獲取匹配值,這個很容易忘記。

Python正則表達式,看這一篇就夠了

另外還需要注意:match.group 與match.groups 函數的差別!

4.重複使用某個正則

如果要重複使用某個正則表達式,推薦先使用 re.compile(pattern)函數返回一個正則對象,然後複用這個正則對象,這樣會更快!

5.Python 正則面試

筆試可能會遇到需要使用Python正則表達式,不過不會太難的,大家只要記住那幾個方法的區別,會正確使用,基本問題不大。

文章所有內容精華豬哥已經整理成一份思維導圖:鏈接(或閱讀原文)
:https://pan.baidu.com/s/10MMpuf6Rcba-gvBo1oIzlw 密碼:y6z3

Python正則表達式,看這一篇就夠了
Python正則表達式,看這一篇就夠了

今日福利

遇見陸奇

同樣作為“百萬人學 AI”的重要組成部分,2020 AIProCon 開發者萬人大會將於 7 月 3 日至 4 日通過線上直播形式,讓開發者們一站式學習瞭解當下 AI 的前沿技術研究、核心技術與應用以及企業案例的實踐經驗,同時還可以在線參加精彩多樣的開發者沙龍與編程項目。參與前瞻系列活動、在線直播互動,不僅可以與上萬名開發者們一起交流,還有機會贏取直播專屬好禮,與技術大咖連麥。

    你點的每個“在看”,我都認真當成了AI


分享到:


相關文章: