Python正則表達式

正則表達式介紹

正則表達式(Regular expressions 也稱爲 REs,或 regexes 或 regex patterns)本質上是一個微小的且高度專業化的編程語言。它被嵌入到 Python 中,並經過 re 模塊提供給程序猿使用。使用正則表達式,你須要指定一些規則來描述那些你但願匹配的字符串集合。這些字符串集合可能包含英語句子、 e-mail 地址、TeX 命令,或任何你想要的東東。

正則表達式模式被編譯成一系列的字節碼,而後由一個 C 語言寫的匹配引擎所執行。對於高級的使用,你可能須要更關注匹配引擎是如何執行給定的 RE,並經過必定的方式來編寫 RE,以便產生一個能夠運行得更快的字節碼。本文暫不講解優化的細節,由於這須要你對匹配引擎的內部機制有一個很好的理解。但本文的例子均是符合標準的正則表達式語法。

小甲魚註釋:Python 的正則表達式引擎是用 C 語言寫的,因此效率是極高的。另,所謂的正則表達式,這裏說的 RE,就是上文咱們提到的「一些規則」。

正則表達式語言相對較小,而且受到限制,因此不是全部可能的字符串處理任務均可以使用正則表達式來完成。還有一些特殊的任務,可使用正則表達式來完成,可是表達式會所以而變得很是複雜。在這種狀況下,你可能經過本身編寫 Python 代碼來處理會更好些;儘管 Python 代碼比一個精巧的正則表達式執行起來會慢一些,但可能會更容易理解。

小甲魚註釋:這多是你們常說的「醜話說在前」吧,你們別管他,正則表達式很是優秀,她能夠處理你 98.3% 的文本任務,必定要好好學哦~~~~~


簡單的模式

咱們將從最簡單的正則表達式學習開始。因爲正則表達式經常使用於操做字符串的,所以咱們從最多見的任務下手:字符匹配。


字符匹配

大多數字母和字符會匹配它們自身。舉個例子,正則表達式 FishC 將徹底匹配字符串 "FishC"。(你能夠啓用不區分大小寫模式,這將使得 FishC 能夠匹配 "FISHC" 或 "fishc",咱們會在後邊討論這個話題。)

固然這個規則也有例外。有少數特殊的字符咱們稱之爲元字符(metacharacter),它們並不能匹配自身,它們定義了字符類、子組匹配和模式重複次數等。本文用很大的篇幅專門討論了各類元字符及其做用。

下邊是元字符的完整列表(咱們將在後邊逐一講解):

. ^ $ * + ? { } [ ] \ | ( )

小甲魚註釋:若是沒有這些元字符,正則表達式就變得跟字符串的 find() 方法同樣平庸了......


咱們先來看下方括號 [ ],它們指定一個字符類用於存放你須要匹配的字符集合。能夠單獨列出須要匹配的字符,也能夠經過兩個字符和一個橫杆 - 指定匹配的範圍。例如 [abc] 會匹配字符 a,b 或 c;[a-c] 能夠實現相同的功能。後者使用範圍來表示與前者相同的字符集合。若是你想只匹配小寫字母,你的 RE 能夠寫成 [a-z]。

須要注意的一點是:元字符在方括號中不會觸發「特殊功能」,在字符類中,它們只匹配自身。例如 [akm$] 會匹配任何字符 'a','k','m' 或 '$','$' 是一個元字符,但在方括號中它不表示特殊含義,它只匹配 '$' 字符自己。

你還能夠匹配方括號中未列出的全部其餘字符。作法是在類的開頭添加一個脫字符號 ^ ,例如 [^5] 會匹配除了 '5' 以外的任何字符。


或許最重要的元字符當屬反斜槓 \ 了。跟 Python 的字符串規則同樣,若是在反斜槓後邊緊跟着一個元字符,那麼元字符的「特殊功能」也不會被觸發。例如你須要匹配符號 [ 或 \,你能夠在它們前面加上一個反斜槓,以消除它們的特殊功能:\[,\\。

反斜槓後邊跟一些字符還能夠表示特殊的意義,例如表示十進制數字,表示全部的字母或者表示非空白的字符集合。

小甲魚解釋:反斜槓真牛逼,反斜槓後邊跟元字符去除特殊功能,反斜槓後邊跟普通字符實現特殊功能。

讓咱們來舉個例子:\w 匹配任何單詞字符。若是正則表達式以字節的形式表示,這至關於字符類 [a-zA-Z0-9_];若是正則表達式是一個字符串,\w 會匹配全部 Unicode 數據庫(unicodedata 模塊提供)中標記爲字母的字符。你能夠在編譯正則表達式的時候,經過提供 re.ASCII 表示進一步限制 \w 的定義。

小甲魚解釋:re.ASCII 標誌使得 \w 只能匹配 ASCII 字符,不要忘了,Python3 是 Unicode 的。

下邊列舉一些反斜槓加字符構成的特殊含義:

html

特殊字符
含義
\d
匹配任何十進制數字;至關於類 [0-9]
\D
與 \d 相反,匹配任何非十進制數字的字符;至關於類 [^0-9]
\s
匹配任何空白字符(包含空格、換行符、製表符等);至關於類 [ \t\n\r\f\v]
\S
與 \s 相反,匹配任何非空白字符;至關於類 [^ \t\n\r\f\v]
\w
匹配任何單詞字符,見上方解釋
\W
於 \w 相反
\b
匹配單詞的開始或結束
\B
與 \b 相反


它們能夠包含在一個字符類中,而且同樣擁有特殊含義。例如 [\s,.] 是一個字符類,它將匹配任何空白字符(/s 的特殊含義),',' 或 '.'。

最後咱們要講的一個元字符是 .,它匹配除了換行符之外的任何字符。若是設置了 re.DOTALL 標誌,. 將匹配包括換行符在內的任何字符。


重複的事情

使用正則表達式可以輕鬆的匹配不一樣的字符集合,但 Python 字符串現有的方法卻沒法實現。然而,若是你認爲這是正則表達式的惟一優點,那你就 too young too native 了。正則表達式有另外一個強大的功能,就是你能夠指定 RE 部分被重複的次數。


咱們來看看 * 這個元字符,固然它不是匹配 '*' 字符自己(咱們說過元字符都是有特殊能力的),它用於指定前一個字符匹配零次或者屢次。

例如 ca*t 將匹配 ct(0 個字符 a),cat(1 個字符 a),caaat(3 個字符 a),等等。須要注意的是,因爲受到 C 語言的 int 類型大小的內部限制,正則表達式引擎會限制字符 'a' 的重複個數不超過 20 億個;不過,一般咱們工做中也用不到那麼大的數據。


正則表達式默認的重複規則是貪婪的,當你重複匹配一個 RE 時,匹配引擎會嘗試儘量多的去匹配。直到 RE 不匹配或者到告終尾,匹配引擎就會回退一個字符,而後再繼續嘗試匹配。

咱們經過例子一步步的給你們講解什麼叫「貪婪」:先考慮一下表達式 a[bcd]*b,首先須要匹配字符 'a',而後是零個到多個 [bcd],最後以 'b' 結尾。那如今想象一下,這個 RE 匹配字符串 abcbd 會怎樣?

python

步驟 匹配 說明
1 a 匹配 RE 的第一個字符 'a'
2 abcbd 引擎在符合規則的狀況下儘量地匹配 [bcd]*,直到該字符串的結尾
3 失敗 引擎嘗試匹配 RE 最後一個字符 'b',但當前位置已是字符串的結尾,因此失敗了結
4 abcb 回退,因此 [bcd]* 匹配少一個字符
5 失敗 再一次嘗試匹配 RE 最後一個字符 'b',但字符串最後一個字符是 'd',因此失敗了結
6 abc 再次回退,因此 [bcd]* 此次只匹配 'bc'
7 abcb 再一次嘗試匹配字符 'b',這一次字符串當前位置指向的字符正好是 'b',匹配成功


最終,RE 匹配的結果是 abcb。

小甲魚解釋:正則表達式默認的匹配規則是貪婪的,後邊有教你如何使用非貪婪的方法匹配。


另外一個實現重複的元字符是 +,用於指定前一個字符匹配一次或者屢次。

要特別注意 * 和 + 的區別:* 匹配的是零次或者屢次,因此被重複的內容可能壓根兒不會出現;+ 至少須要出現一次。例如 ca+t 會匹配 cat 和 caaat,但不會匹配 ct。


還有兩個表示重複的元字符,其中一個是問號 ?,用於指定前一個字符匹配零次或者一次。你能夠這麼想,它的做用就是把某種東西標誌位可選的。例如 小?甲魚 能夠匹配 小甲魚,也能夠匹配 甲魚。


最靈活的應該是元字符 {m,n}(m 和 n 都是十進制整數),上邊講到的幾個元字符均可以使用它來表達,它的含義是前一個字符必須匹配 m 次到 n 次之間。例如 a/{1,3}b 會匹配 a/b,a//b 和 a///b。但不會匹配 ab(沒有斜槓);也不會匹配 a////b(斜槓超過三個)。

你能夠省略 m 或者 n,這樣的話,引擎會假定一個合理的值代替。省略 m,將被解釋爲下限 0;省略 n 則會被解釋爲無窮大(事實上是上邊咱們提到的 20 億)。

小甲魚解釋:若是是 {,n} 至關於 {0,n};若是是 {m,} 至關於 {m,+無窮};若是是 {n},則是重複前一個字符 n 次。另外還有一個超容易出錯的是寫成 {m, n},看着挺美,但注意,正則表達式裏邊不能隨意添加空格,否則會改變原來的含義。


聰明的魚油應該已經發現了,其實 *、+ 和 ? 均可以使用 {m,n} 來代替。{0,} 跟 * 是同樣的;{1,} 跟 + 是同樣的;{0,1} 跟 ? 是同樣的。不過仍是鼓勵你們記住並使用 *、+ 和 ?,由於這些字符更短而且更容易閱讀。

小甲魚解釋:還有一個緣由是匹配引擎對 * + ? 作了優化,效率要更高些。正則表達式

 

使用正則表達式

如今咱們開始來寫一些簡單的正則表達式吧。Python 經過 re 模塊爲正則表達式引擎提供一個接口,同時容許你將正則表達式編譯成模式對象,並用它們來進行匹配。

小甲魚解釋:re 模塊是使用 C 語言編寫,因此效率比你用普通的字符串方法要高得多;將正則表達式進行編譯(compile)也是爲了進一步提升效率;後邊咱們會常常提到「模式」,指的就是正則表達式被編譯成的模式對象。


編譯正則表達式

正則表達式被編譯爲模式對象,該對象擁有各類方法供你操做字符串,如查找模式匹配或者執行字符串替換。

spring

  1. >>> import re
  2. >>> p = re.compile('ab*')
  3. >>> p
  4. <_sre.SRE_Pattern object at 0x...>
複製代碼


re.compile() 也能夠接受 flags 參數,用於開啓各類特殊功能和語法變化,咱們會在後邊一一介紹。

如今咱們先來看個簡單的例子:
數據庫

  1. >>> p = re.compile('ab*', re.IGNORECASE)
複製代碼


正則表達式做爲一個字符串參數傳給 re.compile()。因爲正則表達式並非 Python 的核心部分,所以沒有爲它提供特殊的語法支持,因此正則表達式只能以字符串的形式表示。(有些應用根本就不須要使用到正則表達式,因此 Python 社區的小夥伴們認爲沒有必要將其歸入 Python 的核心。)相反,re 模塊僅僅是做爲 C 的擴展模塊包含在 Python 中,就像 socket 模塊和 zlib 模塊。

使用字符串來表示正則表達式保持了 Python 簡潔的一向風格,但也所以有一些負面影響,下邊咱們就來談一談。


麻煩的反斜槓

上一篇中咱們已經提到了,正則表達式使用 '\' 字符來使得一些普通的字符擁有特殊的能力(例如 \d 表示匹配任何十進制數字),或者剝奪一些特殊字符的能力(例如 \[ 表示匹配左方括號 '[')。這會跟 Python 字符串中實現相同功能的字符發生衝突。

小甲魚解釋:挺拗口,接着看例子你就懂了~

如今的狀況是,你須要在 LaTeX 文件中使用正則表達式匹配字符串 '\section'。由於反斜槓做爲須要匹配的特殊字符,因此你須要再它前邊加多一個反斜槓來剝奪它的特殊功能。因此咱們會把正則表達式的字符寫成 '\\section'。

但不要忘了,Python 在字符串中一樣使用反斜槓來表示特殊意義。所以,若是咱們想將 '\\section' 完整地傳給 re.compile(),咱們須要再次添加兩個反斜槓......

express

匹配字符 匹配階段
\section 須要匹配的字符串
\\section 正則表達式使用 '\\' 表示匹配字符 '\'
"\\\\section" 不巧,Python 字符串也使用 '\\' 表示字符 '\'


簡而言之,爲了匹配反斜槓這個字符,咱們須要在字符串中使用四個反斜槓才行。因此,在正則表達式中頻繁地使用反斜槓,會形成反斜槓風暴,進而致使你的字符串極其難懂。

解決方法是使用 Python 的原始字符串來表示正則表達式(就是在字符串前邊加上 r,你們還記得吧...):

編程

正則字符串 原始字符串
"ab*" r"ab*"
"\\\\section" r"\\section"
"\\w+\\s+\\1" r"\w+\s+\1"


小甲魚解釋:強烈建議使用原始字符串來表達正則表達式。


實現匹配

當你將正則表達式編譯以後,你就獲得一個模式對象。那你拿他能夠用來作什麼呢?模式對象擁有不少方法和屬性,咱們下邊列舉最重要的幾個來說:

緩存

方法 功能
match() 判斷一個正則表達式是否從開始處匹配一個字符串
search() 遍歷字符串,找到正則表達式匹配的第一個位置
findall() 遍歷字符串,找到正則表達式匹配的全部位置,並以列表的形式返回
finditer() 遍歷字符串,找到正則表達式匹配的全部位置,並以迭代器的形式返回


若是沒有找到任何匹配的話,match() 和 search() 會返回 None;若是匹配成功,則會返回一個匹配對象(match object),包含全部匹配的信息:例如從哪兒開始,到哪兒結束,匹配的子字符串等等。


接下來咱們一步步講解:

socket

  1. >>> import re
  2. >>> p = re.compile('[a-z]+')
  3. >>> p
  4. re.compile('[a-z]+')
複製代碼


如今,你能夠嘗試使用正則表達式
編程語言

  • >>> print(p.match(""))
  • None

複製代碼
由於 + 表示匹配一次或者屢次,因此空字符串不能被匹配。所以,match() 返回 None。

咱們再嘗試一個能夠匹配的字符串:

  1. >>> m = p.match('fishc')
  2. >>> m
  3. <_sre.SRE_Match object; span=(0, 5), match='fishc'>
複製代碼


在這個例子中,match() 返回一個匹配對象,咱們將其存放在變量 m 中,以便往後使用。


接下來讓咱們來看看匹配對象裏邊有哪些信息吧。匹配對象包含了不少方法和屬性,如下幾個是最重要的:

方法 功能
group() 返回匹配的字符串
start() 返回匹配的開始位置
end() 返回匹配的結束位置
span() 返回一個元組表示匹配位置(開始,結束)


你們看:

  1. >>> m.group()
  2. 'fishc'
  3. >>> m.start()
  4. 0
  5. >>> m.end()
  6. 5
  7. >>> m.span()
  8. (0, 5)
複製代碼


因爲 match() 只檢查正則表達式是否在字符串的起始位置匹配,因此 start() 老是返回 0。

然而,search() 方法可就不同咯:

  1. >>> print(p.match('^_^fishc'))
  2. None
  3. >>> m = p.search('^_^fishc')
  4. >>> print(m)
  5. <_sre.SRE_Match object; span=(3, 8), match='fishc'>
  6. >>> m.group()
  7. 'fishc'
  8. >>> m.span()
  9. (3, 8)
複製代碼


在實際應用中,最經常使用的方式是將匹配對象存放在一個局部變量中,並檢查其返回值是否爲 None。

形式一般以下:

  1. p = re.compile( ... )
  2. m = p.match( 'string goes here' )
  3. if m:
  4. print('Match found: ', m.group())
  5. else:
  6. print('No match')
複製代碼


有兩個方法能夠返回全部的匹配結果,一個是 findall(),另外一個是 finditer()。

findall() 返回的是一個列表:

  1. >>> p = re.compile('\d+')
  2. >>> p.findall('3只小甲魚,15條腿,多出的3條在哪裏?')
  3. ['3', '15', '3']
複製代碼


findall() 須要在返回前先建立一個列表,而 finditer() 則是將匹配對象做爲一個迭代器返回:

  1. >>> iterator = p.finditer('3只小甲魚,15條腿,還有3條去了哪裏?')
  2. >>> iterator
  3. <callable_iterator object at 0x10511b588>
  4. >>> for match in iterator:
  5. print(match.span())

  6. (0, 1)
  7. (6, 8)
  8. (13, 14)
複製代碼


小甲魚解釋:若是列表很大,那麼返回迭代器的效率要高不少。迭代器的相關知識請看:《零基礎入門學習Python》048 | 魔法方法:迭代器

 

 

模塊級別的函數

使用正則表達式也並不是必定要建立模式對象,而後調用它的匹配方法。由於,re 模塊同時還提供了一些全局函數,例如 match(),search(),findall(),sub() 等等。這些函數的第一個參數是正則表達式字符串,其餘參數跟模式對象同名的方法採用同樣的參數;返回值也同樣,一樣是返回 None 或者匹配對象。

  1. >>> print(re.match(r'From\s+', 'From_FishC.com'))
  2. None
  3. >>> re.match(r'From\s+', 'From FishC.com')
  4. <_sre.SRE_Match object; span=(0, 5), match='From '>
複製代碼


其實,這些函數只是幫你自動建立一個模式對象,並調用相關的函數(上一篇的內容,還記得嗎?)。它們還將編譯好的模式對象存放在緩存中,以便未來能夠快速地直接調用。


那咱們究竟是應該直接使用這些模塊級別的函數呢,仍是先編譯一個模式對象,再調用模式對象的方法呢?這其實取決於正則表達式的使用頻率,若是說咱們這個程序只是偶爾使用到正則表達式,那麼全局函數是比較方便的;若是咱們的程序是大量的使用正則表達式(例如在一個循環中使用),那麼建議你使用後一種方法,由於預編譯的話能夠節省一些函數調用。但若是是在循環外部,因爲得益於內部緩存機制,二者效率相差無幾。


編譯標誌

編譯標誌讓你能夠修改正則表達式的工做方式。在 re 模塊下,編譯標誌均有兩個名字:完整名和簡寫,例如 IGNORECASE 簡寫是 I(若是你是 Perl 的粉絲,那麼你有福了,由於這些簡寫跟 Perl 是同樣的,例如 re.VERBOSE 的簡寫是 re.X)。另外,多個標誌還能夠同時使用(經過「|」),如:re.I | re.M 就是同時設置 I 和 M 標誌。

下邊列舉一些支持的編譯標誌:

標誌 含義
ASCII, A 使得轉義符號如 \w,\b,\s 和 \d 只能匹配 ASCII 字符
DOTALL, S 使得 . 匹配任何符號,包括換行符
IGNORECASE, I 匹配的時候不區分大小寫
LOCALE, L 支持當前的語言(區域)設置
MULTILINE, M 多行匹配,影響 ^ 和 $
VERBOSE, X (for 'extended') 啓用詳細的正則表達式



下面咱們來詳細講解一下它們的含義:

A
ASCII
使得 \w,\W,\b,\B,\s 和 \S 只匹配 ASCII 字符,而不匹配完整的 Unicode 字符。這個標誌僅對 Unicode 模式有意義,並忽略字節模式。

S
DOTALL
使得 . 能夠匹配任何字符,包括換行符。若是不使用這個標誌,. 將匹配除了換行符的全部字符。

I
IGNORECASE
字符類和文本字符串在匹配的時候不區分大小寫。舉個例子,正則表達式 [A-Z] 也將會匹配對應的小寫字母,像 FishC 能夠匹配 FishC,fishc 或 FISHC 等。若是你不設置 LOCALE,則不會考慮語言(區域)設置這方面的大小寫問題。

L
LOCALE
使得 \w,\W,\b 和 \B 依賴當前的語言(區域)環境,而不是 Unicode 數據庫。

區域設置是 C 語言的一個功能,主要做用是消除不一樣語言之間的差別。例如你正在處理的是法文文本,你想使用 \w+ 來匹配單詞,可是 \w 只是匹配 [A-Za-z] 中的單詞,並不會匹配 'é' 或 'ç'。若是你的系統正確的設置了法語區域環境,那麼 C 語言的函數就會告訴程序 'é' 或 'ç' 也應該被認爲是一個字符。當編譯正則表達式的時候設置了 LOCALE 的標誌,\w+ 就能夠識別法文了,但速度多少會受到影響。

M
MULTILINE
(^ 和 $ 咱們尚未提到,彆着急,後邊咱們有細講...)

一般 ^ 只匹配字符串的開頭,而 $ 則匹配字符串的結尾。當這個標誌被設置的時候,^ 不只匹配字符串的開頭,還匹配每一行的行首;& 不只匹配字符串的結尾,還匹配每一行的行尾。

X
VERBOSE
這個標誌使你的正則表達式能夠寫得更好看和更有條理,由於使用了這個標誌,空格會被忽略(除了出如今字符類中和使用反斜槓轉義的空格);這個標誌同時容許你在正則表達式字符串中使用註釋,# 符號後邊的內容是註釋,不會遞交給匹配引擎(除了出如今字符類中和使用反斜槓轉義的 #)。

下邊是使用 re.VERBOSE 的例子,你們看下正則表達式的可讀性是否是提升了很多:

  1. charref = re.compile(r"""
  2. &[#] # 開始數字引用
  3. (
  4. 0[0-7]+ # 八進制格式
  5. | [0-9]+ # 十進制格式
  6. | x[0-9a-fA-F]+ # 十六進制格式
  7. )
  8. ; # 結尾分號
  9. """, re.VERBOSE)
複製代碼


若是沒有設置 VERBOSE 標誌,那麼一樣的正則表達式會寫成:

  1. charref = re.compile("&#(0[0-7]+|[0-9]+|x[0-9a-fA-F]+);")
複製代碼


哪一個可讀性更加?相信你們內心有底了。

 

更多強大的功能

到目前爲止,咱們只是介紹了正則表達式的一部分功能。在這一篇中,咱們會學習到一些新的元字符,而後再教你們如何使用組來得到被匹配的部分文本。


更多元字符

還有一些元字符咱們沒有講到,接下來小甲魚一一爲你們講解。

有些元字符它們不匹配任何字符,只是簡單地表示成功或失敗,所以這些字符也稱之爲零寬斷言。例如 \b 表示當前位置位於一個單詞的邊界,但 \b 並不能改變位置。所以,零寬斷言不該該被重複使用,由於 \b 並不會修改當前位置,因此 \b\b 跟 \b 是沒什麼兩樣的。

小甲魚解釋:不少人可能不理解「改變位置」和「零寬斷言」的意思?我嘗試解釋下,好比 abc 匹配完 a 以後,咱的當前位置就會移動,才能繼續匹配 b,依次類推...可是 \babc 的話,\b 表示當前位置在單詞的邊界(單詞的第一個字母或者最後一個字母),這時候當前位置不會發生改變,接着將 a 與當前位置的字符進行匹配......


|

或操做符,對兩個正則表達式進行或操做。若是 A 和 B 是正則表達式,A | B 會匹配 A 或 B 中出現的任何字符。爲了可以更加合理的工做,| 的優先級很是低。例如 Fish|C 應該匹配 Fish 或 C,而不是匹配 Fis,而後一個 'h' 或 'C'。

一樣,咱們使用 \| 來匹配 '|' 字符自己;或者包含在一個字符類中,像這樣 [|]。


^

匹配字符串的起始位置。若是設置了 MULTILINE 標誌,就會變成匹配每一行的起始位置。在 MULTILINE 中,每當遇到換行符就會馬上進行匹配。

舉個例子,若是你只但願匹配位於字符串開頭的單詞 From,那麼你的正則表達式能夠寫爲 ^From:

  1. >>> print(re.search('^From', 'From Here to Eternity'))
  2. <_sre.SRE_Match object; span=(0, 4), match='From'>
  3. >>> print(re.search('^From', 'Reciting From Memory'))
  4. None
複製代碼



$

匹配字符串的結束位置,每當遇到換行符也會離開進行匹配。

>>> print(re.search('}$', '{block}'))
<_sre.SRE_Match object; span=(6, 7), match='}'>
>>> print(re.search('}$', '{block} '))
None
>>> print(re.search('}$', '{block}\n'))
<_sre.SRE_Match object; span=(6, 7), match='}'>

一樣,咱們使用 \$ 來匹配 '$' 字符自己;或者包含在一個字符類中,像這樣 [$]。


\A

只匹配字符串的起始位置。若是沒有設置 MULTILINE 標誌的時候,\A 和 ^ 的功能是同樣的;但若是設置了 MULTILINE 標誌,則會有一些不一樣:\A 仍是匹配字符串的起始位置,但 ^ 會對字符串中的每一行都進行匹配。


\Z

只匹配字符串的結束位置。


\b

單詞邊界,這是一個只匹配單詞的開始和結尾的零寬斷言。「單詞」定義爲一個字母數字的序列,因此單詞的結束指的是空格或者非字母數字的字符。

下邊例子中,class 只有在出現一個完整的單詞 class 時才匹配;若是出如今別的單詞中,並不會匹配。

  1. >>> p = re.compile(r'\bclass\b')
  2. >>> print(p.search('no class at all'))
  3. <_sre.SRE_Match object; span=(3, 8), match='class'>
  4. >>> print(p.search('the declassified algorithm'))
  5. None
  6. >>> print(p.search('one subclass is'))
  7. None
複製代碼


在使用這些特殊的序列的時候,有兩點是須要注意的:第一點須要注意的是,Python 的字符串跟正則表達式在有些字符上是有衝突的(回憶以前反斜槓的例子)。好比說在 Python 中,\b 表示的是退格符(ASCII 碼值是 8)。因此,你若是不使用原始字符串,Python 會將 \b 轉換成退格符處理,這樣就確定跟你的預期不同了。

下邊的例子中,咱們故意不寫表示原始字符串的 'r',結果確實大相庭徑:

  1. >>> p = re.compile('\bclass\b')
  2. >>> print(p.search('no class at all'))
  3. None
  4. >>> print(p.search('\b' + 'class' + '\b'))
  5. <_sre.SRE_Match object; span=(0, 7), match='\x08class\x08'>
複製代碼


第二點須要注意的是,在字符類中不能使用這個斷言。跟 Python 同樣,在字符類中,\b 只是用來表示退格符。


\B

另外一個零寬斷言,與 \b 的含義相反,\B 表示非單詞邊界的位置。


分組

一般在實際的應用過程當中,咱們除了須要知道一個正則表達式是否匹配以外,還須要更多的信息。對於比較複雜的內容,正則表達式一般使用分組的方式分別對不一樣內容進行匹配。

下邊的例子,咱們將 RFC-822 頭用「:」號分紅名字和值分別匹配:

  1. From: author@example.com
  2. User-Agent: Thunderbird 1.5.0.9 (X11/20061227)
  3. MIME-Version: 1.0
  4. To: editor@example.com
複製代碼


像這種狀況,咱們就能夠寫一個正則表達式先來匹配一整個 RFC-822 頭,而後利用分組功能,使用一個組來匹配頭的名字,另外一個組匹配名字對應的值。

小甲魚解釋:RFC-822 是電子郵件的標準格式,固然看到這裏你還不知道分組要怎麼分,不急,請接着往下看......

在正則表達式中,使用元字符 ( ) 來劃分組。( ) 元字符跟數學表達式中的小括號含義差很少;它們將包含在內部的表達式組合在一塊兒,因此你能夠對一個組的內容使用重複操做的元字符,例如 *,+,? 或者 {m,n}。

例如,(ab)* 會匹配零個或者多個 ab:

  1. >>> p = re.compile('(ab)*')
  2. >>> print(p.match('ababababab').span())
  3. (0, 10)
複製代碼


使用 ( ) 表示的子組咱們還能夠對它進行按層次索引,能夠將索引值做爲參數傳遞給這些方法:group(),start(),end() 和 span()。序號 0 表示第一個分組(這個是默認分組,一直存在的,因此不傳入參數至關於默認值 0):

  1. >>> p = re.compile('(a)b')
  2. >>> m = p.match('ab')
  3. >>> m.group()
  4. 'ab'
  5. >>> m.group(0)
  6. 'ab'
複製代碼


小甲魚解釋:有幾對小括號就是分紅了幾個子組,例如 (a)(b) 和 (a(b)) 都是由兩個子組構成的。

子組的索引值是從左到右進行編號,子組也容許嵌套,所以咱們能夠經過從左往右來統計左括號 ( 來肯定子組的序號。

  1. >>> p = re.compile('(a(b)c)d')
  2. >>> m = p.match('abcd')
  3. >>> m.group(0)
  4. 'abcd'
  5. >>> m.group(1)
  6. 'abc'
  7. >>> m.group(2)
  8. 'b'
複製代碼


group() 方法能夠一次傳入多個子組的序號:

  1. >>> m.group(2,1,2)
  2. ('b', 'abc', 'b')
複製代碼


小甲魚解釋:start() 是得到參數子組的開始位置;end() 是得到對應子組的結束位置;span() 是得到對應子組的範圍。

咱們還特麼能夠經過 groups() 方法一次性返回全部的子組匹配的字符串:

  1. >>> m.groups()
  2. ('abc', 'b')
複製代碼


還有一個反向引用的概念須要介紹。反向引用指的是你能夠在後面的位置使用先前匹配過的內容,用法是反斜槓加上數字。例如 \1 表示引用前邊成功匹配的序號爲 1 的子組。

  1. >>> p = re.compile(r'(\b\w+)\s+\1')
  2. >>> p.search('Paris in the the spring').group()
  3. 'the the'
複製代碼


若是隻是搜索字符串,反向引用不會被用到,由於不多有文本格式會這樣來重複字符。可是,你很快會發現,在字符串替換的時候,反向引用是很是有用的(深井冰)!

小甲魚註釋:注意,在 Python 的字符串中會使用反斜槓加數字的方式來表示數字的值對應的 ASCII 字符,因此在使用反向索引的正則表達式中,咱們依然強調要使用原始字符串。

 

 

修改字符串

咱們已經介紹完如何對字符進行搜索,接下來咱們講講正則表達式如何修改字符串。

正則表達式使用如下方法修改字符串:

方法 用途
split() 在正則表達式匹配的地方進行分割,並返回一個列表
sub() 找到全部匹配的子字符串,並替換爲新的內容
subn() 跟 sub() 幹同樣的勾當,但返回新的字符串以及替換的數目



分割字符串

正則表達式的 split() 方法將字符串在匹配的地方進行分割,並將分割後的結果做爲列表返回。它的作法其實很像字符串的 split() 方法,但這個可使用更加普遍的分隔符。你猜的沒錯,它同時提供了一個模塊級別的函數:re.split()

.split(string[, maxsplit=0])

經過正則表達式匹配來分割字符串。若是在 RE 中,你使用了捕獲組,那麼它們的內容會做爲一個列表返回。你能夠經過傳入一個 maxsplit 參數來設置分割的數量。若是 maxsplit 的值是非 0,表示至多有 maxsplit 個分割會被處理,剩下的內容做爲列表的最後一個元素返回。


下邊例子中,分隔符是任何非字母數字字符:

  1. >>> p = re.compile(r'\W+')
  2. >>> p.split('This is a test, short and sweet, of split().')
  3. ['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']
  4. >>> p.split('This is a test, short and sweet, of split().', 3)
  5. ['This', 'is', 'a', 'test, short and sweet, of split().']
複製代碼


有時候你可能不只對分隔符之間的內容感興趣,你可能對分隔符自己(就是正則表達式匹配的內容)也一樣感興趣。若是使用了捕獲組,那麼做爲分隔符的值也會被返回:

  1. >>> p = re.compile(r'\W+')
  2. >>> p2 = re.compile(r'(\W+)')
  3. >>> p.split('This... is a test.')
  4. ['This', 'is', 'a', 'test', '']
  5. >>> p2.split('This... is a test.')
  6. ['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']
複製代碼


模塊級別的函數 re.split() 除了將 RE 做爲第一個參數外,其餘參數是同樣的:

  1. >>> re.split('[\W]+', 'Words, words, words.')
  2. ['Words', 'words', 'words', '']
  3. >>> re.split('([\W]+)', 'Words, words, words.')
  4. ['Words', ', ', 'words', ', ', 'words', '.', '']
  5. >>> re.split('[\W]+', 'Words, words, words.', 1)
  6. ['Words', 'words, words.']
複製代碼



搜索和替換

另外一個常見的任務就是找到全部的匹配部分,並替換成不一樣的字符串。sub 方法能夠幫你實現這個願望!sub 方法有一個 replacement 參數,它能夠是一個待替換的字符串,或者一個處理字符串的函數。

.sub(replacement, string[, count=0])

返回一個字符串,這個字符串從最左邊開始,全部 RE 匹配的地方都替換成 replacement。若是沒有找到任何匹配,那麼返回原字符串。

可選參數 count 指定最多替換的次數,必須是一個非負值。默認值是 0,意思是替換全部找到的匹配。


下邊是使用 sub() 方法的例子,它會將全部的顏色替換成 color:

  1. >>> p = re.compile( '(blue|white|red)')
  2. >>> p.sub( 'colour', 'blue socks and red shoes')
  3. 'colour socks and colour shoes'
  4. >>> p.sub( 'colour', 'blue socks and red shoes', count=1)
  5. 'colour socks and red shoes'
複製代碼


subn() 方法跟 sub() 方法幹一樣的勾當,但區別是返回值爲一個包含有兩個元素的元組:一個是替換後的字符串,一個是替換的數目。

  1. >>> p = re.compile( '(blue|white|red)')
  2. >>> p.subn( 'colour', 'blue socks and red shoes')
  3. ('colour socks and colour shoes', 2)
  4. >>> p.subn( 'colour', 'no colours at all')
  5. ('no colours at all', 0)
複製代碼


空匹配只有在它們沒有緊挨着前一個匹配時纔會被替換掉:

  1. >>> p = re.compile('x*')
  2. >>> p.sub('-', 'abxd')
  3. '-a-b-d-'
複製代碼


若是 replacement 參數是一個字符串,那麼裏邊的反斜槓都會被處理。好比 \n 將會被轉換成一個換行符,\r 轉換成回車,等等。未知的轉義如 \j 保持原樣。逆向引用如 \6,則被 RE 中相應的捕獲組匹配的內容所替換。這使你能夠在替換後的字符串中插入一部分原字符串。

下邊例子中,將匹配被 { 和 } 括起來的單詞 section,並將 section 替換成 subsection:

  1. >>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE)
  2. >>> p.sub(r'subsection{\1}','section{First} section{second}')
  3. 'subsection{First} subsection{second}'
複製代碼


小甲魚解釋:1. 你們還記得嗎?這裏開啓了 re.VERBOSE,空格將被忽略。由於這裏一堆符號,用空格隔開看着纔不會亂糟糟的......2. 這裏 r'subsection{\1}' 使用 \1 引用匹配模式中的 ([^}]*) 匹配的字符串內容。

還可使用 Python 的擴展語法 (?P<name>...) 指定命名組,引用命名組的語法是 \g<name> 會將名字爲 \g<2> 其實就至關於 \g<2>0 的含義是引用序號爲 2 的組,而後後邊匹配一個字符 '0',而你寫成 \20 就會被認爲是引用序號爲 20 的組了。

  1. >>> p = re.compile('section{ (?P<name> [^}]* ) }', re.VERBOSE)
  2. >>> p.sub(r'subsection{\1}','section{First}')
  3. 'subsection{First}'
  4. >>> p.sub(r'subsection{\g<1>}','section{First}')
  5. 'subsection{First}'
  6. >>> p.sub(r'subsection{\g<name>}','section{First}')
  7. 'subsection{First}'
複製代碼


有時候你可能不知足簡單的字符串替換,你可能須要在替換的過程當中動點「手腳」......不要緊,同樣能夠知足你!replacement 參數還能夠是一個函數,該函數將會在正則表達式模式每次不重複匹配的時候被調用。在每次調用時,函數會收到一個匹配對象的參數,所以你就能夠利用這個對象去計算出新的字符串並返回它。

下邊的例子中,替換函數將十進制數替換爲十六進制數:

  1. >>> def hexrepl(match):
  2. ... "Return the hex string for a decimal number"
  3. ... value = int(match.group())
  4. ... return hex(value)
  5. ...
  6. >>> p = re.compile(r'\d+')
  7. >>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')
  8. 'Call 0xffd2 for printing, 0xc000 for user code.'
複製代碼


當使用模塊級的 re.sub() 函數時,正則表達式模式做爲第一個參數。該模式能夠是一個字符串或一個編譯好的對象。若是你須要指定正則表達式標誌,那麼你必須使用後者;或者使用模式內嵌修正器,例如 sub("(?i)b+", "x", "bbbb BBBB") 返回 'x x'。

 

常見問題

正則表達式是一個很是強大的工具,但在有些時候它並不能直觀地按照你的意願來運行。本篇咱們將指出一些最多見的錯誤。


使用字符串方法

有時使用 re 模塊是個錯誤!若是你匹配一個固定的字符串或者單個字符類,而且你沒有使用 re 的任何標誌(像 IGNORECASE 標誌),那麼就沒有必要使用正則表達式了。字符串有一些方法是對固定字符串進行操做的,而且它們一般比較快。由於它們都是獨立優化的 C 語言小循環,目的是在簡單的狀況下代替功能更增強大、更具通用性的正則表達式引擎。

舉個例子,例如你想把字符串中全部的 dead 替換成 word,你會想到使用正則表達式的 re.sub() 方法來實現,但這麼簡單的替換,仍是考慮直接使用字符串的 replace() 方法吧。但有一點你須要注意,就是 replace() 會在單詞裏邊進行替換,像 swordfish 會變成 sdeedfish,這顯然不是你想要的!replace() 沒辦法識別單詞的邊界,所以你纔來考慮使用正則表達式。只須要將 RE 的模式寫成 \bword\b 便可勝任此任務。

另外一個常見的狀況是從一個字符串中刪除單個字符或者用另外一個字符替代它。你也許會想到用 re.sub('\n', ' ', S) 這樣的正則表達式來實現,但其實字符的 translate() 方法徹底可以勝任這個任務,而且比任何正則表達式操做起來更快些。
簡而言之,在使用 re 模塊以前,先考慮一下你的問題是否能夠用更快速、簡單的字符串自帶方法來解決。


match() VS search()

match() 函數只會檢查 RE 是否在字符串的開始處匹配,而 search() 會遍歷整個字符串搜索匹配的內容。記住這一區別很重要。再次強調一下,match() 只會報告一次成功的匹配,而且匹配的位置必須是從字符串的第一個字符開始:

  1. >>> print(re.match('super', 'superstition').span())
  2. (0, 5)
  3. >>> print(re.match('super', 'insuperable'))
  4. None
複製代碼


另外一方面,search() 函數將遍歷整個字符串,並報告它找到的第一個匹配:

  1. >>> print(re.search('super', 'superstition').span())
  2. (0, 5)
  3. >>> print(re.search('super', 'insuperable').span())
  4. (2, 7)
複製代碼


有時候你可能會耍點小聰明,使用 re.match() 而後在 RE 的前邊加上 .*。但儘可能不要這麼作,最好採用 re.search() 代替。正則表達式編譯器會對 REs 作一些分析,以即可以在搜索匹配時提升速度。通常分析會先找到匹配的第一個字符是什麼。舉個例子,模式 Crow 必須從字符 'C' 開始匹配,那麼匹配引擎分析後會快速遍歷字符串,而後在 'C' 被找到以後纔開始所有匹配。

按照上面的分析,你添加一個 .* 會致使這個優化失敗,這就須要從頭至尾掃描一遍,而後再回溯匹配 RE 剩餘的部分。因此,請使用 re.search() 代替。


貪婪 VS 非貪婪

當重複一個正則表達式時,若是使用 a*,那麼結果是儘量多地去匹配。當你嘗試匹配一對對稱的定界符,例如 HTML 標誌中的尖括號,默認的貪婪模式會使得你很困擾。

咱們來看下例子:

  1. >>> s = '<html><head><title>Title</title>'
  2. >>> len(s)
  3. 32
  4. >>> print(re.match('<.*>', s).span())
  5. (0, 32)
  6. >>> print(re.match('<.*>', s).group())
  7. <html><head><title>Title</title>
複製代碼


RE 匹配在 < 後,.* 消耗掉字符串的剩餘部分。因爲正則表達式默認是貪婪的緣由,RE 必須從字符串的尾部一個字符一個字符地回溯,直到找到匹配的 >。你們看到,按照這種方法,最後找到匹配內容竟是 < 開始,到 > 結束。顯然這不是你想要的結果。

在這種狀況下,解決方案是使用非貪婪的限定符 *?、+?、?? 或 {m,n}?,儘量地匹配小的文本。

  1. >>> print(re.match('<.*?>', s).group())
  2. <html>
複製代碼


在上邊的例子中,< 被匹配後馬上嘗試匹配,若是失敗,匹配引擎前進一步,嘗試下一個字符,直到第一次匹配 >,這樣就獲得了咱們想要的結果。

注意,使用正則表達式分析 HTML 和 XML 是很痛苦的。當你編寫一個正則表達式去處理全部可能的狀況時,你會發現 HTML 和 XML 總會打破你的「規則」,這讓你很頭疼......像這樣的話,建議使用 HTML 和 XML 解析器來處理更合適。


使用 re.VERBOSE

如今你應該意識到了,正則表達式的表示很是緊湊。這也帶來了一個問題,就是很差閱讀。中等複雜的正則表達式可能包含許多反斜槓、圓括號和元字符,以致於難以讀懂。

在這些 REs 中,當編譯正則表達式時指定 re.VERBOSE 標誌是很是有幫助的。由於它容許你能夠編輯正則表達式的格式,使之更清楚。

re.VERBOSE 標誌有幾個做用。在正則表達式中不在字符類中的空白字符將被忽略。這就意味着像 I love FishC 這樣的表達式和可讀性較差的 IloveFishC 相同。但 [a b] 將匹配字符 'a'、'b' 或 ' ';另外,你也能夠把註釋放到 RE 中,註釋是從 # 開始到下一行。當使用三引號字符串時,會使得 REs 的格式更整潔:

pat = re.compile(r"""
\s* # Skip leading whitespace
(?P<header>[^:]+) # Header name
\s* : # Whitespace, and a colon
(?P<value>.*?) # The header's value -- *? used to
# lose the following trailing whitespace
\s*$ # Trailing whitespace to end-of-line
""", re.VERBOSE)

一樣的內容,下邊這個要難讀得多:

pat = re.compile(r"\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$")


Python3 正則表達式特殊符號及用法(詳細列表)



正則表達式的強大之處在於特殊符號的應用,特殊符號定義了字符集合、子組匹配、模式重複次數。正是這些特殊符號使得一個正則表達式能夠匹配字符串集合而不僅是一個字符串。

注1:爲了便於理解,難點的地方均用斜體舉了栗子。

注2:若是你對正則表達式一頭霧水或者看不懂下邊這個列表,那麼小甲魚極力推薦你先學習這個:Python3 如何優雅地使用正則表達式

字符
含義
.
表示匹配除了換行符外的任何字符
注:經過設置 re.DOTALL 標誌可使 . 匹配任何字符(包含換行符)
|
A | B,表示匹配正則表達式 A 或者 B
^
1. (脫字符)匹配輸入字符串的開始位置
2. 若是設置了 re.MULTILINE 標誌,^ 也匹配換行符以後的位置
$
1. 匹配輸入字符串的結束位置
2. 若是設置了 re.MULTILINE 標誌,$ 也匹配換行符以前的位置
\
1. 將一個普通字符變成特殊字符,例如 \d 表示匹配全部十進制數字
2. 解除元字符的特殊功能,例如 \. 表示匹配點號自己
3. 引用序號對應的子組所匹配的字符串
4. 詳見下方列舉
[...]
字符類,匹配所包含的任意一個字符
注1:連字符 - 若是出如今字符串中間表示字符範圍描述;若是若是出如今首位則僅做爲普通字符
注2:特殊字符僅有反斜線 \ 保持特殊含義,用於轉義字符。其它特殊字符如 *、+、? 等均做爲普通字符匹配
注3:脫字符 ^ 若是出如今首位則表示匹配不包含其中的任意字符;若是 ^ 出如今字符串中間就僅做爲普通字符匹配
{M,N}
M 和 N 均爲非負整數,其中 M <= N,表示前邊的 RE 匹配 M ~ N 次
注1:{M,} 表示至少匹配 M 次
注2:{,N} 等價於 {0,N}
注3:{N} 表示須要匹配 N 次
*
匹配前面的子表達式零次或屢次,等價於 {0,}
+
匹配前面的子表達式一次或屢次,等價於 {1,}
?
匹配前面的子表達式零次或一次,等價於 {0,1}
*?, +?, ??
默認狀況下 *、+ 和 ? 的匹配模式是貪婪模式(即會盡量多地匹配符合規則的字符串);*?、+? 和 ?? 表示啓用對應的非貪婪模式。
舉個栗子:對於字符串 "FishCCC",正則表達式 FishC+ 會匹配整個字符串,而 FishC+? 則匹配 "FishC"。
{M,N}?
同上,啓用非貪婪模式,即只匹配 M 次
(...)
匹配圓括號中的正則表達式,或者指定一個子組的開始和結束位置
注:子組的內容能夠在匹配以後被 \數字 再次引用
舉個栗子:(\w+) \1 能夠字符串 "FishC FishC.com" 中的 "FishC FishC"(注意有空格)
(?...)
(? 開頭的表示爲正則表達式的擴展語法(下邊這些是 Python 支持的全部擴展語法)
(?aiLmsux)
1. (? 後能夠緊跟着 'a','i','L','m','s','u','x' 中的一個或多個字符,只能在正則表達式的開頭使用
2. 每個字符對應一種匹配標誌:re-A(只匹配 ASCII 字符),re-I(忽略大小寫),re-L(區域設置),re-M(多行模式), re-S(. 匹配任何符號),re-X(詳細表達式),包含這些字符將會影響整個正則表達式的規則
3. 當你不想經過 re.compile() 設置正則表達式標誌,這種方法就很是有用啦
注意,因爲 (?x) 決定正則表達式如何被解析,因此它應該老是被放在最前邊(最多容許前邊有空白符)。若是 (?x) 的前邊是非空白字符,那麼 (?x) 就發揮不了做用了。
(?:...)
非捕獲組,即該子組匹配的字符串沒法從後邊獲取
(?P<name>...)
命名組,經過組的名字(name)便可訪問到子組匹配的字符串
(?P=name)
反向引用一個命名組,它匹配指定命名組匹配的任何內容
(?#...)
註釋,括號中的內容將被忽略
(?=...)
前向確定斷言。若是當前包含的正則表達式(這裏以 ... 表示)在當前位置成功匹配,則表明成功,不然失敗。一旦該部分正則表達式被匹配引擎嘗試過,就不會繼續進行匹配了;剩下的模式在此斷言開始的地方繼續嘗試。
舉個栗子:love(?=FishC) 只匹配後邊緊跟着 "FishC" 的字符串 "love"
(?!...)
前向否認斷言。這跟前向確定斷言相反(不匹配則表示成功,匹配表示失敗)。
舉個栗子:FishC(?!\.com) 只匹配後邊不是 ".com" 的字符串 "FishC"
(?<=...)
後向確定斷言。跟前向確定斷言同樣,只是方向相反。
舉個栗子:(?<=love)FishC 只匹配前邊緊跟着 "love" 的字符串 "FishC"
(?<!...)
後向否認斷言。跟前向確定斷言同樣,只是方向相反。
舉個栗子:(?<!FishC)\.com 只匹配前邊不是 "FishC" 的字符串 ".com"
(?(id/name)yes-pattern|no-pattern)
1. 若是子組的序號或名字存在的話,則嘗試 yes-pattern 匹配模式;不然嘗試 no-pattern 匹配模式
2. no-pattern 是可選的
舉個栗子:(<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$) 是一個匹配郵件格式的正則表達式,能夠匹配 <user@fishc.com> 和 'user@fishc.com',可是不會匹配 '<user@fishc.com' 或 'user@fishc.com>'
\
下邊列舉了由字符 '\' 和另外一個字符組成的特殊含義。注意,'\' + 元字符的組合能夠解除元字符的特殊功能
\序號
1. 引用序號對應的子組所匹配的字符串,子組的序號從 1 開始計算
2. 若是序號是以 0 開頭,或者 3 個數字的長度。那麼不會被用於引用對應的子組,而是用於匹配八進制數字所表示的 ASCII 碼值對應的字符
舉個栗子:(.+) \1 會匹配 "FishC FishC" 或 "55 55",但不會匹配 "FishCFishC"(注意,由於子組後邊還有一個空格)
\A
匹配輸入字符串的開始位置
\Z
匹配輸入字符串的結束位置
\b
匹配一個單詞邊界,單詞被定義爲 Unidcode 的字母數字或下橫線字符
舉個栗子:\bFishC\b 會匹配字符串 "love FishC"、FishC." 或 "(FishC)"
\B
匹配非單詞邊界,其實就是與 \b 相反
舉個栗子:py\B 會匹配字符串 "python"、"py3" 或 "py2",但不會匹配 "py "、"py." 或 "py!"
\d
1. 對於 Unicode(str 類型)模式:匹配任何一個數字,包括 [0-9] 和其餘數字字符;若是開啓了 re.ASCII 標誌,就只匹配 [0-9]
2. 對於 8 位(bytes 類型)模式:匹配 [0-9] 中任何一個數字
\D
匹配任何非 Unicode 的數字,其實就是與 \d 相反;若是開啓了 re.ASCII 標誌,則至關於匹配 [^0-9]
\s
1. 對於 Unicode(str 類型)模式:匹配 Unicode 中的空白字符(包括 [ \t\n\r\f\v] 以及其餘空白字符);若是開啓了 re.ASCII 標誌,就只匹配 [ \t\n\r\f\v]
2. 對於 8 位(bytes 類型)模式:匹配 ASCII 中定義的空白字符,即 [ \t\n\r\f\v]
\S
匹配任何非 Unicode 中的空白字符,其實就是與 \s 相反;若是開啓了 re.ASCII 標誌,則至關於匹配 [^ \t\n\r\f\v]
\w
1. 對於 Unicode(str 類型)模式:匹配任何 Unicode 的單詞字符,基本上全部語言的字符均可以匹配,固然也包括數字和下橫線;若是開啓了 re.ASCII 標誌,就只匹配 [a-zA-Z0-9_]
2. 對於 8 位(bytes 類型)模式:匹配 ASCII 中定義的字母數字,即 [a-zA-Z0-9_]
\W
匹配任何非 Unicode 的單詞字符,其實就是與 \w 相反;若是開啓了 re.ASCII 標誌,則至關於 [^a-zA-Z0-9_]
轉義符號
正則表達式還支持大部分 Python 字符串的轉義符號:\a,\b,\f,\n,\r,\t,\u,\U,\v,\x,\\
注1:\b 一般用於匹配一個單詞邊界,只有在字符類中才表示「退格」
注2:\u 和 \U 只有在 Unicode 模式下才會被識別
注3:八進制轉義(\數字)是有限制的,若是第一個數字是 0,或者若是有 3 個八進制數字,那麼就被認爲是八進制數;其餘狀況則被認爲是子組引用;至於字符串,八進制轉義老是最多隻能是 3 個數字的長度
相關文章
相關標籤/搜索