溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

python正則表達(dá)式的用法

發(fā)布時(shí)間:2021-08-21 19:59:36 來(lái)源:億速云 閱讀:176 作者:chen 欄目:關(guān)系型數(shù)據(jù)庫(kù)

本篇內(nèi)容介紹了“python正則表達(dá)式的用法”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

簡(jiǎn)單模式

我們將從最簡(jiǎn)單的正則表達(dá)式學(xué)習(xí)開始。由于正則表達(dá)式常用于字符串操作,那我們就從最常見的任務(wù):字符匹配 下手。

有關(guān)正則表達(dá)式底層的計(jì)算機(jī)科學(xué)上的詳細(xì)解釋(確定性和非確定性有限自動(dòng)機(jī)),你可以查閱編寫編譯器相關(guān)的任何教科書。

字符匹配

大多數(shù)字母和字符一般都會(huì)和自身匹配。例如,正則表達(dá)式 test 會(huì)和字符串“test”完全匹配。(你也可以使用大小寫不敏感模式,它還能讓這個(gè) RE 匹配“Test”或“TEST”;稍后會(huì)有更多解釋。)

這個(gè)規(guī)則當(dāng)然會(huì)有例外;有些字符比較特殊,它們和自身并不匹配,而是會(huì)表明應(yīng)和一些特殊的東西匹配,或者它們會(huì)影響到 RE 其它部分的重復(fù)次數(shù)。本文很大篇幅專門討論了各種元字符及其作用。

這里有一個(gè)元字符的完整列表;其含義會(huì)在本指南余下部分進(jìn)行討論。

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

我們首先考察的元字符是"[" 和 "]"。它們常用來(lái)指定一個(gè)字符類別,所謂字符類別就是你想匹配的一個(gè)字符集。字符可以單個(gè)列出,也可以用“-”號(hào)分隔的兩個(gè)給定字符來(lái)表示一個(gè)字符區(qū)間。例如,[abc] 將匹配"a", "b", 或 "c"中的任意一個(gè)字符;也可以用區(qū)間[a-c]來(lái)表示同一字符集,和前者效果一致。如果你只想匹配小寫字母,那么 RE 應(yīng)寫成 [a-z].

1)元字符在類別里并不起作用。例如:[akm$]將匹配字符"a", "k", "m", 或 "$" 中的任意一個(gè);"$"通常用作元字符,但在字符類別里,其特性被除去,恢復(fù)成普通字符。

2)補(bǔ)集來(lái)匹配不在區(qū)間范圍內(nèi)的字符。其做法是把"^"作為類別的首個(gè)字符;其它地方的"^"只會(huì)簡(jiǎn)單匹配 "^"字符本身。例如,[^5] 將匹配除 "5" 之外的任意字符。

3)元字符反斜杠"\"。 做為 Python 中的字符串字母,反斜杠后面可以加不同的字符以表示不同特殊意義。它也可以用于取消所有的元字符,這樣你就可以在模式中匹配它們了。舉個(gè)例子,如果你需要匹配字符 "[" 或 "\",你可以在它們之前用反斜杠來(lái)取消它們的特殊意義: \[ 或 \\。

一些用 "\" 開始的特殊字符所表示的預(yù)定義字符集通常是很有用的,象數(shù)字集,字母集,或其它非空字符集。下列是可用的預(yù)設(shè)特殊字符:

\d  匹配任何十進(jìn)制數(shù);它相當(dāng)于類 [0-9]。
\D  匹配任何非數(shù)字字符;它相當(dāng)于類 [^0-9]。
\s  匹配任何空白字符;它相當(dāng)于類  [ \t\n\r\f\v]。
\S  匹配任何非空白字符;它相當(dāng)于類 [^ \t\n\r\f\v]。
\w  匹配任何字母數(shù)字字符;它相當(dāng)于類 [a-zA-Z0-9_]。
\W  匹配任何非字母數(shù)字字符;它相當(dāng)于類 [^a-zA-Z0-9_]。

這樣特殊字符都可以包含在一個(gè)字符類中。如,[\s,.]字符類將匹配任何空白字符或","或"."。

4)元字符是 . 。它匹配除了換行字符外的任何字符,在 alternate 模式(re.DOTALL)下它甚至可以匹配換行。"." 通常被用于你想匹配“任何字符”的地方。

重復(fù)

正則表達(dá)式第一件能做的事是能夠匹配不定長(zhǎng)的字符集,而這是其它能作用在字符串上的方法所不能做到的。 不過(guò),如果那是正則表達(dá)式唯一的附加功能的話,那么它們也就不那么優(yōu)秀了。它們的另一個(gè)功能就是你可以指定正則表達(dá)式的一部分的重復(fù)次數(shù)。

我們討論的第一個(gè)重復(fù)功能的元字符是 *。* 并不匹配字母字符 "*";相反,它指定前一個(gè)字符可以被匹配零次或更多次,而不是只有一次。

舉個(gè)例子,ca*t 將匹配 "ct" (0 個(gè) "a" 字符), "cat" (1 個(gè) "a"), "caaat" (3 個(gè) "a" 字符)等等。RE 引擎有各種來(lái)自 C 的整數(shù)類型大小的內(nèi)部限制,以防止它匹配超過(guò)20億個(gè) "a" 字符;你也許沒有足夠的內(nèi)存去建造那么大的字符串,所以將不會(huì)累計(jì)到那個(gè)限制。

象 * 這樣地重復(fù)是“貪婪的”;當(dāng)重復(fù)一個(gè) RE 時(shí),匹配引擎會(huì)試著重復(fù)盡可能多的次數(shù)。如果模式的后面部分沒有被匹配,匹配引擎將退回并再次嘗試更小的重復(fù)。


一步步的示例可以使它更加清晰。讓我們考慮表達(dá)式 a[bcd]*b。它匹配字母 "a",零個(gè)或更多個(gè)來(lái)自類 [bcd]中的字母,最后以 "b" 結(jié)尾?,F(xiàn)在想一想該 RE 對(duì)字符串 "abcbd" 的匹配。

Step Matched Explanation
1 a a 匹配模式
2 abcbd 引擎匹配 [bcd]*,并盡其所能匹配到字符串的結(jié)尾
3 Failure 引擎嘗試匹配 b,但當(dāng)前位置已經(jīng)是字符的最后了,所以失敗
4 abcb 退回,[bcd]*嘗試少匹配一個(gè)字符。
5 Failure 再次嘗次b,但在當(dāng)前最后一位字符是"d"。
6 abc 再次退回,[bcd]*只匹配 "bc"。
7 abcb 再次嘗試 b ,這次當(dāng)前位上的字符正好是 "b"

RE 的結(jié)尾部分現(xiàn)在可以到達(dá)了,它匹配 "abcb"。這證明了匹配引擎一開始會(huì)盡其所能進(jìn)行匹配,如果沒有匹配然后就逐步退回并反復(fù)嘗試 RE 剩下來(lái)的部分。直到它退回嘗試匹配 [bcd] 到零次為止,如果隨后還是失敗,那么引擎就會(huì)認(rèn)為該字符串根本無(wú)法匹配 RE 。


另一個(gè)重復(fù)元字符是 +,表示匹配一或更多次。請(qǐng)注意 * 和 + 之間的不同;* 匹配零或更多次,所以可以根本就不出現(xiàn),而 + 則要求至少出現(xiàn)一次。用同一個(gè)例子,ca+t 就可以匹配 "cat" (1 個(gè) "a"), "caaat" (3 個(gè) "a"), 但不能匹配 "ct"。


還有更多的限定符。問(wèn)號(hào) ? 匹配一次或零次;你可以認(rèn)為它用于標(biāo)識(shí)某事物是可選的。例如:home-?brew 匹配 "homebrew" 或 "home-brew"。


最復(fù)雜的重復(fù)限定符是 {m,n},其中 m 和 n 是十進(jìn)制整數(shù)。該限定符的意思是至少有 m 個(gè)重復(fù),至多到 n 個(gè)重復(fù)。舉個(gè)例子,a/{1,3}b 將匹配 "a/b","a//b" 和 "a///b"。它不能匹配 "ab" 因?yàn)闆]有斜杠,也不能匹配 "a////b" ,因?yàn)橛兴膫€(gè)。


你可以忽略 m 或 n;因?yàn)闀?huì)為缺失的值假設(shè)一個(gè)合理的值。忽略 m 會(huì)認(rèn)為下邊界是 0,而忽略 n 的結(jié)果將是上邊界為無(wú)窮大 -- 實(shí)際上是先前我們提到的20億,但這也許同無(wú)窮大一樣。


細(xì)心的讀者也許注意到其他三個(gè)限定符都可以用這樣方式來(lái)表示。 {0,} 等同于 *,{1,} 等同于 +,而{0,1}則與 ? 相同。如果可以的話,最好使用 *,+,或?。很簡(jiǎn)單因?yàn)樗鼈兏桃哺菀锥?/p>

使用正則表達(dá)式

現(xiàn)在我們已經(jīng)看了一些簡(jiǎn)單的正則表達(dá)式,那么我們實(shí)際在 Python 中是如何使用它們的呢? re 模塊提供了一個(gè)正則表達(dá)式引擎的接口,可以讓你將 REs 編譯成對(duì)象并用它們來(lái)進(jìn)行匹配。

編譯正則表達(dá)式

正則表達(dá)式被編譯成 `RegexObject` 實(shí)例,可以為不同的操作提供方法,如模式匹配搜索或字符串替換。

#!python
>>> import re
>>> p = re.compile('ab*')
>>> print p
<_sre.SRE_Pattern object at 0xb76e1a70>

re.compile() 也接受可選的標(biāo)志參數(shù),常用來(lái)實(shí)現(xiàn)不同的特殊功能和語(yǔ)法變更。我們稍后將查看所有可用的設(shè)置,但現(xiàn)在只舉一個(gè)例子:

#!python
>>> p = re.compile('ab*', re.IGNORECASE)

RE 被做為一個(gè)字符串發(fā)送給 re.compile()。REs 被處理成字符串是因?yàn)檎齽t表達(dá)式不是 Python 語(yǔ)言的核心部分,也沒有為它創(chuàng)建特定的語(yǔ)法。(應(yīng)用程序根本就不需要 REs,因此沒必要包含它們?nèi)ナ拐Z(yǔ)言說(shuō)明變得臃腫不堪。)而 re 模塊則只是以一個(gè) C 擴(kuò)展模塊的形式來(lái)被 Python 包含,就象 socket 或 zlib 模塊一樣

將 REs 作為字符串以保證 Python 語(yǔ)言的簡(jiǎn)潔,但這樣帶來(lái)的一個(gè)麻煩就是象下節(jié)標(biāo)題所講的。

反斜杠的麻煩

在早期規(guī)定中,正則表達(dá)式用反斜杠字符 ("\") 來(lái)表示特殊格式或允許使用特殊字符而不調(diào)用它的特殊用法。這就與 Python 在字符串中的那些起相同作用的相同字符產(chǎn)生了沖突。


讓我們舉例說(shuō)明,你想寫一個(gè) RE 以匹配字符串 "\section",可能是在一個(gè) LATEX 文件查找。為了要在程序代碼中判斷,首先要寫出想要匹配的字符串。接下來(lái)你需要在所有反斜杠和其它元字符前加反斜杠來(lái)取消其特殊意義,結(jié)果要匹配的字符串就成了"\\section"。 當(dāng)把這個(gè)字符串傳遞給re.compile()時(shí)必須還是"\\section"。然而,作為Python的字符串實(shí)值(string literals)來(lái)表示的話,"\\section"中兩個(gè)反斜杠還要再次取消特殊意義,最后結(jié)果就變成了"\\\\section"。

字符 階段
\section 要匹配的字符串
\\section 為 re.compile 取消反斜杠的特殊意義
"\\\\section" 為"\\section"的字符串實(shí)值(string literals)取消反斜杠的特殊意義


簡(jiǎn)單地說(shuō),為了匹配一個(gè)反斜杠,不得不在 RE 字符串中寫 '\\\\',因?yàn)檎齽t表達(dá)式中必須是 "\\",而每個(gè)反斜杠在常規(guī)的 Python 字符串實(shí)值中必須表示成 "\\"。在 REs 中反斜杠的這個(gè)重復(fù)特性會(huì)導(dǎo)致大量重復(fù)的反斜杠,而且所生成的字符串也很難懂。

解決的辦法就是為正則表達(dá)式使用 Python 的 raw 字符串表示;在字符串前加個(gè) "r" 反斜杠就不會(huì)被任何特殊方式處理,所以 r"\n" 就是包含"\" 和 "n" 的兩個(gè)字符,而 "\n" 則是一個(gè)字符,表示一個(gè)換行。正則表達(dá)式通常在 Python 代碼中都是用這種 raw 字符串表示。

常規(guī)字符串 Raw 字符串
"ab*" r"ab*"
"\\\\section" r"\\section"
"\\w+\\s+\\1" r"\w+\s+\1"

執(zhí)行匹配

一旦你有了已經(jīng)編譯了的正則表達(dá)式的對(duì)象,你要用它做什么呢?`RegexObject` 實(shí)例有一些方法和屬性。這里只顯示了最重要的幾個(gè),如果要看完整的列表請(qǐng)查閱 Python Library Reference

方法/屬性 作用
match() 決定 RE 是否在字符串剛開始的位置匹配
search() 掃描字符串,找到這個(gè) RE 匹配的位置
findall() 找到 RE 匹配的所有子串,并把它們作為一個(gè)列表返回
finditer() 找到 RE 匹配的所有子串,并把它們作為一個(gè)迭代器返回


如果沒有匹配到的話,match() 和 search() 將返回 None。如果成功的話,就會(huì)返回一個(gè) `MatchObject` 實(shí)例,其中有這次匹配的信息:它是從哪里開始和結(jié)束,它所匹配的子串等等。

你可以用采用人機(jī)對(duì)話并用 re 模塊實(shí)驗(yàn)的方式來(lái)學(xué)習(xí)它。如果你有 Tkinter 的話,你也許可以考慮參考一下 Tools/scripts/redemo.py,一個(gè)包含在 Python 發(fā)行版里的示范程序。

首先,運(yùn)行 Python 解釋器,導(dǎo)入 re 模塊并編譯一個(gè) RE:

#!python
Python 2.2.2 (#1, Feb 10 2003, 12:57:01)
>>> import re
>>> p = re.compile('[a-z]+')
>>> p
<_sre.SRE_Pattern object at 80c3c28>

現(xiàn)在,你可以試著用 RE 的 [a-z]+ 去匹配不同的字符串。一個(gè)空字符串將根本不能匹配,因?yàn)?+ 的意思是 “一個(gè)或更多的重復(fù)次數(shù)”。 在這種情況下 match() 將返回 None,因?yàn)樗菇忉屍鳑]有輸出。你可以明確地打印出 match() 的結(jié)果來(lái)弄清這一點(diǎn)。

#!python
>>> p.match("")
>>> print p.match("")
None

現(xiàn)在,讓我們?cè)囍盟鼇?lái)匹配一個(gè)字符串,如 "tempo"。這時(shí),match() 將返回一個(gè) MatchObject。因此你可以將結(jié)果保存在變量里以便後面使用。

#!python
>>> m = p.match( 'tempo')
>>> print m
<_sre.SRE_Match object at 80c4f68>

現(xiàn)在你可以查詢 `MatchObject` 關(guān)于匹配字符串的相關(guān)信息了。MatchObject 實(shí)例也有幾個(gè)方法和屬性;最重要的那些如下所示:

方法/屬性 作用
group() 返回被 RE 匹配的字符串
start() 返回匹配開始的位置
end() 返回匹配結(jié)束的位置
span() 返回一個(gè)元組包含匹配 (開始,結(jié)束) 的位置


試試這些方法不久就會(huì)清楚它們的作用了:

#!python
>>> m.group()
'tempo'
>>> m.start(), m.end()
(0, 5)
>>> m.span()
(0, 5)

group() 返回 RE 匹配的子串。start() 和 end() 返回匹配開始和結(jié)束時(shí)的索引。span() 則用單個(gè)元組把開始和結(jié)束時(shí)的索引一起返回。因?yàn)槠ヅ浞椒z查到如果 RE 在字符串開始處開始匹配,那么 start() 將總是為零。然而, `RegexObject` 實(shí)例的 search 方法掃描下面的字符串的話,在這種情況下,匹配開始的位置就也許不是零了。

#!python
>>> print p.match('::: message')
None
>>> m = p.search('::: message') ; print m
<re.MatchObject instance at 80c9650>
>>> m.group()
'message'
>>> m.span()
(4, 11)

在實(shí)際程序中,最常見的作法是將 `MatchObject` 保存在一個(gè)變量里,然後檢查它是否為 None,通常如下所示:

#!python
p = re.compile( ... )
m = p.match( 'string goes here' )
if m:
print 'Match found: ', m.group()
else:
print 'No match'

兩個(gè) `RegexObject` 方法返回所有匹配模式的子串。findall()返回一個(gè)匹配字符串行表:

#!python
>>> p = re.compile('\d+')
>>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
['12', '11', '10']

findall() 在它返回結(jié)果時(shí)不得不創(chuàng)建一個(gè)列表。在 Python 2.2中,也可以用 finditer() 方法。

#!python
>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...')
>>> iterator
<callable-iterator object at 0x401833ac>
>>> for match in iterator:
...     print match.span()
...
(0, 2)
(22, 24)
(29, 31)
標(biāo)志 含義
DOTALL, S 使 . 匹配包括換行在內(nèi)的所有字符
IGNORECASE, I 使匹配對(duì)大小寫不敏感
LOCALE, L 做本地化識(shí)別(locale-aware)匹配
MULTILINE, M 多行匹配,影響 ^ 和 $
VERBOSE, X 能夠使用 REs 的 verbose 狀態(tài),使之被組織得更清晰易懂

I (IGNORECASE)

使匹配對(duì)大小寫不敏感;字符類和字符串匹配字母時(shí)忽略大小寫。舉個(gè)例子,[A-Z]也可以匹配小寫字母,Spam 可以匹配 "Spam", "spam", 或 "spAM"。這個(gè)小寫字母并不考慮當(dāng)前位置。

 

L (LOCALE)

影響 \w, \W, \b, 和 \B,這取決于當(dāng)前的本地化設(shè)置。

locales 是 C 語(yǔ)言庫(kù)中的一項(xiàng)功能,是用來(lái)為需要考慮不同語(yǔ)言的編程提供幫助的。舉個(gè)例子,如果你正在處理法文文本,你想用 \w+ 來(lái)匹配文字,但 \w 只匹配字符類 [A-Za-z];它并不能匹配 "é" 或 "?"。如果你的系統(tǒng)配置適當(dāng)且本地化設(shè)置為法語(yǔ),那么內(nèi)部的 C 函數(shù)將告訴程序 "é" 也應(yīng)該被認(rèn)為是一個(gè)字母。當(dāng)在編譯正則表達(dá)式時(shí)使用 LOCALE 標(biāo)志會(huì)得到用這些 C 函數(shù)來(lái)處理 \w 后的編譯對(duì)象;這會(huì)更慢,但也會(huì)象你希望的那樣可以用 \w+ 來(lái)匹配法文文本。

 

M (MULTILINE)

(此時(shí) ^ 和 $ 不會(huì)被解釋; 它們將在 4.1 節(jié)被介紹.)

使用 "^" 只匹配字符串的開始,而 $ 則只匹配字符串的結(jié)尾和直接在換行前(如果有的話)的字符串結(jié)尾。當(dāng)本標(biāo)志指定后, "^" 匹配字符串的開始和字符串中每行的開始。同樣的, $ 元字符匹配字符串結(jié)尾和字符串中每行的結(jié)尾(直接在每個(gè)換行之前)。

 

S (DOTALL)

使 "." 特殊字符完全匹配任何字符,包括換行;沒有這個(gè)標(biāo)志, "." 匹配除了換行外的任何字符。

 

X (VERBOSE)

該標(biāo)志通過(guò)給予你更靈活的格式以便你將正則表達(dá)式寫得更易于理解。當(dāng)該標(biāo)志被指定時(shí),在 RE 字符串中的空白符被忽略,除非該空白符在字符類中或在反斜杠之后;這可以讓你更清晰地組織和縮進(jìn) RE。它也可以允許你將注釋寫入 RE,這些注釋會(huì)被引擎忽略;注釋用 "#"號(hào) 來(lái)標(biāo)識(shí),不過(guò)該符號(hào)不能在字符串或反斜杠之后。


舉個(gè)例子,這里有一個(gè)使用 re.VERBOSE 的 RE;看看讀它輕松了多少?

#!python
charref = re.compile(r"""&[[]]		   # Start of a numeric entity reference|||here has wrong.i can't fix
(
[0-9]+[^0-9]      # Decimal form
| 0[0-7]+[^0-7]   # Octal form
| x[0-9a-fA-F]+[^0-9a-fA-F] # Hexadecimal form
)
""", re.VERBOSE)

沒有 verbose 設(shè)置, RE 會(huì)看起來(lái)象這樣:

#!python
charref = re.compile("&#([0-9]+[^0-9]"
"|0[0-7]+[^0-7]"
"|x[0-9a-fA-F]+[^0-9a-fA-F])")

在上面的例子里,Python 的字符串自動(dòng)連接可以用來(lái)將 RE 分成更小的部分,但它比用 re.VERBOSE 標(biāo)志時(shí)更難懂

 

更多模式功能

到目前為止,我們只展示了正則表達(dá)式的一部分功能。在本節(jié),我們將展示一些新的元字符和如何使用組來(lái)檢索被匹配的文本部分。

還有一些我們還沒展示的元字符,其中的大部分將在本節(jié)展示。


剩下來(lái)要討論的一部分元字符是零寬界定符(zero-width assertions)。它們并不會(huì)使引擎在處理字符串時(shí)更快;相反,它們根本就沒有對(duì)應(yīng)任何字符,只是簡(jiǎn)單的成功或失敗。舉個(gè)例子, \b 是一個(gè)在單詞邊界定位當(dāng)前位置的界定符(assertions),這個(gè)位置根本就不會(huì)被 \b 改變。這意味著零寬界定符(zero-width assertions)將永遠(yuǎn)不會(huì)被重復(fù),因?yàn)槿绻鼈冊(cè)诮o定位置匹配一次,那么它們很明顯可以被匹配無(wú)數(shù)次。

|

可選項(xiàng),或者 "or" 操作符。如果 A 和 B 是正則表達(dá)式,A|B 將匹配任何匹配了 "A" 或 "B" 的字符串。| 的優(yōu)先級(jí)非常低,是為了當(dāng)你有多字符串要選擇時(shí)能適當(dāng)?shù)剡\(yùn)行。Crow|Servo 將匹配"Crow" 或 "Servo", 而不是 "Cro", 一個(gè) "w" 或 一個(gè) "S", 和 "ervo"。

為了匹配字母 "|",可以用 \|,或?qū)⑵浒谧址愔?,如[|]。

 

^

匹配行首。除非設(shè)置 MULTILINE 標(biāo)志,它只是匹配字符串的開始。在 MULTILINE 模式里,它也可以直接匹配字符串中的每個(gè)換行。


例如,如果你只希望匹配在行首單詞 "From",那么 RE 將用 ^From。

#!python
>>> print re.search('^From', 'From Here to Eternity')
<re.MatchObject instance at 80c1520>
>>> print re.search('^From', 'Reciting From Memory')
None

$

匹配行尾,行尾被定義為要么是字符串尾,要么是一個(gè)換行字符后面的任何位置。

#!python
>>> print re.search('}$', '{block}')
<re.MatchObject instance at 80adfa8>
>>> print re.search('}$', '{block} ')
None
>>> print re.search('}$', '{block}\n')
<re.MatchObject instance at 80adfa8>

匹配一個(gè) "$",使用 \$ 或?qū)⑵浒谧址愔?,如[$]。

\A

只匹配字符串首。當(dāng)不在 MULTILINE 模式,\A 和 ^ 實(shí)際上是一樣的。然而,在 MULTILINE 模式里它們是不同的;\A 只是匹配字符串首,而 ^ 還可以匹配在換行符之后字符串的任何位置。

\Z

Matches only at the end of the string. 
只匹配字符串尾。

\b

單詞邊界。這是個(gè)零寬界定符(zero-width assertions)只用以匹配單詞的詞首和詞尾。單詞被定義為一個(gè)字母數(shù)字序列,因此詞尾就是用空白符或非字母數(shù)字符來(lái)標(biāo)示的。


下面的例子只匹配 "class" 整個(gè)單詞;而當(dāng)它被包含在其他單詞中時(shí)不匹配。

#!python
>>> p = re.compile(r'\bclass\b')
>>> print p.search('no class at all')
<re.MatchObject instance at 80c8f28>
>>> print p.search('the declassified algorithm')
None
>>> print p.search('one subclass is')
None

當(dāng)用這個(gè)特殊序列時(shí)你應(yīng)該記住這里有兩個(gè)微妙之處。第一個(gè)是 Python 字符串和正則表達(dá)式之間最常見的沖突,在 Python 字符串里,"\b" 是反斜杠字符,ASCII值是8。如果你沒有使用 raw 字符串時(shí),那么 Python 將會(huì)把 "\b" 轉(zhuǎn)換成一個(gè)回退符,你的 RE 將無(wú)法像你希望的那樣匹配它了。下面的例子看起來(lái)和我們前面的 RE 一樣,但在 RE 字符串前少了一個(gè) "r" 。

#!python
>>> p = re.compile('\bclass\b')
>>> print p.search('no class at all')
None
>>> print p.search('\b' + 'class' + '\b')
<re.MatchObject instance at 80c3ee0>

第二個(gè)在字符類中,這個(gè)限定符(assertion)不起作用,\b 表示回退符,以便與 Python 字符串兼容。

\B

另一個(gè)零寬界定符(zero-width assertions),它正好同 \b 相反,只在當(dāng)前位置不在單詞邊界時(shí)匹配。例如:

 

#!python
>>> p = re.compile(r'\Bclass\B')
>>> print p.search('the declassified algorithm')
<_sre.SRE_Match object at 0x7f015e8df920>
>>> p = re.compile(r'\bclass\b')
>>> print p.search('the declassified algorithm')
None

 

 

分組

你經(jīng)常需要得到比 RE 是否匹配還要多的信息。正則表達(dá)式常常用來(lái)分析字符串,編寫一個(gè) RE 匹配感興趣的部分并將其分成幾個(gè)小組。舉個(gè)例子,一個(gè) RFC-822 的頭部用 ":" 隔成一個(gè)頭部名和一個(gè)值,這就可以通過(guò)編寫一個(gè)正則表達(dá)式匹配整個(gè)頭部,用一組匹配頭部名,另一組匹配頭部值的方式來(lái)處理。


組是通過(guò) "(" 和 ")" 元字符來(lái)標(biāo)識(shí)的。 "(" 和 ")" 有很多在數(shù)學(xué)表達(dá)式中相同的意思;它們一起把在它們里面的表達(dá)式組成一組。舉個(gè)例子,你可以用重復(fù)限制符,象 *, +, ?, 和 {m,n},來(lái)重復(fù)組里的內(nèi)容,比如說(shuō)(ab)* 將匹配零或更多個(gè)重復(fù)的 "ab"。

#!python
>>> p = re.compile('(ab)*')
>>> print p.match('ababababab').span()
(0, 10)
>>> print p.match('bababababab').span()
(0, 0)
>>> p = re.compile('b(ab)*')
>>> print p.match('bababababab').span()
(0, 11)

組用 "(" 和 ")" 來(lái)指定,并且得到它們匹配文本的開始和結(jié)尾索引;這就可以通過(guò)一個(gè)參數(shù)用 group()、start()、end() 和 span() 來(lái)進(jìn)行檢索。組是從 0 開始計(jì)數(shù)的。組 0 總是存在;它就是整個(gè) RE,所以 `MatchObject` 的方法都把組 0 作為它們?nèi)笔〉膮?shù)。稍后我們將看到怎樣表達(dá)不能得到它們所匹配文本的 span。

#!python
>>> p = re.compile('(a)b')
>>> m = p.match('ab')
>>> m.group()
'ab'
>>> m.group(0)
'ab'

小組是從左向右計(jì)數(shù)的,從1開始。組可以被嵌套。計(jì)數(shù)的數(shù)值可以通過(guò)從左到右計(jì)算打開的括號(hào)數(shù)來(lái)確定。

#!python
>>> p = re.compile('(a(b)c)d')
>>> m = p.match('abcd')
>>> m.group(0)
'abcd'
>>> m.group(1)
'abc'
>>> m.group(2)
'b'

group() 可以一次輸入多個(gè)組號(hào),在這種情況下它將返回一個(gè)包含那些組所對(duì)應(yīng)值的元組。

#!python
>>> m.group(2,1,2)
('b', 'abc', 'b')

The groups() 方法返回一個(gè)包含所有小組字符串的元組,從 1 到 所含的小組號(hào)。

#!python
>>> m.groups()
('abc', 'b')

 

模式中的逆向引用允許你指定先前捕獲組的內(nèi)容,該組也必須在字符串當(dāng)前位置被找到。舉個(gè)例子,如果組 1 的內(nèi)容能夠在當(dāng)前位置找到的話,\1 就成功否則失敗。記住 Python 字符串也是用反斜杠加數(shù)據(jù)來(lái)允許字符串中包含任意字符的,所以當(dāng)在 RE 中使用逆向引用時(shí)確保使用 raw 字符串。

例如,下面的 RE 在一個(gè)字符串中找到成雙的詞。

#!python
>>> p = re.compile(r'(\b\w+)\s+\1')
>>> p.search('Paris in the the spring').group()
'the the'

象這樣只是搜索一個(gè)字符串的逆向引用并不常見 -- 用這種方式重復(fù)數(shù)據(jù)的文本格式并不多見 -- 但你不久就可以發(fā)現(xiàn)它們用在字符串替換上非常有用。

 

無(wú)捕獲組和命名組

精心設(shè)計(jì)的 REs 也許會(huì)用很多組,既可以捕獲感興趣的子串,又可以分組和結(jié)構(gòu)化 RE 本身。在復(fù)雜的 REs 里,追蹤組號(hào)變得困難。有兩個(gè)功能可以對(duì)這個(gè)問(wèn)題有所幫助。它們也都使用正則表達(dá)式擴(kuò)展的通用語(yǔ)法,因此我們來(lái)看看第一個(gè)。


Perl 5 對(duì)標(biāo)準(zhǔn)正則表達(dá)式增加了幾個(gè)附加功能,Python 的 re 模塊也支持其中的大部分。選擇一個(gè)新的單按鍵元字符或一個(gè)以 "\" 開始的特殊序列來(lái)表示新的功能,而又不會(huì)使 Perl 正則表達(dá)式與標(biāo)準(zhǔn)正則表達(dá)式產(chǎn)生混亂是有難度的。如果你選擇 "&" 做為新的元字符,舉個(gè)例子,老的表達(dá)式認(rèn)為 "&" 是一個(gè)正常的字符,而不會(huì)在使用 \& 或 [&] 時(shí)也不會(huì)轉(zhuǎn)義。


Perl 開發(fā)人員的解決方法是使用 (?...) 來(lái)做為擴(kuò)展語(yǔ)法。"?" 在括號(hào)后面會(huì)直接導(dǎo)致一個(gè)語(yǔ)法錯(cuò)誤,因?yàn)?"?" 沒有任何字符可以重復(fù),因此它不會(huì)產(chǎn)生任何兼容問(wèn)題。緊隨 "?" 之后的字符指出擴(kuò)展的用途,因此 (?=foo)


Python 新增了一個(gè)擴(kuò)展語(yǔ)法到 Perl 擴(kuò)展語(yǔ)法中。如果在問(wèn)號(hào)后的第一個(gè)字符是 "P",你就可以知道它是針對(duì) Python 的擴(kuò)展。目前有兩個(gè)這樣的擴(kuò)展: (?P<name>...) 定義一個(gè)命名組,(?P=name) 則是對(duì)命名組的逆向引用。如果 Perl 5 的未來(lái)版本使用不同的語(yǔ)法增加了相同的功能,那么 re 模塊也將改變以支持新的語(yǔ)法,與此同時(shí)為了兼容性的目的而繼續(xù)保持的 Python 專用語(yǔ)法。


現(xiàn)在我們看一下普通的擴(kuò)展語(yǔ)法,我們回過(guò)頭來(lái)簡(jiǎn)化在復(fù)雜 REs 中使用組運(yùn)行的特性。因?yàn)榻M是從左到右編號(hào)的,而且一個(gè)復(fù)雜的表達(dá)式也許會(huì)使用許多組,它可以使跟蹤當(dāng)前組號(hào)變得困難,而修改如此復(fù)雜的 RE 是十分麻煩的。在開始時(shí)插入一個(gè)新組,你可以改變它之后的每個(gè)組號(hào)。


首先,有時(shí)你想用一個(gè)組去收集正則表達(dá)式的一部分,但又對(duì)組的內(nèi)容不感興趣。你可以用一個(gè)無(wú)捕獲組: (?:...) 來(lái)實(shí)現(xiàn)這項(xiàng)功能,這樣你可以在括號(hào)中發(fā)送任何其他正則表達(dá)式。

#!python
>>> m = re.match("([abc])+", "abc")
>>> m.groups()
('c',)
>>> m = re.match("(?:[abc])+", "abc")
>>> m.groups()
()

除了捕獲匹配組的內(nèi)容之外,無(wú)捕獲組與捕獲組表現(xiàn)完全一樣;你可以在其中放置任何字符,可以用重復(fù)元字符如 "*" 來(lái)重復(fù)它,可以在其他組(無(wú)捕獲組與捕獲組)中嵌套它。(?:...) 對(duì)于修改已有組尤其有用,因?yàn)槟憧梢圆挥酶淖兯衅渌M號(hào)的情況下添加一個(gè)新組。捕獲組和無(wú)捕獲組在搜索效率方面也沒什么不同,沒有哪一個(gè)比另一個(gè)更快。


其次,更重要和強(qiáng)大的是命名組;與用數(shù)字指定組不同的是,它可以用名字來(lái)指定。


命令組的語(yǔ)法是 Python 專用擴(kuò)展之一: (?P<name>...)。名字很明顯是組的名字,除了該組有個(gè)名字之外,命名組也同捕獲組是相同的。`MatchObject` 的方法處理捕獲組時(shí)接受的要么是表示組號(hào)的整數(shù),要么是包含組名的字符串。命名組也可以是數(shù)字,所以你可以通過(guò)兩種方式來(lái)得到一個(gè)組的信息:

#!python
>>> p = re.compile(r'(?P<word>\b\w+\b)')
>>> m = p.search( '(((( Lots of punctuation )))' )
>>> m.group('word')
'Lots'
>>> m.group(1)
'Lots'

命名組是便于使用的,因?yàn)樗梢宰屇闶褂萌菀子涀〉拿謥?lái)代替不得不記住的數(shù)字。這里有一個(gè)來(lái)自 imaplib 模塊的 RE 示例:

#!python
InternalDate = re.compile(r'INTERNALDATE "'
r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-'
	r'(?P<year>[0-9][0-9][0-9][0-9])'
r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'
r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'
r'"')

很明顯,得到 m.group('zonem') 要比記住得到組 9 要容易得多。


因?yàn)槟嫦蛞玫恼Z(yǔ)法,象 (...)\1 這樣的表達(dá)式所表示的是組號(hào),這時(shí)用組名代替組號(hào)自然會(huì)有差別。還有一個(gè) Python 擴(kuò)展:(?P=name) ,它可以使叫 name 的組內(nèi)容再次在當(dāng)前位置發(fā)現(xiàn)。正則表達(dá)式為了找到重復(fù)的單詞,(\b\w+)\s+\1 也可以被寫成 (?P<word>\b\w+)\s+(?P=word):

#!python
>>> p = re.compile(r'(?P<word>\b\w+)\s+(?P=word)')
>>> p.search('Paris in the the spring').group()
'the the'


方法/屬性 作用
split() 將字符串在 RE 匹配的地方分片并生成一個(gè)列表,
sub() 找到 RE 匹配的所有子串,并將其用一個(gè)不同的字符串替換
subn() 與 sub() 相同,但返回新的字符串和替換次數(shù)


語(yǔ)法說(shuō)明示例
. 匹配除換行符 \n 以外的任意字符 b.c 匹配 bac,bdc
* 匹配前一個(gè)字符 0 次或多次 b*c 匹配 c,或者 bbbc
+ 匹配前一個(gè)字符 1 次或多次 b+c 匹配 bc 或者 bbbc
? 匹配前一個(gè)字符 0 或 1 次 b?c 匹配 c 或者 bc
{m} 匹配前一個(gè)字符 m 次 b{2}c 匹配 bbc
{m,n} 匹配前一個(gè)字符 m 至 n 次 b{2,5}c 匹配 bbc 或者 bbbbc
[abc] 匹配 [] 內(nèi)的任意字符 [bc] 匹配 b 或者 c
\d 匹配數(shù)字 [0-9] b\dc 匹配 b1c 等
\D 匹配非數(shù)字,等價(jià)于 [^\d] b\Dc 匹配 bAc
\s 匹配空白字符 b\sc 匹配 b c
\S 匹配非空白字符 [\^s] b\Sc 匹配 bac
\w 匹配 [A-Za-z0-9_] b\wc 匹配 bAc 等
\W 等價(jià)于 [^\w] b\Wc 匹配 b c
\ 轉(zhuǎn)義字符, b\\c 匹配 b\c
^ 匹配字符串開頭 ^bc 匹配句首的 bc
$ 匹配字符串末尾 bc$ 匹配以 bc 結(jié)尾的字符串
\A 僅匹配字符串開頭 \Abc 匹配字符串開頭的 bc
\Z 僅僅匹配字符串末尾 bc\Z 匹配字符串末尾的 bc
| 匹配左右表達(dá)式任意一個(gè) b|c 匹配 b 或者 c

 

 

 

 

re模塊實(shí)例

2.1. 開始使用re

Python通過(guò)re模塊提供對(duì)正則表達(dá)式的支持。使用re的一般步驟是先將正則表達(dá)式的字符串形式編譯為Pattern實(shí)例,然后使用Pattern實(shí)例處理文本并獲得匹配結(jié)果(一個(gè)Match實(shí)例),最后使用Match實(shí)例獲得信息,進(jìn)行其他的操作。











import re
 
p = re.compile(r'(\w+) (\w+)')
s = 'i say, hello world!'
 
print p.subn(r'\2 \1', s)
 
def func(m):
    return m.group(1).title() + ' ' + m.group(2).title()
 
print p.subn(func, s)
 
### output ###
# ('say i, world hello!', 2)
# ('I Say, Hello World!', 2)

“python正則表達(dá)式的用法”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI