您好,登錄后才能下訂單哦!
本篇內容介紹了“python正則表達式的用法”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
簡單模式
我們將從最簡單的正則表達式學習開始。由于正則表達式常用于字符串操作,那我們就從最常見的任務:字符匹配 下手。
有關正則表達式底層的計算機科學上的詳細解釋(確定性和非確定性有限自動機),你可以查閱編寫編譯器相關的任何教科書。
字符匹配
大多數字母和字符一般都會和自身匹配。例如,正則表達式 test 會和字符串“test”完全匹配。(你也可以使用大小寫不敏感模式,它還能讓這個 RE 匹配“Test”或“TEST”;稍后會有更多解釋。)
這個規則當然會有例外;有些字符比較特殊,它們和自身并不匹配,而是會表明應和一些特殊的東西匹配,或者它們會影響到 RE 其它部分的重復次數。本文很大篇幅專門討論了各種元字符及其作用。
這里有一個元字符的完整列表;其含義會在本指南余下部分進行討論。
. ^ $ * + ? { [ ] \ | ( )
我們首先考察的元字符是"[" 和 "]"。它們常用來指定一個字符類別,所謂字符類別就是你想匹配的一個字符集。字符可以單個列出,也可以用“-”號分隔的兩個給定字符來表示一個字符區間。例如,[abc] 將匹配"a", "b", 或 "c"中的任意一個字符;也可以用區間[a-c]來表示同一字符集,和前者效果一致。如果你只想匹配小寫字母,那么 RE 應寫成 [a-z].
1)元字符在類別里并不起作用。例如:[akm$]將匹配字符"a", "k", "m", 或 "$" 中的任意一個;"$"通常用作元字符,但在字符類別里,其特性被除去,恢復成普通字符。
2)補集來匹配不在區間范圍內的字符。其做法是把"^"作為類別的首個字符;其它地方的"^"只會簡單匹配 "^"字符本身。例如,[^5] 將匹配除 "5" 之外的任意字符。
3)元字符反斜杠"\"。 做為 Python 中的字符串字母,反斜杠后面可以加不同的字符以表示不同特殊意義。它也可以用于取消所有的元字符,這樣你就可以在模式中匹配它們了。舉個例子,如果你需要匹配字符 "[" 或 "\",你可以在它們之前用反斜杠來取消它們的特殊意義: \[ 或 \\。
一些用 "\" 開始的特殊字符所表示的預定義字符集通常是很有用的,象數字集,字母集,或其它非空字符集。下列是可用的預設特殊字符:
\d 匹配任何十進制數;它相當于類 [0-9]。 \D 匹配任何非數字字符;它相當于類 [^0-9]。 \s 匹配任何空白字符;它相當于類 [ \t\n\r\f\v]。 \S 匹配任何非空白字符;它相當于類 [^ \t\n\r\f\v]。 \w 匹配任何字母數字字符;它相當于類 [a-zA-Z0-9_]。 \W 匹配任何非字母數字字符;它相當于類 [^a-zA-Z0-9_]。
這樣特殊字符都可以包含在一個字符類中。如,[\s,.]字符類將匹配任何空白字符或","或"."。
4)元字符是 . 。它匹配除了換行字符外的任何字符,在 alternate 模式(re.DOTALL)下它甚至可以匹配換行。"." 通常被用于你想匹配“任何字符”的地方。
重復
正則表達式第一件能做的事是能夠匹配不定長的字符集,而這是其它能作用在字符串上的方法所不能做到的。 不過,如果那是正則表達式唯一的附加功能的話,那么它們也就不那么優秀了。它們的另一個功能就是你可以指定正則表達式的一部分的重復次數。
我們討論的第一個重復功能的元字符是 *。* 并不匹配字母字符 "*";相反,它指定前一個字符可以被匹配零次或更多次,而不是只有一次。
舉個例子,ca*t 將匹配 "ct" (0 個 "a" 字符), "cat" (1 個 "a"), "caaat" (3 個 "a" 字符)等等。RE 引擎有各種來自 C 的整數類型大小的內部限制,以防止它匹配超過20億個 "a" 字符;你也許沒有足夠的內存去建造那么大的字符串,所以將不會累計到那個限制。
象 * 這樣地重復是“貪婪的”;當重復一個 RE 時,匹配引擎會試著重復盡可能多的次數。如果模式的后面部分沒有被匹配,匹配引擎將退回并再次嘗試更小的重復。
一步步的示例可以使它更加清晰。讓我們考慮表達式 a[bcd]*b。它匹配字母 "a",零個或更多個來自類 [bcd]中的字母,最后以 "b" 結尾。現在想一想該 RE 對字符串 "abcbd" 的匹配。
Step | Matched | Explanation |
1 | a | a 匹配模式 |
2 | abcbd | 引擎匹配 [bcd]*,并盡其所能匹配到字符串的結尾 |
3 | Failure | 引擎嘗試匹配 b,但當前位置已經是字符的最后了,所以失敗 |
4 | abcb | 退回,[bcd]*嘗試少匹配一個字符。 |
5 | Failure | 再次嘗次b,但在當前最后一位字符是"d"。 |
6 | abc | 再次退回,[bcd]*只匹配 "bc"。 |
7 | abcb | 再次嘗試 b ,這次當前位上的字符正好是 "b" |
RE 的結尾部分現在可以到達了,它匹配 "abcb"。這證明了匹配引擎一開始會盡其所能進行匹配,如果沒有匹配然后就逐步退回并反復嘗試 RE 剩下來的部分。直到它退回嘗試匹配 [bcd] 到零次為止,如果隨后還是失敗,那么引擎就會認為該字符串根本無法匹配 RE 。
另一個重復元字符是 +,表示匹配一或更多次。請注意 * 和 + 之間的不同;* 匹配零或更多次,所以可以根本就不出現,而 + 則要求至少出現一次。用同一個例子,ca+t 就可以匹配 "cat" (1 個 "a"), "caaat" (3 個 "a"), 但不能匹配 "ct"。
還有更多的限定符。問號 ? 匹配一次或零次;你可以認為它用于標識某事物是可選的。例如:home-?brew 匹配 "homebrew" 或 "home-brew"。
最復雜的重復限定符是 {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億,但這也許同無窮大一樣。
細心的讀者也許注意到其他三個限定符都可以用這樣方式來表示。 {0,} 等同于 *,{1,} 等同于 +,而{0,1}則與 ? 相同。如果可以的話,最好使用 *,+,或?。很簡單因為它們更短也更容易懂。
使用正則表達式
現在我們已經看了一些簡單的正則表達式,那么我們實際在 Python 中是如何使用它們的呢? re 模塊提供了一個正則表達式引擎的接口,可以讓你將 REs 編譯成對象并用它們來進行匹配。
編譯正則表達式
正則表達式被編譯成 `RegexObject` 實例,可以為不同的操作提供方法,如模式匹配搜索或字符串替換。
#!python >>> import re >>> p = re.compile('ab*') >>> print p <_sre.SRE_Pattern object at 0xb76e1a70>
re.compile() 也接受可選的標志參數,常用來實現不同的特殊功能和語法變更。我們稍后將查看所有可用的設置,但現在只舉一個例子:
#!python >>> p = re.compile('ab*', re.IGNORECASE)
RE 被做為一個字符串發送給 re.compile()。REs 被處理成字符串是因為正則表達式不是 Python 語言的核心部分,也沒有為它創建特定的語法。(應用程序根本就不需要 REs,因此沒必要包含它們去使語言說明變得臃腫不堪。)而 re 模塊則只是以一個 C 擴展模塊的形式來被 Python 包含,就象 socket 或 zlib 模塊一樣
將 REs 作為字符串以保證 Python 語言的簡潔,但這樣帶來的一個麻煩就是象下節標題所講的。
反斜杠的麻煩
在早期規定中,正則表達式用反斜杠字符 ("\") 來表示特殊格式或允許使用特殊字符而不調用它的特殊用法。這就與 Python 在字符串中的那些起相同作用的相同字符產生了沖突。
讓我們舉例說明,你想寫一個 RE 以匹配字符串 "\section",可能是在一個 LATEX 文件查找。為了要在程序代碼中判斷,首先要寫出想要匹配的字符串。接下來你需要在所有反斜杠和其它元字符前加反斜杠來取消其特殊意義,結果要匹配的字符串就成了"\\section"。 當把這個字符串傳遞給re.compile()時必須還是"\\section"。然而,作為Python的字符串實值(string literals)來表示的話,"\\section"中兩個反斜杠還要再次取消特殊意義,最后結果就變成了"\\\\section"。
字符 | 階段 |
\section | 要匹配的字符串 |
\\section | 為 re.compile 取消反斜杠的特殊意義 |
"\\\\section" | 為"\\section"的字符串實值(string literals)取消反斜杠的特殊意義 |
簡單地說,為了匹配一個反斜杠,不得不在 RE 字符串中寫 '\\\\',因為正則表達式中必須是 "\\",而每個反斜杠在常規的 Python 字符串實值中必須表示成 "\\"。在 REs 中反斜杠的這個重復特性會導致大量重復的反斜杠,而且所生成的字符串也很難懂。
解決的辦法就是為正則表達式使用 Python 的 raw 字符串表示;在字符串前加個 "r" 反斜杠就不會被任何特殊方式處理,所以 r"\n" 就是包含"\" 和 "n" 的兩個字符,而 "\n" 則是一個字符,表示一個換行。正則表達式通常在 Python 代碼中都是用這種 raw 字符串表示。
常規字符串 | Raw 字符串 |
"ab*" | r"ab*" |
"\\\\section" | r"\\section" |
"\\w+\\s+\\1" | r"\w+\s+\1" |
執行匹配
一旦你有了已經編譯了的正則表達式的對象,你要用它做什么呢?`RegexObject` 實例有一些方法和屬性。這里只顯示了最重要的幾個,如果要看完整的列表請查閱 Python Library Reference
方法/屬性 | 作用 |
match() | 決定 RE 是否在字符串剛開始的位置匹配 |
search() | 掃描字符串,找到這個 RE 匹配的位置 |
findall() | 找到 RE 匹配的所有子串,并把它們作為一個列表返回 |
finditer() | 找到 RE 匹配的所有子串,并把它們作為一個迭代器返回 |
如果沒有匹配到的話,match() 和 search() 將返回 None。如果成功的話,就會返回一個 `MatchObject` 實例,其中有這次匹配的信息:它是從哪里開始和結束,它所匹配的子串等等。
你可以用采用人機對話并用 re 模塊實驗的方式來學習它。如果你有 Tkinter 的話,你也許可以考慮參考一下 Tools/scripts/redemo.py,一個包含在 Python 發行版里的示范程序。
首先,運行 Python 解釋器,導入 re 模塊并編譯一個 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>
現在,你可以試著用 RE 的 [a-z]+ 去匹配不同的字符串。一個空字符串將根本不能匹配,因為 + 的意思是 “一個或更多的重復次數”。 在這種情況下 match() 將返回 None,因為它使解釋器沒有輸出。你可以明確地打印出 match() 的結果來弄清這一點。
#!python >>> p.match("") >>> print p.match("") None
現在,讓我們試著用它來匹配一個字符串,如 "tempo"。這時,match() 將返回一個 MatchObject。因此你可以將結果保存在變量里以便後面使用。
#!python >>> m = p.match( 'tempo') >>> print m <_sre.SRE_Match object at 80c4f68>
現在你可以查詢 `MatchObject` 關于匹配字符串的相關信息了。MatchObject 實例也有幾個方法和屬性;最重要的那些如下所示:
方法/屬性 | 作用 |
group() | 返回被 RE 匹配的字符串 |
start() | 返回匹配開始的位置 |
end() | 返回匹配結束的位置 |
span() | 返回一個元組包含匹配 (開始,結束) 的位置 |
試試這些方法不久就會清楚它們的作用了:
#!python >>> m.group() 'tempo' >>> m.start(), m.end() (0, 5) >>> m.span() (0, 5)
group() 返回 RE 匹配的子串。start() 和 end() 返回匹配開始和結束時的索引。span() 則用單個元組把開始和結束時的索引一起返回。因為匹配方法檢查到如果 RE 在字符串開始處開始匹配,那么 start() 將總是為零。然而, `RegexObject` 實例的 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)
在實際程序中,最常見的作法是將 `MatchObject` 保存在一個變量里,然後檢查它是否為 None,通常如下所示:
#!python p = re.compile( ... ) m = p.match( 'string goes here' ) if m: print 'Match found: ', m.group() else: print 'No match'
兩個 `RegexObject` 方法返回所有匹配模式的子串。findall()返回一個匹配字符串行表:
#!python >>> p = re.compile('\d+') >>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping') ['12', '11', '10']
findall() 在它返回結果時不得不創建一個列表。在 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)
標志 | 含義 |
DOTALL, S | 使 . 匹配包括換行在內的所有字符 |
IGNORECASE, I | 使匹配對大小寫不敏感 |
LOCALE, L | 做本地化識別(locale-aware)匹配 |
MULTILINE, M | 多行匹配,影響 ^ 和 $ |
VERBOSE, X | 能夠使用 REs 的 verbose 狀態,使之被組織得更清晰易懂 |
I (IGNORECASE)
使匹配對大小寫不敏感;字符類和字符串匹配字母時忽略大小寫。舉個例子,[A-Z]也可以匹配小寫字母,Spam 可以匹配 "Spam", "spam", 或 "spAM"。這個小寫字母并不考慮當前位置。
L (LOCALE)
影響 \w, \W, \b, 和 \B,這取決于當前的本地化設置。
locales 是 C 語言庫中的一項功能,是用來為需要考慮不同語言的編程提供幫助的。舉個例子,如果你正在處理法文文本,你想用 \w+ 來匹配文字,但 \w 只匹配字符類 [A-Za-z];它并不能匹配 "é" 或 "?"。如果你的系統配置適當且本地化設置為法語,那么內部的 C 函數將告訴程序 "é" 也應該被認為是一個字母。當在編譯正則表達式時使用 LOCALE 標志會得到用這些 C 函數來處理 \w 后的編譯對象;這會更慢,但也會象你希望的那樣可以用 \w+ 來匹配法文文本。
M (MULTILINE)
(此時 ^ 和 $ 不會被解釋; 它們將在 4.1 節被介紹.)
使用 "^" 只匹配字符串的開始,而 $ 則只匹配字符串的結尾和直接在換行前(如果有的話)的字符串結尾。當本標志指定后, "^" 匹配字符串的開始和字符串中每行的開始。同樣的, $ 元字符匹配字符串結尾和字符串中每行的結尾(直接在每個換行之前)。
S (DOTALL)
使 "." 特殊字符完全匹配任何字符,包括換行;沒有這個標志, "." 匹配除了換行外的任何字符。
X (VERBOSE)
該標志通過給予你更靈活的格式以便你將正則表達式寫得更易于理解。當該標志被指定時,在 RE 字符串中的空白符被忽略,除非該空白符在字符類中或在反斜杠之后;這可以讓你更清晰地組織和縮進 RE。它也可以允許你將注釋寫入 RE,這些注釋會被引擎忽略;注釋用 "#"號 來標識,不過該符號不能在字符串或反斜杠之后。
舉個例子,這里有一個使用 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 設置, RE 會看起來象這樣:
#!python charref = re.compile("&#([0-9]+[^0-9]" "|0[0-7]+[^0-7]" "|x[0-9a-fA-F]+[^0-9a-fA-F])")
在上面的例子里,Python 的字符串自動連接可以用來將 RE 分成更小的部分,但它比用 re.VERBOSE 標志時更難懂
更多模式功能
到目前為止,我們只展示了正則表達式的一部分功能。在本節,我們將展示一些新的元字符和如何使用組來檢索被匹配的文本部分。
還有一些我們還沒展示的元字符,其中的大部分將在本節展示。
剩下來要討論的一部分元字符是零寬界定符(zero-width assertions)。它們并不會使引擎在處理字符串時更快;相反,它們根本就沒有對應任何字符,只是簡單的成功或失敗。舉個例子, \b 是一個在單詞邊界定位當前位置的界定符(assertions),這個位置根本就不會被 \b 改變。這意味著零寬界定符(zero-width assertions)將永遠不會被重復,因為如果它們在給定位置匹配一次,那么它們很明顯可以被匹配無數次。
|
可選項,或者 "or" 操作符。如果 A 和 B 是正則表達式,A|B 將匹配任何匹配了 "A" 或 "B" 的字符串。| 的優先級非常低,是為了當你有多字符串要選擇時能適當地運行。Crow|Servo 將匹配"Crow" 或 "Servo", 而不是 "Cro", 一個 "w" 或 一個 "S", 和 "ervo"。
為了匹配字母 "|",可以用 \|,或將其包含在字符類中,如[|]。
^
匹配行首。除非設置 MULTILINE 標志,它只是匹配字符串的開始。在 MULTILINE 模式里,它也可以直接匹配字符串中的每個換行。
例如,如果你只希望匹配在行首單詞 "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
$
匹配行尾,行尾被定義為要么是字符串尾,要么是一個換行字符后面的任何位置。
#!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>
匹配一個 "$",使用 \$ 或將其包含在字符類中,如[$]。
\A
只匹配字符串首。當不在 MULTILINE 模式,\A 和 ^ 實際上是一樣的。然而,在 MULTILINE 模式里它們是不同的;\A 只是匹配字符串首,而 ^ 還可以匹配在換行符之后字符串的任何位置。
\Z
Matches only at the end of the string.
只匹配字符串尾。
\b
單詞邊界。這是個零寬界定符(zero-width assertions)只用以匹配單詞的詞首和詞尾。單詞被定義為一個字母數字序列,因此詞尾就是用空白符或非字母數字符來標示的。
下面的例子只匹配 "class" 整個單詞;而當它被包含在其他單詞中時不匹配。
#!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
當用這個特殊序列時你應該記住這里有兩個微妙之處。第一個是 Python 字符串和正則表達式之間最常見的沖突,在 Python 字符串里,"\b" 是反斜杠字符,ASCII值是8。如果你沒有使用 raw 字符串時,那么 Python 將會把 "\b" 轉換成一個回退符,你的 RE 將無法像你希望的那樣匹配它了。下面的例子看起來和我們前面的 RE 一樣,但在 RE 字符串前少了一個 "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>
第二個在字符類中,這個限定符(assertion)不起作用,\b 表示回退符,以便與 Python 字符串兼容。
\B
另一個零寬界定符(zero-width assertions),它正好同 \b 相反,只在當前位置不在單詞邊界時匹配。例如:
#!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
分組
你經常需要得到比 RE 是否匹配還要多的信息。正則表達式常常用來分析字符串,編寫一個 RE 匹配感興趣的部分并將其分成幾個小組。舉個例子,一個 RFC-822 的頭部用 ":" 隔成一個頭部名和一個值,這就可以通過編寫一個正則表達式匹配整個頭部,用一組匹配頭部名,另一組匹配頭部值的方式來處理。
組是通過 "(" 和 ")" 元字符來標識的。 "(" 和 ")" 有很多在數學表達式中相同的意思;它們一起把在它們里面的表達式組成一組。舉個例子,你可以用重復限制符,象 *, +, ?, 和 {m,n},來重復組里的內容,比如說(ab)* 將匹配零或更多個重復的 "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)
組用 "(" 和 ")" 來指定,并且得到它們匹配文本的開始和結尾索引;這就可以通過一個參數用 group()、start()、end() 和 span() 來進行檢索。組是從 0 開始計數的。組 0 總是存在;它就是整個 RE,所以 `MatchObject` 的方法都把組 0 作為它們缺省的參數。稍后我們將看到怎樣表達不能得到它們所匹配文本的 span。
#!python >>> p = re.compile('(a)b') >>> m = p.match('ab') >>> m.group() 'ab' >>> m.group(0) 'ab'
小組是從左向右計數的,從1開始。組可以被嵌套。計數的數值可以通過從左到右計算打開的括號數來確定。
#!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() 可以一次輸入多個組號,在這種情況下它將返回一個包含那些組所對應值的元組。
#!python >>> m.group(2,1,2) ('b', 'abc', 'b')
The groups() 方法返回一個包含所有小組字符串的元組,從 1 到 所含的小組號。
#!python >>> m.groups() ('abc', 'b')
模式中的逆向引用允許你指定先前捕獲組的內容,該組也必須在字符串當前位置被找到。舉個例子,如果組 1 的內容能夠在當前位置找到的話,\1 就成功否則失敗。記住 Python 字符串也是用反斜杠加數據來允許字符串中包含任意字符的,所以當在 RE 中使用逆向引用時確保使用 raw 字符串。
例如,下面的 RE 在一個字符串中找到成雙的詞。
#!python >>> p = re.compile(r'(\b\w+)\s+\1') >>> p.search('Paris in the the spring').group() 'the the'
象這樣只是搜索一個字符串的逆向引用并不常見 -- 用這種方式重復數據的文本格式并不多見 -- 但你不久就可以發現它們用在字符串替換上非常有用。
無捕獲組和命名組
精心設計的 REs 也許會用很多組,既可以捕獲感興趣的子串,又可以分組和結構化 RE 本身。在復雜的 REs 里,追蹤組號變得困難。有兩個功能可以對這個問題有所幫助。它們也都使用正則表達式擴展的通用語法,因此我們來看看第一個。
Perl 5 對標準正則表達式增加了幾個附加功能,Python 的 re 模塊也支持其中的大部分。選擇一個新的單按鍵元字符或一個以 "\" 開始的特殊序列來表示新的功能,而又不會使 Perl 正則表達式與標準正則表達式產生混亂是有難度的。如果你選擇 "&" 做為新的元字符,舉個例子,老的表達式認為 "&" 是一個正常的字符,而不會在使用 \& 或 [&] 時也不會轉義。
Perl 開發人員的解決方法是使用 (?...) 來做為擴展語法。"?" 在括號后面會直接導致一個語法錯誤,因為 "?" 沒有任何字符可以重復,因此它不會產生任何兼容問題。緊隨 "?" 之后的字符指出擴展的用途,因此 (?=foo)
Python 新增了一個擴展語法到 Perl 擴展語法中。如果在問號后的第一個字符是 "P",你就可以知道它是針對 Python 的擴展。目前有兩個這樣的擴展: (?P<name>...) 定義一個命名組,(?P=name) 則是對命名組的逆向引用。如果 Perl 5 的未來版本使用不同的語法增加了相同的功能,那么 re 模塊也將改變以支持新的語法,與此同時為了兼容性的目的而繼續保持的 Python 專用語法。
現在我們看一下普通的擴展語法,我們回過頭來簡化在復雜 REs 中使用組運行的特性。因為組是從左到右編號的,而且一個復雜的表達式也許會使用許多組,它可以使跟蹤當前組號變得困難,而修改如此復雜的 RE 是十分麻煩的。在開始時插入一個新組,你可以改變它之后的每個組號。
首先,有時你想用一個組去收集正則表達式的一部分,但又對組的內容不感興趣。你可以用一個無捕獲組: (?:...) 來實現這項功能,這樣你可以在括號中發送任何其他正則表達式。
#!python >>> m = re.match("([abc])+", "abc") >>> m.groups() ('c',) >>> m = re.match("(?:[abc])+", "abc") >>> m.groups() ()
除了捕獲匹配組的內容之外,無捕獲組與捕獲組表現完全一樣;你可以在其中放置任何字符,可以用重復元字符如 "*" 來重復它,可以在其他組(無捕獲組與捕獲組)中嵌套它。(?:...) 對于修改已有組尤其有用,因為你可以不用改變所有其他組號的情況下添加一個新組。捕獲組和無捕獲組在搜索效率方面也沒什么不同,沒有哪一個比另一個更快。
其次,更重要和強大的是命名組;與用數字指定組不同的是,它可以用名字來指定。
命令組的語法是 Python 專用擴展之一: (?P<name>...)。名字很明顯是組的名字,除了該組有個名字之外,命名組也同捕獲組是相同的。`MatchObject` 的方法處理捕獲組時接受的要么是表示組號的整數,要么是包含組名的字符串。命名組也可以是數字,所以你可以通過兩種方式來得到一個組的信息:
#!python >>> p = re.compile(r'(?P<word>\b\w+\b)') >>> m = p.search( '(((( Lots of punctuation )))' ) >>> m.group('word') 'Lots' >>> m.group(1) 'Lots'
命名組是便于使用的,因為它可以讓你使用容易記住的名字來代替不得不記住的數字。這里有一個來自 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 要容易得多。
因為逆向引用的語法,象 (...)\1 這樣的表達式所表示的是組號,這時用組名代替組號自然會有差別。還有一個 Python 擴展:(?P=name) ,它可以使叫 name 的組內容再次在當前位置發現。正則表達式為了找到重復的單詞,(\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 匹配的地方分片并生成一個列表, |
sub() | 找到 RE 匹配的所有子串,并將其用一個不同的字符串替換 |
subn() | 與 sub() 相同,但返回新的字符串和替換次數 |
語法 | 說明 | 示例 |
---|---|---|
. | 匹配除換行符 \n 以外的任意字符 | b.c 匹配 bac,bdc |
* | 匹配前一個字符 0 次或多次 | b*c 匹配 c,或者 bbbc |
+ | 匹配前一個字符 1 次或多次 | b+c 匹配 bc 或者 bbbc |
? | 匹配前一個字符 0 或 1 次 | b?c 匹配 c 或者 bc |
{m} | 匹配前一個字符 m 次 | b{2}c 匹配 bbc |
{m,n} | 匹配前一個字符 m 至 n 次 | b{2,5}c 匹配 bbc 或者 bbbbc |
[abc] | 匹配 [] 內的任意字符 | [bc] 匹配 b 或者 c |
\d | 匹配數字 [0-9] | b\dc 匹配 b1c 等 |
\D | 匹配非數字,等價于 [^\d] | b\Dc 匹配 bAc |
\s | 匹配空白字符 | b\sc 匹配 b c |
\S | 匹配非空白字符 [\^s] | b\Sc 匹配 bac |
\w | 匹配 [A-Za-z0-9_] | b\wc 匹配 bAc 等 |
\W | 等價于 [^\w] | b\Wc 匹配 b c |
\ | 轉義字符, | b\\c 匹配 b\c |
^ | 匹配字符串開頭 | ^bc 匹配句首的 bc |
$ | 匹配字符串末尾 | bc$ 匹配以 bc 結尾的字符串 |
\A | 僅匹配字符串開頭 | \Abc 匹配字符串開頭的 bc |
\Z | 僅僅匹配字符串末尾 | bc\Z 匹配字符串末尾的 bc |
| | 匹配左右表達式任意一個 | b|c 匹配 b 或者 c |
Python通過re模塊提供對正則表達式的支持。使用re的一般步驟是先將正則表達式的字符串形式編譯為Pattern實例,然后使用Pattern實例處理文本并獲得匹配結果(一個Match實例),最后使用Match實例獲得信息,進行其他的操作。
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正則表達式的用法”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。