python3從零學習-5.1.3、正則表達式re

模式和被搜索的字符串既可以是 Unicode 字符串 (str) ,也可以是8位字節串 (bytes)。 但是,Unicode 字符串與8位字節串不能混用:也就是說,你不能用一個字節串模式去匹配 Unicode 字符串,反之亦然;類似地,當進行替換操作時,替換字符串的類型也必須與所用的模式和搜索字符串的類型一致。

正則表達式使用反斜槓('\')來表示特殊形式,或者把特殊字符轉義成普通字符。 而反斜槓在普通的 Python 字符串裡也有相同的作用,所以就產生了衝突。比如說,要匹配一個字面上的反斜槓,正則表達式模式不得不寫成 '\\\\',因為正則表達式裡匹配一個反斜槓必須是 \\ ,而每個反斜槓在普通的 Python 字符串裡都要寫成 \\ 。

解決辦法是對於正則表達式樣式使用 Python 的原始字符串表示法;在帶有 'r' 前綴的字符串字面值中,反斜槓不必做任何特殊處理。 因此 r"\n" 表示包含 '\' 和 'n' 兩個字符的字符串,而 "\n" 則表示只包含一個換行符的字符串。 樣式在 Python 代碼中通常都會使用這種原始字符串表示法來表示。

絕大部分正則表達式操作都提供為模塊函數和方法,在 編譯正則表達式. 這些函數是一個捷徑,不需要先編譯一個正則對象,但是損失了一些優化參數。

參見

第三方模塊 regex , 提供了與標準庫 re 模塊兼容的API接口, 同時還提供了額外的功能和更全面的Unicode支持。

正則表達式語法

一個正則表達式(或RE)指定了一集與之匹配的字符串;模塊內的函數可以讓你檢查某個字符串是否跟給定的正則表達式匹配(或者一個正則表達式是否匹配到一個字符串,這兩種說法含義相同)。

正則表達式可以拼接; 如果 A 和 B 都是正則表達式, 那麼 AB 也是正則表達式。 通常, 如果字符串 p 匹配 A 並且另一個字符串 q 匹配 B, 那麼 pq 可以匹配 AB。除非 A 或者 B 包含低優先級操作,A 和 B 存在邊界條件;或者命名組引用。所以,複雜表達式可以很容易的從這裡描述的簡單源語表達式構建。 瞭解更多正則表達式理論和實現,參考the Friedl book [Frie09] ,或者其他編譯器構建的書籍。

以下是正則表達式格式的簡要說明。更詳細的信息和演示,參考 正則表達式HOWTO。

正則表達式可以包含普通或者特殊字符。絕大部分普通字符,比如 'A', 'a', 或者 '0',都是最簡單的正則表達式。它們就匹配自身。你可以拼接普通字符,所以 last 匹配字符串 'last'. (在這一節的其他部分,我們將用 this special style 這種方式表示正則表達式,通常不帶引號,要匹配的字符串用 'in single quotes' ,單引號形式。)

有些字符,比如 '|' 或者 '(',屬於特殊字符。 特殊字符既可以表示它的普通含義, 也可以影響它旁邊的正則表達式的解釋。

重複修飾符 (*, +, ?, {m,n}, 等) 不能直接嵌套。這樣避免了非貪婪後綴 ? 修飾符,和其他實現中的修飾符產生的多義性。要應用一個內層重複嵌套,可以使用括號。 比如,表達式 (?:a{6})* 匹配6個 'a' 字符重複任意次數。

特殊字符是:

.(點) 在默認模式,匹配除了換行的任意字符。如果指定了標籤 DOTALL ,它將匹配包括換行符的任意字符。^(插入符號) 匹配字符串的開頭, 並且在 MULTILINE 模式也匹配換行後的首個符號。$匹配字符串尾或者換行符的前一個字符,在 MULTILINE 模式匹配換行符的前一個字符。 foo 匹配 'foo' 和 'foobar' , 但正則 foo$ 只匹配 'foo'。更有趣的是, 在 'foo1\nfoo2\n' 搜索 foo.$ ,通常匹配 'foo2' ,但在 MULTILINE 模式 ,可以匹配到 'foo1' ;在 'foo\n' 搜索 $ 會找到兩個空串:一個在換行前,一個在字符串最後。*對它前面的正則式匹配0到任意次重複, 儘量多的匹配字符串。 ab* 會匹配 'a', 'ab', 或者 'a'``後面跟隨任意個 ``'b'。+對它前面的正則式匹配1到任意次重複。 ab+ 會匹配 'a' 後面跟隨1個以上到任意個 'b',它不會匹配 'a'。?對它前面的正則式匹配0到1次重複。 ab? 會匹配 'a' 或者 'ab'。*?, +?, ??'*', '+',和 '?' 修飾符都是 貪婪的;它們在字符串進行儘可能多的匹配。有時候並不需要這種行為。如果正則式 希望找到 '

  • 字符可以單獨列出,比如 [amk] 匹配 'a', 'm', 或者 'k'。
  • 可以表示字符範圍,通過用 '-' 將兩個字符連起來。比如 [a-z] 將匹配任何小寫ASCII字符, [0-5][0-9] 將匹配從 00 到 59 的兩位數字, [0-9A-Fa-f] 將匹配任何十六進制數位。 如果 - 進行了轉義 (比如 [a\-z])或者它的位置在首位或者末尾(如 [-a] 或 [a-]),它就只表示普通字符 '-'。
  • 特殊字符在集合中,失去它的特殊含義。比如 [(+*)] 只會匹配這幾個文法字符 '(', '+', '*', or ')'。
  • 字符類如 \w 或者 \S (如下定義) 在集合內可以接受,它們可以匹配的字符由 ASCII 或者 LOCALE 模式決定。
  • 不在集合範圍內的字符可以通過 取反 來進行匹配。如果集合首字符是 '^' ,所有 不 在集合內的字符將會被匹配,比如 [^5] 將匹配所有字符,除了 '5', [^^] 將匹配所有字符,除了 '^'. ^ 如果不在集合首位,就沒有特殊含義。
  • 在集合內要匹配一個字符 ']',有兩種方法,要麼就在它之前加上反斜槓,要麼就把它放到集合首位。比如, [()[\]{}] 和 []()[{}] 都可以匹配括號。

|A|B, A 和 B 可以是任意正則表達式,創建一個正則表達式,匹配 A 或者 B. 任意個正則表達式可以用 '|' 連接。它也可以在組合(見下列)內使用。掃描目標字符串時, '|' 分隔開的正則樣式從左到右進行匹配。當一個樣式完全匹配時,這個分支就被接受。意思就是,一旦 A 匹配成功, B 就不再進行匹配,即便它能產生一個更好的匹配。或者說,'|' 操作符絕不貪婪。 如果要匹配 '|' 字符,使用 \|, 或者把它包含在字符集裡,比如 [|].(...)(組合),匹配括號內的任意正則表達式,並標識出組合的開始和結尾。匹配完成後,組合的內容可以被獲取,並可以在之後用 \number 轉義序列進行再次匹配,之後進行詳細說明。要匹配字符 '(' 或者 ')', 用 \( 或 \), 或者把它們包含在字符集合裡: [(], [)].(?…)這是個擴展標記法 (一個 '?' 跟隨 '(' 並無含義)。 '?' 後面的第一個字符決定了這個構建採用什麼樣的語法。這種擴展通常並不創建新的組合; (?P...) 是唯一的例外。 以下是目前支持的擴展。(?aiLmsux)( 'a', 'i', 'L', 'm', 's', 'u', 'x' 中的一個或多個) 這個組合匹配一個空字符串;這些字符對正則表達式設置以下標記 re.A (只匹配ASCII字符), re.I (忽略大小寫), re.L (語言依賴), re.M (多行模式), re.S (點dot匹配全部字符), re.U (Unicode匹配), and re.X (冗長模式)。 (這些標記在 模塊內容 中描述) 如果你想將這些標記包含在正則表達式中,這個方法就很有用,免去了在 re.compile() 中傳遞 flag 參數。標記應該在表達式字符串首位表示。(?:…)正則括號的非捕獲版本。 匹配在括號內的任何正則表達式,但該分組所匹配的子字符串 不能 在執行匹配後被獲取或是之後在模式中被引用。(?imsx-imsx:...)(Zero or more letters from the set 'i', 'm', 's', 'x', optionally followed by '-' followed by one or more letters from the same set.) The letters set or removes the corresponding flags: re.I (ignore case), re.M (multi-line), re.S (dot matches all), and re.X (verbose), for the part of the expression. (The flags are described in 模塊內容.)3.6 新版功能.(?P…)(命名組合)類似正則組合,但是匹配到的子串組在外部是通過定義的 name 來獲取的。組合名必須是有效的Python標識符,並且每個組合名只能用一個正則表達式定義,只能定義一次。一個符號組合同樣是一個數字組合,就像這個組合沒有被命名一樣。命名組合可以在三種上下文中引用。如果樣式是 (?P['"]).*?(?P=quote) (也就是說,匹配單引號或者雙引號括起來的字符串): 引用組合 “quote” 的上下文引用方法 在正則式自身內

  • (?P=quote) (如示)
  • \1

處理匹配對象 m

  • m.group('quote')
  • m.end('quote') (等)

傳遞到 re.sub() 裡的 repl 參數中

  • \g
  • \g<1>
  • \1


(?P=name)反向引用一個命名組合;它匹配前面那個叫 name 的命名組中匹配到的串同樣的字串。(?#…)註釋;裡面的內容會被忽略。(?=…)匹配 … 的內容,但是並不消費樣式的內容。這個叫做 lookahead assertion。比如, Isaac (?=Asimov) 匹配 'Isaac ' 只有在後面是 'Asimov' 的時候。(?!…)匹配 … 不符合的情況。這個叫 negative lookahead assertion (前視取反)。比如說, Isaac (?!Asimov) 只有後面 不 是 'Asimov' 的時候才匹配 'Isaac ' 。(?<=…)匹配字符串的當前位置,它的前面匹配 … 的內容到當前位置。這叫:dfn:positive lookbehind assertion (正向後視斷定)。 (?<=abc)def 會在 'abcdef' 中找到一個匹配,因為後視會往後看3個字符並檢查是否包含匹配的樣式。包含的匹配樣式必須是定長的,意思就是 abc 或 a|b 是允許的,但是 a* 和 a{3,4} 不可以。注意以 positive lookbehind assertions 開始的樣式,如 (?

<=abc)def ,並不是從 a 開始搜索,而是從 d 往回看的。你可能更加願意使用 search() 函數,而不是 match() 函數:>>> import re >>> m = re.search('(?<=abc)def', 'abcdef') >>> m.group(0) 'def'


這個例子搜索一個跟隨在連字符後的單詞:

>>> m = re.search(r'(?<=-)\w+', 'spam-egg')

>>> m.group(0)

'egg'


在 3.5 版更改: 添加定長組合引用的支持。

(?|$) 是一個email樣式匹配,將匹配 '' 或 '[email protected]' ,但不會匹配 ''。由 '\' 和一個字符組成的特殊序列在以下列出。 如果普通字符不是ASCII數位或者ASCII字母,那麼正則樣式將匹配第二個字符。比如,\$ 匹配字符 '$'.\number匹配數字代表的組合。每個括號是一個組合,組合從1開始編號。比如 (.+) \1 匹配 'the the' 或者 '55 55', 但不會匹配 'thethe' (注意組合後面的空格)。這個特殊序列只能用於匹配前面99個組合。如果 number 的第一個數位是0, 或者 number 是三個八進制數,它將不會被看作是一個組合,而是八進制的數字值。在 '[' 和 ']' 字符集合內,任何數字轉義都被看作是字符。\A只匹配字符串開始。\b匹配空字符串,但只在單詞開始或結尾的位置。一個單詞被定義為一個單詞字符的序列。注意,通常 \b 定義為 \w 和 \W 字符之間,或者 \w 和字符串開始/結尾的邊界, 意思就是 r'\bfoo\b' 匹配 'foo', 'foo.', '(foo)', 'bar foo baz' 但不匹配 'foobar' 或者 'foo3'。默認情況下,Unicode字母和數字是在Unicode樣式中使用的,但是可以用 ASCII 標記來更改。如果 LOCALE 標記被設置的話,詞的邊界是由當前語言區域設置決定的,\b 表示退格字符,以便與Python字符串文本兼容。\B匹配空字符串,但 不 能在詞的開頭或者結尾。意思就是 r'py\B' 匹配 'python', 'py3', 'py2', 但不匹配 'py', 'py.', 或者 'py!'. \B 是 \b 的取非,所以Unicode樣式的詞語是由Unicode字母,數字或下劃線構成的,雖然可以用 ASCII 標誌來改變。如果使用了 LOCALE 標誌,則詞的邊界由當前語言區域設置。\d對於 Unicode (str) 樣式:Matches any Unicode decimal digit (that is, any character in Unicode character category [Nd]). This includes [0-9], and also many other digit characters. If the ASCII flag is used only [0-9] is matched (but the flag affects the entire regular expression, so in such cases using an explicit [0-9] may be a better choice).對於8位(bytes)樣式:匹配任何十進制數,就是 [0-9]。\DMatches any character which is not a decimal digit. This is the opposite of \d. If the ASCII flag is used this becomes the equivalent of [^0-9] (but the flag affects the entire regular expression, so in such cases using an explicit [^0-9] may be a better choice).\s對於 Unicode (str) 樣式:Matches Unicode whitespace characters (which includes [ \t\n\r\f\v], and also many other characters, for example the non-breaking spaces mandated by typography rules in many languages). If the ASCII flag is used, only [ \t\n\r\f\v] is matched (but the flag affects the entire regular expression, so in such cases using an explicit [ \t\n\r\f\v] may be a better choice).對於8位(bytes)樣式:匹配ASCII中的空白字符,就是 [ \t\n\r\f\v] 。\SMatches any character which is not a whitespace character. This is the opposite of \s. If the ASCII flag is used this becomes the equivalent of [^ \t\n\r\f\v] (but the flag affects the entire regular expression, so in such cases using an explicit [^ \t\n\r\f\v] may be a better choice).\w對於 Unicode (str) 樣式:Matches Unicode word characters; this includes most characters that can be part of a word in any language, as well as numbers and the underscore. If the ASCII flag is used, only [a-zA-Z0-9_] is matched (but the flag affects the entire regular expression, so in such cases using an explicit [a-zA-Z0-9_] may be a better choice).對於8位(bytes)樣式:匹配ASCII字符中的數字和字母和下劃線,就是 [a-zA-Z0-9_] 。如果設置了 LOCALE 標記,就匹配當前語言區域的數字和字母和下劃線。\WMatches any character which is not a word character. This is the opposite of \w. If the ASCII flag is used this becomes the equivalent of [^a-zA-Z0-9_] (but the flag affects the entire regular expression, so in such cases using an explicit [^a-zA-Z0-9_] may be a better choice). If the LOCALE flag is used, matches characters considered alphanumeric in the current locale and the underscore.\Z只匹配字符串尾。絕大部分Python的標準轉義字符也被正則表達式分析器支持。:\a \b \f \n\r \t \u \U\v \x \\


(注意 \b 被用於表示詞語的邊界,它只在字符集合內表示退格,比如 [\b] 。)

'\u' and '\U' escape sequences are only recognized in Unicode patterns. In bytes patterns they are errors.

八進制轉義包含為一個有限形式。如果首位數字是 0, 或者有三個八進制數位,那麼就認為它是八進制轉義。其他的情況,就看作是組引用。對於字符串文本,八進制轉義最多有三個數位長。

在 3.3 版更改: 增加了 '\u' 和 '\U' 轉義序列。

在 3.6 版更改: 由 '\' 和一個ASCII字符組成的未知轉義會被看成錯誤。

模塊內容

模塊定義了幾個函數,常量,和一個例外。有些函數是編譯後的正則表達式方法的簡化版本(少了一些特性)。絕大部分重要的應用,總是會先將正則表達式編譯,之後在進行操作。

在 3.6 版更改: 標誌常量現在是 RegexFlag 類的實例,這個類是 enum.IntFlag 的子類。

re.compile(pattern, flags=0)Compile a regular expression pattern into a regular expression object, which can be used for matching using its match(), search() and other methods, described below.這個表達式的行為可以通過指定 標記 的值來改變。值可以是以下任意變量,可以通過位的OR操作來結合( | 操作符)。序列prog = re.compile(pattern) result = prog.match(string)


等價於

result = re.match(pattern, string)


如果需要多次使用這個正則表達式的話,使用 re.compile() 和保存這個正則對象以便複用,可以讓程序更加高效。

註解

通過 re.compile() 編譯後的樣式,和模塊級的函數會被緩存, 所以少數的正則表達式使用無需考慮編譯的問題。

re.Are.ASCII讓 \w, \W, \b, \B, \d, \D, \s 和 \S 只匹配ASCII,而不是Unicode。這隻對Unicode樣式有效,會被byte樣式忽略。相當於前面語法中的內聯標誌 (?a) 。注意,為了保持向後兼容, re.U 標記依然存在(還有他的同義 re.UNICODE 和嵌入形式 (?u) ) , 但是這些在 Python 3 是冗餘的,因為默認字符串已經是Unicode了(並且Unicode匹配不允許byte出現)。re.DEBUG顯示編譯時的debug信息,沒有內聯標記。re.Ire.IGNORECASE

進行忽略大小寫匹配;表達式如 [A-Z] 也會匹配小寫字符。Unicode匹配(比如 Ü 匹配 ü)同樣有用,除非設置了 re.ASCII 標記來禁用非ASCII匹配。當前語言區域不會改變這個標記,除非設置了 re.LOCALE 標記。這個相當於內聯標記 (?i) 。Note that when the Unicode patterns [a-z] or [A-Z] are used in combination with the IGNORECASE flag, they will match the 52 ASCII letters and 4 additional non-ASCII letters: ‘İ’ (U+0130, Latin capital letter I with dot above), ‘ı’ (U+0131, Latin small letter dotless i), ‘ſ’ (U+017F, Latin small letter long s) and ‘K’ (U+212A, Kelvin sign). If the ASCII flag is used, only letters ‘a’ to ‘z’ and ‘A’ to ‘Z’ are matched (but the flag affects the entire regular expression, so in such cases using an explicit (?-i:[a-zA-Z]) may be a better choice).re.Lre.LOCALE由當前語言區域決定 \w, \W, \b, \B 和大小寫敏感匹配。這個標記只能對byte樣式有效。這個標記不推薦使用,因為語言區域機制很不可靠,它一次只能處理一個 “習慣”,而且只對8位字節有效。Unicode匹配在Python 3 裡默認啟用,並可以處理不同語言。 這個對應內聯標記 (?L) 。在 3.6 版更改: re.LOCALE 只能用於byte樣式,而且不能和 re.ASCII 一起用。re.Mre.MULTILINE設置以後,樣式字符 '^' 匹配字符串的開始,和每一行的開始(換行符後面緊跟的符號);樣式字符 '$' 匹配字符串尾,和每一行的結尾(換行符前面那個符號)。默認情況下,’^’ 匹配字符串頭,'$' 匹配字符串尾。對應內聯標記 (?m) 。re.Sre.DOTALL讓 '.' 特殊字符匹配任何字符,包括換行符;如果沒有這個標記,'.' 就匹配 除了 換行符的其他任意字符。對應內聯標記 (?s) 。re.Xre.
VERBOSE這個標記允許你編寫更具可讀性更友好的正則表達式。通過分段和添加註釋。空白符號會被忽略,除非在一個字符集合當中或者由反斜槓轉義,或者在 *?, (?: or (?P 分組之內。當一個行內有 # 不在字符集和轉義序列,那麼它之後的所有字符都是註釋。意思就是下面兩個正則表達式等價地匹配一個十進制數字:a = re.compile(r"""\d + # the integral part \. # the decimal point \d * # some fractional digits""", re.X)b = re.compile(r"\d+\.\d*")


對應內聯標記 (?x) 。

re.search(pattern, string, flags=0)掃描整個 字符串 找到匹配樣式的第一個位置,並返回一個相應的 匹配對象。如果沒有匹配,就返回一個 None ; 注意這和找到一個零長度匹配是不同的。re.match(pattern, string, flags=0)如果 string 開始的0或者多個字符匹配到了正則表達式樣式,就返回一個相應的 匹配對象 。 如果沒有匹配,就返回 None ;注意它跟零長度匹配是不同的。注意即便是 MULTILINE 多行模式, re.match() 也只匹配字符串的開始位置,而不匹配每行開始。如果你想定位 string 的任何位置,使用 search() 來替代(也可參考 search() vs. match() )re.fullmatch(pattern, string, flags=0)如果整個 string 匹配到正則表達式樣式,就返回一個相應的 匹配對象 。 否則就返回一個 None ;注意這跟零長度匹配是不同的。3.4 新版功能.re.

split(pattern, string, maxsplit=0, flags=0)用 pattern 分開 string 。 如果在 pattern 中捕獲到括號,那麼所有的組裡的文字也會包含在列表裡。如果 maxsplit 非零, 最多進行 maxsplit 次分隔, 剩下的字符全部返回到列表的最後一個元素。>>>>>> re.split(r'\W+', 'Words, words, words.') ['Words', 'words', 'words', '']>>> re.split(r'(\W+)', 'Words, words, words.') ['Words', ', ', 'words', ', ', 'words', '.', '']>>> re.split(r'\W+', 'Words, words, words.', 1) ['Words', 'words, words.']>>> re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE) ['0', '3', '9']


如果分隔符裡有捕獲組合,並且匹配到字符串的開始,那麼結果將會以一個空字符串開始。對於結尾也是一樣

>>>

>>> re.split(r'(\W+)', '...words, words...')

['', '...', 'words', ', ', 'words', '...', '']


這樣的話,分隔組將會出現在結果列表中同樣的位置。

註解

split() doesn’t currently split a string on an empty pattern match. For example:

>>>

>>> re.split('x*', 'axbc')

['a', 'bc']


Even though 'x*' also matches 0 ‘x’ before ‘a’, between ‘b’ and ‘c’, and after ‘c’, currently these matches are ignored. The correct behavior (i.e. splitting on empty matches too and returning ['', 'a', 'b', 'c', '']) will be implemented in future versions of Python, but since this is a backward incompatible change, a FutureWarning will be raised in the meanwhile.

Patterns that can only match empty strings currently never split the string. Since this doesn’t match the expected behavior, a ValueError will be raised starting from Python 3.5:

>>>

>>> re.split("^$", "foo\n\nbar\n", flags=re.M)

Traceback (most recent call last):

File "", line 1, in

...

ValueError: split() requires a non-empty pattern match.


在 3.1 版更改: 增加了可選標記參數。

在 3.5 版更改: Splitting on a pattern that could match an empty string now raises a warning. Patterns that can only match empty strings are now rejected.

re.findall(pattern, string, flags=0)對 string 返回一個不重複的 pattern 的匹配列表, string 從左到右進行掃描,匹配按找到的順序返回。如果樣式裡存在一到多個組,就返回一個組合列表;就是一個元組的列表(如果樣式裡有超過一個組合的話)。空匹配也會包含在結果裡。

註解Due to the limitation of the current implementation the character following an empty match is not included in a next match, so findall(r'^|\w+', 'two words') returns ['', 'wo', 'words'] (note missed “t”). This is changed in Python 3.7.re.finditer(pattern, string, flags=0)Return an iterator yielding match objects over all non-overlapping matches for the RE pattern in string. The string is scanned left-to-right, and matches are returned in the order found. Empty matches are included in the result. See also the note about findall().re.sub(pattern, repl, string, count=0, flags=0)Return the string obtained by replacing the leftmost non-overlapping occurrences of pattern in string by the replacement repl. If the pattern isn’t found, string is returned unchanged. repl can be a string or a function; if it is a string, any backslash escapes in it are processed. That is, \n is converted to a single newline character, \r is converted to a carriage return, and so forth. Unknown escapes such as \& are left alone. Backreferences, such as \6, are replaced with the substring matched by group 6 in the pattern. For example:>>>>>> re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9]*)\s*\(\s*\):', ... r'static PyObject*\npy_\1(void)\n{', ... 'def myfunc():') 'static PyObject*\npy_myfunc(void)\n{'


如果 repl 是一個函數,那它會對每個非重複的 pattern 的情況調用。這個函數只能有一個 匹配對象 參數,並返回一個替換後的字符串。比如

>>>

>>> def dashrepl(matchobj):

... if matchobj.group(0) == '-': return ' '

... else: return '-'

>>> re.sub('-{1,2}', dashrepl, 'pro----gram-files')

'pro--gram files'

>>> re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)

'Baked Beans & Spam'


樣式可以是一個字符串或者一個 樣式對象 。

The optional argument count is the maximum number of pattern occurrences to be replaced; count must be a non-negative integer. If omitted or zero, all occurrences will be replaced. Empty matches for the pattern are replaced only when not adjacent to a previous match, so sub('x*', '-', 'abc') returns '-a-b-c-'.

在字符串類型的 repl 參數裡,如上所述的轉義和向後引用中,\g 會使用命名組合 name,(在 (?P…) 語法中定義) \g 會使用數字組;\g<2> 就是 \2,但它避免了二義性,如 \g<2>0。 \20 就會被解釋為組20,而不是組2後面跟隨一個字符 '0'。向後引用 \g<0> 把 pattern 作為一整個組進行引用。

在 3.1 版更改: 增加了可選標記參數。

在 3.5 版更改: 不匹配的組合替換為空字符串。

在 3.6 版更改: pattern 中的未知轉義(由 '\' 和一個 ASCII 字符組成)被視為錯誤。

Deprecated since version 3.5, will be removed in version 3.7: Unknown escapes in repl consisting of '\' and an ASCII letter now raise a deprecation warning and will be forbidden in Python 3.7.

re.subn(pattern, repl, string, count=0, flags=0)行為與 sub() 相同,但是返回一個元組 (字符串, 替換次數).在 3.1 版更改: 增加了可選標記參數。在 3.5 版更改: 不匹配的組合替換為空字符串。re.

escape(pattern)Escape all the characters in pattern except ASCII letters, numbers and '_'. This is useful if you want to match an arbitrary literal string that may have regular expression metacharacters in it. For example:>>>>>> print(re.escape('python.exe')) python\.exe


>>> legal_chars = string.ascii_lowercase + string.digits + "!#$%&'*+-.^_`|~:"

>>> print('[%s]+' % re.escape(legal_chars))

[abcdefghijklmnopqrstuvwxyz0123456789\!\#\$\%\&\'\*\+\-\.\^_\`\|\~\:]+


>>> operators = ['+', '-', '*', '/', '**']

>>> print('|'.join(map(re.escape, sorted(operators, reverse=True))))

\/|\-|\+|\*\*|\*


This functions must not be used for the replacement string in sub() and subn(), only backslashes should be escaped. For example:

>>>

>>> digits_re = r'\d+'

>>> sample = '/usr/sbin/sendmail - 0 errors, 12 warnings'

>>>

print(re.sub(digits_re, digits_re.replace('\\', r'\\'), sample))

/usr/sbin/sendmail - \d+ errors, \d+ warnings


在 3.3 版更改: '_' 不再被轉義。

re.purge()清除正則表達式緩存。exception re.error(msg, pattern=None, pos=None)raise 一個例外。當傳遞到函數的字符串不是一個有效正則表達式的時候(比如,包含一個不匹配的括號)或者其他錯誤在編譯時或匹配時產生。如果字符串不包含樣式匹配,是不會被視為錯誤的。錯誤實例有以下附加屬性:msg未格式化的錯誤消息。pattern正則表達式樣式。pos編譯失敗的 pattern 的位置索引(可以是 None )。lineno對應 pos (可以是 None) 的行號。colno對應 pos (可以是 None) 的列號。在 3.5 版更改: 添加了附加屬性。

正則表達式對象 (正則對象)

編譯後的正則表達式對象支持以下方法和屬性:

regex.search(string[, pos[, endpos]])掃描整個 string 尋找第一個匹配的位置, 並返回一個相應的 匹配對象。如果沒有匹配,就返回 None ;注意它和零長度匹配是不同的。可選的第二個參數 pos 給出了字符串中開始搜索的位置索引;默認為 0,它不完全等價於字符串切片; '^' 樣式字符匹配字符串真正的開頭,和換行符後面的第一個字符,但不會匹配索引規定開始的位置。可選參數 endpos 限定了字符串搜索的結束;它假定字符串長度到 endpos , 所以只有從 pos 到 endpos - 1 的字符會被匹配。如果 endpos 小於 pos,就不會有匹配產生;另外,如果 rx 是一個編譯後的正則對象, rx.search(string, 0, 50) 等價於 rx.search(string[:50], 0)。>>>>>> pattern = re.compile("d") >>> pattern.search("dog") # Match at index 0 >>> pattern.search("dog", 1) # No match; search doesn't include the "d"


regex.match(string[, pos[, endpos]])如果 string 的 開始位置 能夠找到這個正則樣式的任意個匹配,就返回一個相應的 匹配對象。如果不匹配,就返回 None ;注意它與零長度匹配是不同的。The optional pos and endpos parameters have the same meaning as for the search() method.>>>>>> pattern = re.compile("o") >>>

pattern.match("dog") # No match as "o" is not at the start of "dog". >>> pattern.match("dog", 1) # Match as "o" is the 2nd character of "dog".


If you want to locate a match anywhere in string, use search() instead (see also search() vs. match()).

regex.fullmatch(string[, pos[, endpos]])如果整個 string 匹配這個正則表達式,就返回一個相應的 匹配對象 。 否則就返回 None ; 注意跟零長度匹配是不同的。The optional pos and endpos parameters have the same meaning as for the search() method.>>>>>> pattern = re.compile("o[gh]") >>> pattern.fullmatch("dog") # No match as "o" is not at the start of "dog". >>> pattern.fullmatch("ogre") # No match as not the full string matches. >>> pattern.fullmatch("doggie", 1, 3) # Matches within given limits.


3.4 新版功能.

regex.split(string, maxsplit=0)等價於 split() 函數,使用了編譯後的樣式。regex.findall(string[, pos[, endpos]])類似函數 findall() , 使用了編譯後樣式,但也可以接收可選參數 pos 和 endpos ,限制搜索範圍,就像 search()。regex.finditer(string[, pos[, endpos]])類似函數 finiter() , 使用了編譯後樣式,但也可以接收可選參數 pos 和 endpos ,限制搜索範圍,就像 search()。regex.sub(repl, string, count=0)等價於 sub() 函數,使用了編譯後的樣式。regex.

subn(repl, string, count=0)等價於 subn() 函數,使用了編譯後的樣式。regex.flags正則匹配標記。這是可以傳遞給 compile() 的參數,任何 (?…) 內聯標記,隱性標記比如 UNICODE 的結合。regex.groups捕獲組合的數量。regex.groupindex映射由 (?P) 定義的命名符號組合和數字組合的字典。如果沒有符號組,那字典就是空的。regex.patternThe pattern string from which the RE object was compiled.

匹配對象

Match objects always have a boolean value of True. Since match() and search() return None when there is no match, you can test whether there was a match with a simple if statement:

match = re.search(pattern, string)

if match:

process(match)


匹配對象支持以下方法和屬性:

match.expand(template)Return the string obtained by doing backslash substitution on the template string template, as done by the sub() method. Escapes such as \n are converted to the appropriate characters, and numeric backreferences (\1, \2) and named backreferences (\g<1>, \g) are replaced by the contents of the corresponding group.在 3.5 版更改: 不匹配的組合替換為空字符串。match.group([group1, ...])返回一個或者多個匹配的子組。如果只有一個參數,結果就是一個字符串,如果有多個參數,結果就是一個元組(每個參數對應一個項),如果沒有參數,組1默認到0(整個匹配都被返回)。 如果一個組N 參數值為 0,相應的返回值就是整個匹配字符串;如果它是一個範圍 [1..99],結果就是相應的括號組字符串。如果一個組號是負數,或者大於樣式中定義的組數,一個 IndexError 索引錯誤就 raise。如果一個組包含在樣式的一部分,並被匹配多次,就返回最後一個匹配。:>>>

>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist") >>> m.group(0) # The entire match 'Isaac Newton'>>> m.group(1) # The first parenthesized subgroup. 'Isaac'>>> m.group(2) # The second parenthesized subgroup. 'Newton'>>> m.group(1, 2) # Multiple arguments give us a tuple. ('Isaac', 'Newton')


如果正則表達式使用了 (?P…) 語法, groupN 參數就也可能是命名組合的名字。如果一個字符串參數在樣式中未定義為組合名,一個 IndexError 就 raise。

一個相對複雜的例子

>>>

>>> m = re.match(r"(?P\w+) (?P\w+)", "Malcolm Reynolds")

>>> m.group('first_name')

'Malcolm'

>>> m.group('last_name')

'Reynolds'


命名組合同樣可以通過索引值引用

>>>

>>> m.group(1)

'Malcolm'

>>> m.group(2)

'Reynolds'


如果一個組匹配成功多次,就只返回最後一個匹配

>>>

>>> m = re.match(r"(..)+", "a1b2c3") # Matches 3 times.

>>> m.group(1) # Returns only the last match.

'c3'


match.__getitem__(g)這個等價於 m.group(g)。這允許更方便的引用一個匹配>>>>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist") >>> m[0] # The entire match 'Isaac Newton'>>> m[1] # The first parenthesized subgroup. 'Isaac'>>> m[2] # The second parenthesized subgroup. 'Newton'


3.6 新版功能.

match.groups(default=None)返回一個元組,包含所有匹配的子組,在樣式中出現的從1到任意多的組合。 default 參數用於不參與匹配的情況,默認為 None。例如>>>>>> m = re.match(r"(\d+)\.(\d+)", "24.1632") >>> m.groups() ('24', '1632')


如果我們使小數點可選,那麼不是所有的組都會參與到匹配當中。這些組合默認會返回一個 None ,除非指定了 default 參數。

>>>

>>> m = re.match(r"(\d+)\.?(\d+)?", "24")

>>> m.groups() # Second group defaults to None.

('24', None)

>>> m.groups('0') # Now, the second group defaults to '0'.

('24', '0')


match.groupdict(default=None)返回一個字典,包含了所有的 命名 子組。key就是組名。 default 參數用於不參與匹配的組合;默認為 None。 例如>>>

>>> m = re.match(r"(?P\w+) (?P\w+)", "Malcolm Reynolds") >>> m.groupdict() {'first_name': 'Malcolm', 'last_name': 'Reynolds'}


match.start([group])match.end([group])返回 group 匹配到的字串的開始和結束標號。group 默認為0(意思是整個匹配的子串)。如果 group 存在,但未產生匹配,就返回 -1 。對於一個匹配對象 m, 和一個未參與匹配的組 g ,組 g (等價於 m.group(g))產生的匹配是m.string[m.start(g):m.end(g)]


注意 m.start(group) 將會等於 m.end(group) ,如果 group 匹配一個空字符串的話。比如,在 m = re.search('b(c?)', 'cba') 之後,m.start(0) 為 1, m.end(0) 為 2, m.start(1) 和 m.end(1) 都是 2, m.start(2) raise 一個 IndexError 例外。

這個例子會從email地址中移除掉 remove_this

>>>

>>> email = "tony@tiremove_thisger.net"

>>> m = re.search("remove_this", email)

>>> email[:m.start()] + email[m.end():]

'[email protected]'


match.span([group])對於一個匹配 m , 返回一個二元組 (m.start(group), m.end(group)) 。 注意如果 group 沒有在這個匹配中,就返回 (-1, -1) 。group 默認為0,就是整個匹配。match.posThe value of pos which was passed to the search() or match() method of a regex object. This is the index into the string at which the RE engine started looking for a match.match.endposThe value of endpos which was passed to the search() or match() method of a regex object. This is the index into the string beyond which the RE engine will not go.match.lastindex捕獲組的最後一個匹配的整數索引值,或者 None 如果沒有匹配產生的話。比如,對於字符串 'ab',表達式 (a)b, ((a)(b)), 和 ((ab)) 將得到 lastindex == 1 , 而 (a)(b) 會得到 lastindex == 2 。match.lastgroup最後一個匹配的命名組名字,或者 None 如果沒有產生匹配的話。match.reThe regular expression object whose match() or search() method produced this match instance.match.stringThe string passed to match() or search().

正則表達式例子

檢查對子

在這個例子裡,我們使用以下輔助函數來更好的顯示匹配對象:

def displaymatch(match):

if match is None

:

return None

return '' % (match.group(), match.groups())


假設你在寫一個撲克程序,一個玩家的一手牌為五個字符的串,每個字符表示一張牌,”a” 就是 A, “k” K, “q” Q, “j” J, “t” 為 10, “2” 到 “9” 表示2 到 9。

要看給定的字符串是否有效,我們可以按照以下步驟

>>>

>>> valid = re.compile(r"^[a2-9tjqk]{5}$")

>>> displaymatch(valid.match("akt5q")) # Valid.

""

>>> displaymatch(valid.match("akt5e")) # Invalid.

>>> displaymatch(valid.match("akt")) # Invalid.

>>> displaymatch(valid.match("727ak")) # Valid.

""


最後一手牌,"727ak" ,包含了一個對子,或者兩張同樣數值的牌。要用正則表達式匹配它,應該使用向後引用如下

>>>

>>> pair = re.compile(r".*(.).*\1")

>>> displaymatch(pair.match("717ak")) # Pair of 7s.

""

>>> displaymatch(pair.match("718ak")) # No pairs.

>>> displaymatch(pair.match("354aa")) # Pair of aces.

""


To find out what card the pair consists of, one could use the group() method of the match object in the following manner:

>>> pair.match("717ak").group(1)

'7'


# Error because re.match() returns None, which doesn't have a group() method:

>>> pair.match("718ak").group(1)

Traceback (most recent call last):

File "", line 1, in

re.match(r".*(.).*\1", "718ak").group(1)

AttributeError: 'NoneType' object has no attribute 'group'


>>> pair.match("354aa").group(1)

'a'


模擬 scanf()

Python 目前沒有一個類似c函數 scanf() 的替代品。正則表達式通常比 scanf() 格式字符串要更強大一些,但也帶來更多複雜性。下面的表格提供了 scanf() 格式符和正則表達式大致相同的映射。

scanf() 格式符

正則表達式

%c

.

%5c

.{5}

%d

[-+]?\d+

%e, %E, %f, %g

[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?

%i

[-+]?(0[xX][\dA-Fa-f]+|0[0-7]*|\d+)

%o

[-+]?[0-7]+

%s

\S+

%u

\d+

%x, %X

[-+]?(0[xX])?[\dA-Fa-f]+

從文件名和數字提取字符串

/usr/sbin/sendmail - 0 errors, 4 warnings


你可以使用 scanf() 格式化

%s - %d errors, %d warnings


等價的正則表達式是:

(\S+) - (\d+) errors, (\d+) warnings


search() vs. match()

Python 提供了兩種不同的操作:基於 re.match() 檢查字符串開頭,或者 re.search() 檢查字符串的任意位置(默認Perl中的行為)。

例如

>>>

>>> re.match("c", "abcdef") # No match

>>> re.search("c", "abcdef") # Match


在 search() 中,可以用 '^' 作為開始來限制匹配到字符串的首位

>>>

>>> re.match("c", "abcdef") # No match

>>> re.search("^c", "abcdef") # No match

>>> re.search("^a", "abcdef") # Match


注意 MULTILINE 多行模式中函數 match() 只匹配字符串的開始,但使用 search() 和以 '^' 開始的正則表達式會匹配每行的開始

>>>

>>> re.match('X', 'A\nB\nX', re.MULTILINE) # No match

>>> re.search('^X', 'A\nB\nX', re.MULTILINE) # Match


建立一個電話本

split() 將字符串用參數傳遞的樣式分隔開。這個方法對於轉換文本數據到易讀而且容易修改的數據結構,是很有用的,如下面的例子證明。

首先,這裡是輸入。通常是一個文件,這裡我們用三引號字符串語法

>>>

>>> text = """Ross McFluff: 834.345.1254 155 Elm Street

...

...

Ronald Heathmore: 892.345.3428 436 Finley Avenue

... Frank Burger: 925.541.7625 662 South Dogwood Way

...

...

... Heather Albrecht: 548.326.4584 919 Park Place"""


條目用一個或者多個換行符分開。現在我們將字符串轉換為一個列表,每個非空行都有一個條目:

>>> entries = re.split("\n+", text)

>>> entries

['Ross McFluff: 834.345.1254 155 Elm Street',

'Ronald Heathmore: 892.345.3428 436 Finley Avenue',

'Frank Burger: 925.541.7625 662 South Dogwood Way',

'Heather Albrecht: 548.326.4584 919 Park Place']


最終,將每個條目分割為一個由名字、姓氏、電話號碼和地址組成的列表。我們為 split() 使用了 maxsplit 形參,因為地址中包含有被我們作為分割模式的空格符:

>>> [re.split(":? ", entry, 3) for entry in entries]

[['Ross', 'McFluff', '834.345.1254', '155 Elm Street'],

['Ronald', 'Heathmore', '892.345.3428', '436 Finley Avenue'],

['Frank', 'Burger', '925.541.7625', '662 South Dogwood Way'],

['Heather', 'Albrecht', '548.326.4584', '919 Park Place']]


:? 樣式匹配姓後面的冒號,因此它不出現在結果列表中。如果 maxsplit 設置為 4 ,我們還可以從地址中獲取到房間號:

>>> [re.split(":? ", entry, 4) for entry in entries]

[['Ross', 'McFluff', '834.345.1254', '155', 'Elm Street'],

['Ronald', 'Heathmore', '892.345.3428', '436', 'Finley Avenue'],

['Frank', 'Burger', '925.541.7625', '662', 'South Dogwood Way'],

['Heather', 'Albrecht', '548.326.4584', '919', 'Park Place']]


文字整理

sub() 替換字符串中出現的樣式的每一個實例。這個例子證明了使用 sub() 來整理文字,或者隨機化每個字符的位置,除了首位和末尾字符

>>>

>>> def repl(m):

... inner_word = list(m.group(2))

... random.shuffle(inner_word)

... return m.group(1) + "".join(inner_word) + m.group(3)

>>> text = "Professor Abdolmalek, please report your absences promptly."

>>> re.sub(r"(\w)(\w+)(\w)", repl, text)

'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.'

>>> re.sub(r"(\w)(\w+)(\w)", repl, text)

'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.'


找到所有副詞

findall() 匹配樣式 所有 的出現,不僅是像 search() 中的第一個匹配。比如,如果一個作者希望找到文字中的所有副詞,他可能會按照以下方法用 findall()

>>>

>>> text = "He was carefully disguised but captured quickly by police."

>>> re.findall(r"\w+ly", text)

['carefully', 'quickly']


找到所有副詞和位置

如果需要匹配樣式的更多信息, finditer() 可以起到作用,它提供了 匹配對象 作為返回值,而不是字符串。繼續上面的例子,如果一個作者希望找到所有副詞和它的位置,可以按照下面方法使用 finditer()

>>>

>>> text = "He was carefully disguised but captured quickly by police."

>>> for m in re.finditer(r"\w+ly", text):

... print('%02d-%02d: %s' % (m.start(), m.end(), m.group(0)))

07-16: carefully

40-47: quickly


原始字符記法

原始字符串記法 (r"text") 保持正則表達式正常。否則,每個正則式裡的反斜槓('\') 都必須前綴一個反斜槓來轉義。比如,下面兩行代碼功能就是完全一致的

>>>

>>> re.match(r"\W(.)\1\W", " ff ")

>>> re.match("\\W(.)\\1\\W", " ff ")


當需要匹配一個字符反斜槓,它必須在正則表達式中轉義。在原始字符串記法,就是 r""。否則就必須用 "\",來表示同樣的意思

>>>

>>> re.match(r"\\", r"\\

")

>>> re.match("\\\\", r"\\")


寫一個詞法分析器

一個 詞法器或詞法分析器 分析字符串,並分類成目錄組。 這是寫一個編譯器或解釋器的第一步。

文字目錄是由正則表達式指定的。這個技術是通過將這些樣式合併為一個主正則式,並且循環匹配來實現的

import collections

import re


Token = collections.namedtuple('Token', ['type', 'value', 'line', 'column'])


def tokenize(code):

keywords = {'IF', 'THEN', 'ENDIF', 'FOR', 'NEXT', 'GOSUB', 'RETURN'}

token_specification = [

('NUMBER', r'\d+(\.\d*)?'), # Integer or decimal number

('ASSIGN', r':='), # Assignment operator

('END', r';'), # Statement terminator

('ID', r'[A-Za-z]+'), # Identifiers

('OP', r'[+\-*/]'), # Arithmetic operators

('NEWLINE', r'\n'), # Line endings

('SKIP', r'[ \t]+'), # Skip over spaces and tabs

('MISMATCH', r'.'), # Any other character

]

tok_regex = '|'.join('(?P%s)' % pair for pair in token_specification)

line_num = 1

line_start = 0

for mo in re.finditer(tok_regex, code):

kind = mo.lastgroup

value = mo.group()

column = mo.start() - line_start

if

kind == 'NUMBER':

value = float(value) if '.' in value else int(value)

elif kind == 'ID' and value in keywords:

kind = value

elif kind == 'NEWLINE':

line_start = mo.end()

line_num += 1

continue

elif kind == 'SKIP':

continue

elif kind == 'MISMATCH':

raise RuntimeError(f'{value!r} unexpected on line {line_num}')

yield

Token(kind, value, line_num, column)


statements = '''

IF quantity THEN

total := total + price * quantity;

tax := price * 0.05;

ENDIF;

'''


for token in tokenize(statements):

print(token)


這個詞法器產生以下輸出

Token(type='IF', value='IF', line=2, column=4)

Token(type='ID', value='quantity', line=2, column=7)

Token(type='THEN', value='THEN', line=2, column=16)

Token(type='ID', value='total', line=3, column=8)

Token(type='ASSIGN', value=':=', line=3, column=14)

Token(type='ID', value='total', line=3, column=17)

Token(type='OP', value='+', line=3, column=23)

Token(type='ID', value='price', line=3, column=25)

Token(type='OP', value='*', line=3, column=31)

Token(type='ID', value='quantity', line=3, column=33)

Token(type='END', value=';', line=3, column=41)

Token(type='ID', value='tax', line=4, column=8)

Token(type='ASSIGN', value=':=', line=4, column=12)

Token(type='ID', value='price', line=4, column=15)

Token(type='OP', value='*', line=4, column=21)

Token(type='NUMBER', value=0.05, line=4, column=23)

Token(type='END', value=';', line=4, column=27)

Token(type='ENDIF', value='ENDIF', line=5, column=4)

Token(type='END', value=';', line=5, column=9)


分享到:


相關文章: