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'。

*?,?+?,???

'*',?'+',和?'?'?修飾符都是?貪婪的;它們在字符串進行盡可能多的匹配。有時候并不需要這種行為。如果正則式?<.*>?希望找到?'<a>?b?<c>',它將會匹配整個字符串,而不僅是?'<a>'。在修飾符之后添加???將使樣式以?非貪婪`方式或者 :dfn:`最小?方式進行匹配; 盡量?少?的字符將會被匹配。 使用正則式?<.*?>?將會僅僅匹配?'<a>'。

“{m}”

對其之前的正則式指定匹配?m?個重復;少于?m?的話就會導致匹配失敗。比如,?a{6}?將匹配6個?'a'?, 但是不能是5個。

“{m, n}”

對正則式進行?m?到?n?次匹配,在?m?和?n?之間取盡量多。 比如,a{3,5}?將匹配 3 到 5個?'a'。忽略?m?意為指定下界為0,忽略?n?指定上界為無限次。 比如?a{4,}b?將匹配?'aaaab'?或者1000個?'a'?尾隨一個?'b',但不能匹配?'aaab'。逗號不能省略,否則無法辨別修飾符應該忽略哪個邊界。

{m,n}?

前一個修飾符的非貪婪模式,只匹配盡量少的字符次數。比如,對于?'aaaaaa',?a{3,5}?匹配 5個?'a'?,而?a{3,5}??只匹配3個?'a'。

\

轉義特殊字符(允許你匹配?'*',?'?', 或者此類其他),或者表示一個特殊序列;特殊序列之后進行討論。

如果你沒有使用原始字符串(?r'raw'?)來表達樣式,要牢記Python也使用反斜杠作為轉義序列;如果轉義序列不被Python的分析器識別,反斜杠和字符才能出現在字符串中。如果Python可以識別這個序列,那么反斜杠就應該重復兩次。這將導致理解障礙,所以高度推薦,就算是最簡單的表達式,也要使用原始字符串。

[]

用于表示一個字符集合。在一個集合中:

字符可以單獨列出,比如?[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<name>...)?是唯一的例外。 以下是目前支持的擴展。

(?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>…)

(命名組合)類似正則組合,但是匹配到的子串組在外部是通過定義的?name?來獲取的。組合名必須是有效的Python標識符,并且每個組合名只能用一個正則表達式定義,只能定義一次。一個符號組合同樣是一個數字組合,就像這個組合沒有被命名一樣。

命名組合可以在三種上下文中引用。如果樣式是?(?P<quote>['"]).*?(?P=quote)?(也就是說,匹配單引號或者雙引號括起來的字符串):

引用組合 “quote” 的上下文引用方法

在正則式自身內(?P=quote)?(如示)

\1

處理匹配對象?mm.group('quote')

m.end('quote')?(等)

傳遞到?re.sub()?里的?repl?參數中\g<quote>

\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()?函數:

>>>importre

>>>m=re.search('(?<=abc)def','abcdef')

>>>m.group(0)

'def'

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

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

>>>m.group(0)

'egg'

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

(?<!…)

匹配當前位置之前不是?…?的樣式。這個叫:dfn:negative lookbehind assertion?(后視斷定取非)。類似正向后視斷定,包含的樣式匹配必須是定長的。由 negative lookbehind assertion 開始的樣式可以從字符串搜索開始的位置進行匹配。

(?(id/name)yes-pattern|no-pattern)

如果給定的?id?或?name?存在,將會嘗試匹配?yes-pattern?,否則就嘗試匹配?no-pattern,no-pattern?可選,也可以被忽略。比如,?(<)?(\w+@\w+(?:\.\w+)+)(?(1)>|$)?是一個email樣式匹配,將匹配?'<user@host.com>'?或?'user@host.com'?,但不會匹配?'<user@host.com'?,也不會匹配?'user@host.com>'。

由?'\'?和一個字符組成的特殊序列在以下列出。 如果普通字符不是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]。

\D

Matches 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]?。

\S

Matches 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?標記,就匹配當前語言區域的數字和字母和下劃線。

\W

Matches 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 ‘?’ (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"<stdin>", line1, in <module>

?...

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?的情況調用。這個函數只能有一個?匹配對象?參數,并返回一個替換后的字符串。比如

>>>

>>>defdashrepl(matchobj):

...???ifmatchobj.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>?會使用命名組合?name,(在?(?P<name>…)?語法中定義)?\g<number>?會使用數字組;\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

<_sre.SRE_Match object; span=(0, 1), match='d'>

>>>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".

<_sre.SRE_Match object; span=(1, 2), match='o'>

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.

<_sre.SRE_Match object; span=(1, 3), match='og'>

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<id>)?定義的命名符號組合和數字組合的字典。如果沒有符號組,那字典就是空的。

regex.pattern

The 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)

ifmatch:

??? 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<name>) 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<name>…)?語法,?groupN?參數就也可能是命名組合的名字。如果一個字符串參數在樣式中未定義為組合名,一個?IndexError?就?raise。

一個相對復雜的例子

>>>

>>>m=re.match(r"(?P<first_name>\w+) (?P<last_name>\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<first_name>\w+) (?P<last_name>\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():]

'tony@tiger.net'

match.span([group])

對于一個匹配?m?, 返回一個二元組?(m.start(group),?m.end(group))?。 注意如果?group?沒有在這個匹配中,就返回?(-1,?-1)?。group?默認為0,就是整個匹配。

match.pos

The 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.endpos

The 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.re

The?regular expression object?whose?match()?or?search()?method produced this match instance.

match.string

The string passed to?match()?or?search().

正則表達式例子

檢查對子

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

defdisplaymatch(match):

???ifmatchisNone:

???????returnNone

???return'<Match:%r, groups=%r>'%(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.

"<Match: 'akt5q', groups=()>"

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

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

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

"<Match: '727ak', groups=()>"

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

>>>

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

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

"<Match: '717', groups=('7',)>"

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

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

"<Match: '354aa', groups=('a',)>"

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"<pyshell#23>", line1, in <module>

??? 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-0errors,4warnings

你可以使用?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

<_sre.SRE_Match object; span=(2, 3), match='c'>

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

>>>

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

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

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

<_sre.SRE_Match object; span=(0, 1), match='a'>

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

>>>

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

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

<_sre.SRE_Match object; span=(4, 5), match='X'>

建立一個電話本

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)forentryinentries]

[['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)forentryinentries]

[['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()?來整理文字,或者隨機化每個字符的位置,除了首位和末尾字符

>>>

>>>defrepl(m):

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

...??? random.shuffle(inner_word)

...???returnm.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."

>>>forminre.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 ")

<_sre.SRE_Match object; span=(0, 4), match=' ff '>

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

<_sre.SRE_Match object; span=(0, 4), match=' ff '>

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

>>>

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

<_sre.SRE_Match object; span=(0, 1), match='\\'>

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

<_sre.SRE_Match object; span=(0, 1), match='\\'>

寫一個詞法分析器

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

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

importcollections

importre

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

deftokenize(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>%s)'%pairforpairintoken_specification)

??? line_num=1

??? line_start=0

???formoinre.finditer(tok_regex, code):

??????? kind=mo.lastgroup

??????? value=mo.group()

??????? column=mo.start()-line_start

???????ifkind=='NUMBER':

??????????? value=float(value)if'.'invalueelseint(value)

???????elifkind=='ID'andvalueinkeywords:

??????????? kind=value

???????elifkind=='NEWLINE':

??????????? line_start=mo.end()

??????????? line_num+=1

???????????continue

???????elifkind=='SKIP':

???????????continue

???????elifkind=='MISMATCH':

???????????raiseRuntimeError(f'{value!r}unexpected on line{line_num}')

???????yieldToken(kind, value, line_num, column)

statements='''

??? IF quantity THEN

??????? total := total + price * quantity;

??????? tax := price * 0.05;

??? ENDIF;

'''

fortokenintokenize(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)

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,835評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,676評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,730評論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,118評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,873評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,266評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,330評論 3 443
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,482評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,036評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,846評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,025評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,575評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,279評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,684評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,953評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,751評論 3 394
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,016評論 2 375