python正則模塊re
一.re模塊內置的函數方法
re.compile(pattern, flags=0)
https://cdn.analyticsvidhya.com/wp-content/uploads/2019/06/seq2seq.gif)
re.compile()方法可以把一個正則表達式編譯成一個正則對象(PatternObj),返回的正則對象是操作其他處理字符串方法的主體。
pattern_obj = re.compile(pattern)
match_obj = pattern_obj.compile(string)
等同於:
match_obj = re.match(pattern,string)
實際上re.match()處理流程內含re.compile()的過程。match方法源碼:
def match(pattern, string, flags=0):
"""Try to apply the pattern at the start of the string, returning
a Match object, or None if no match was found."""
return _compile(pattern, flags).match(string)
可以看出match方法返回的實際就是正則對象pattern_obj調用match()方法的結果。
re.search(pattern, string, flags=0)
re.search()方法是搜索整個字符串,找到第一個符合正則規則的字符串部分,返回一個匹配對象(MatchObject);沒有匹配成功,就返回None。
a= 'ababb_ijfknb'
b=re.search(r'((a|b)+)((\w)+)',a)
b.group(0)
#Out[22]: 'ababb_ijfknb'
b.group(1)
#Out[23]: 'ababb'
b.group(2)
#Out[24]: 'b'
b.group(3)
#Out[25]: '_ijfknb'
關於re.search當進行分組時,group(0) 就是整個匹配的內容,小括號代表分組,上述代碼:group(1)是第一組即第一個小括號裏面匹配的內容,當小括號裏面嵌套了括號,會將最後在string匹配的內容作爲一個組,然後是最後一個組。
re.match(pattern, string, flags=0)
re.match()方法是從字符串開始位置匹配整個字符串,當從字符串開始成功匹配到部分字符內容,返回一個匹配對象(MatchObject);沒有匹配成功,就返回None。
re.search()和re.match()區別對比:位置上,search()方法可以從字符串任意位置匹配部分字符串內容 match()方法必須從字符串開始位置匹配字符串內容,一旦開頭匹配不成,則匹配失敗;內容上,search()方法是非貪婪匹配,只要找到第一個符合正則規則的部分字符串就返回匹配對象,match()方法則是按照正則規則只匹配字符串開始位置的部分字符串;多行模式下,match()方法依舊只會匹配字符串的開始位置,而search()方法和“^”聯合使用則是從多行的每一行開始匹配。
re.split(pattern, string, maxsplit=0, flags=0)
re.split()表示對字符串string,按照正則表達式pattern匹配內容分隔字符串,其中maxsplit是指最大分隔次數,最大分隔次數應該是小於默認分隔次數的。分隔後的字符串內容組成列表返回。 如果在 pattern 中捕獲到括號,那麼所有的組裏的文字也會包含在列表裏。
import re
split_list_default = re.split(r'\W+', 'Words, words, words.')
print(split_list_default)
# ['Words', 'words', 'words', ''] 正則表達式\W+表示以一個或多個非單詞字符對字符串分隔,分隔後組成列
#表的形式返回,注意列表後空字符串爲'.'和之前的words分隔結果
split_list_max = re.split(r'\W+', 'Words, words, words.', 1)
print(split_list_max)
# ['Words', 'words, words.'] 指定分隔次數,字符串分隔會由左至右按照maxsplit最大分隔次數分隔,實際最大分隔次數是小於等於默認分隔次數的
split_list_couple = re.split(r'(\W+)', 'Words, words, words.')
print(split_list_couple)
# ['Words', ', ', 'words', ', ', 'words', '.', '']
#正則表達式中存在分組情況,即捕獲型括號,(\W+)會捕獲字符串中‘, ’並添加至列表一起顯示出來
split_list_couple = re.split(r'(?:\W+)', 'Words, words, words.')
#?:開頭將分隔符不顯示
print(split_list_couple)
#['Words', 'words', 'words', '']
帶分組的split方法
b = 'a12ass12asa123'
re.split(r'(1|2)',b)
#Out[7]: ['a', '1', '', '2', 'ass', '1', '', '2', 'asa', '1', '', '2', '3']
#同時出現1和2時,12中間的‘’當做佔位 被顯示出來
re.split(r'((1|2))',b)
Out[8]:
['a','1','1', '','2', '2','ass','1', '1', '','2', '2', 'asa','1', '1','','2', '2','3']
#這裏講分隔符號顯示了兩次 ,兩組括號
b = 'a1fyu2342ajbui8542ss12asa123'
re.split(r'(?:1|2)',b)
#Out[54]: ['a', 'fyu', '34', 'ajbui854', 'ss', '', 'asa', '', '3']
re.findall(pattern, string, flags=0)
re.findall()類似於re.search()方法,re.search()是在字符串中搜索到第一個與正則表達式匹配的字符串內容就返回一個匹配對象MatchObject,而re.findall()方法是在字符串中搜索並找到所有與正則表達式匹配的字符串內容,組成一個列表返回,列表中元素順序是按照正則表達式在字符串中由左至右匹配的返回;未匹配成功,返回一個空列表。
import re
pattern = r'\d{3}'
find = re.findall(pattern, 'include21321exclude13243alert213lib32')
print(find)
#注意:findall是將匹配的內容得到,以匹配後數值index+1位置繼續進行尋找
# ['213', '132', '213']
#所以,本次並沒有對於21321得到213,132,321序列,而是213,然後從3後面的2出發繼續尋找,得到132和213
注意
: 當re.findall()中的正則表達式存在兩個或兩個以上分組時,按照分組自左向右的形式匹配,匹配結果按照順序組成元組,返回列表中元素以元組的形式給出。a = 'Frank Burger: 925.541.7625 662 South Dogwood Way,Ronald Heathmore: 892.345.3428436 Finley Avenue' re.findall(r'[a-z]+ [a-z]+: (\d{3}\.\d{3}\.\d{4})',a,re.I) #返回分組之後內容 #Out[70]: ['925.541.7625', '892.345.3428']
import re
pattern = r'(\d{3})(1)'
find = re.findall(pattern, 'include21321exclude13243alert213lib32')
print(find)
re.finditer(pattern, string, flags=0)
re.finditer()相似於re.findall()方法,搜索字符串中所有與正則表達式匹配的字符串內容, 返回一個迭代器Iterator ,迭代器Iterator內保存了所有匹配字符串內容生成的匹配對象MatchObject。即匹配文本封裝在匹配對象MatchObject中,多個匹配對象MatchObject保存在一個迭代器Iterator中。
import re
pattern = r'\d{3}'
find = re.finditer(pattern, 'include21321exclude13243alert213lib32')
print(find)
for i in find:
print(i)
print(i.group(0))
# <callable_iterator object at 0x00000000028FB0F0>
# <re.Match object; span=(7, 10), match='213'>
# 213
# <re.Match object; span=(19, 22), match='132'>
# 132
# <re.Match object; span=(29, 32), match='213'>
# 213
re.sub(pattern, repl, string, count=0, flags=0)
re.sub()表示用正則表達式匹配字符串string中的字符串內容,使用repl參數內容替換匹配完成的字符串內容,返回替換後的字符串。參數count指定替換次數,正則表達式匹配字符串是由左至右的,可能匹配多個內容,替換操作也是自左向右替換,如果只想替換左邊部分匹配內容可以設置count參數,參數值爲非負整數且小於等於最大匹配成功個數;未匹配成功,不做替換,返回原字符串。
import re
pattern = r'\d+'
find_default = re.sub(pattern, ' ', 'include21321exclude13243alert213lib32')
print(find_default)
find_count = re.sub(pattern, ' ', 'include21321exclude13243alert213lib32', 2)
print(find_count)
# include exclude alert lib
# include exclude alert213lib32
二 、正則表達式語法
一個正則表達式(或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'
。 -
*?
,+?
,??
'*'
,'+'
,和'?'
修飾符都是 貪婪的;它們在字符串進行儘可能多的匹配。有時候並不需要這種行爲。如果正則式<.*>
希望找到' b '
,它將會匹配整個字符串,而不僅是''
。在修飾符之後添加?
將使樣式以 非貪婪方式或者 :dfn:
最小 方式進行匹配; 儘量 少 的字符將會被匹配。 使用正則式<.*?>
將會僅僅匹配''
。 -
{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'
,[^^]
將匹配所有字符,除了'^'
.^
如果不在集合首位,就沒有特殊含義。在集合內要匹配一個字符']'
,有兩種方法,要麼就在它之前加上反斜槓,要麼就把它放到集合首位。比如,[()[\]{}]
和[]()[{}]
都可以匹配括號。Unicode Technical Standard #18 裏的嵌套集合和集合操作支持可能在未來添加。這將會改變語法,所以爲了幫助這個改變,一個FutureWarning
將會在有多義的情況裏被raise
,包含以下幾種情況,集合由'['
開始,或者包含下列字符序列'--'
,'&&'
,'~~'
, 和'||'
。爲了避免警告,需要將它們用反斜槓轉義。在 3.7 版更改: 如果一個字符串構建的語義在未來會改變的話,一個FutureWarning
會raise
。 -
|
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匹配), andre.X
(冗長模式)。 (這些標記在 模塊內容 中描述) 如果你想將這些標記包含在正則表達式中,這個方法就很有用,免去了在re.compile()
中傳遞 flag 參數。標記應該在表達式字符串首位表示。 -
(?:…)
正則括號的非捕獲版本。 匹配在括號內的任何正則表達式,但該分組所匹配的子字符串 不能 在執行匹配後被獲取或是之後在模式中被引用。
-
(?aiLmsux-imsx:…)
(
'a'
,'i'
,'L'
,'m'
,'s'
,'u'
,'x'
中的0或者多個, 之後可選跟隨'-'
在後面跟隨'i'
,'m'
,'s'
,'x'
中的一到多個 .) 這些字符爲表達式的其中一部分 設置 或者 去除 相應標記re.A
(只匹配ASCII),re.I
(忽略大小寫),re.L
(語言依賴),re.M
(多行),re.S
(點匹配所有字符),re.U
(Unicode匹配), andre.X
(冗長模式)。(標記描述在 模塊內容 .)'a'
,'L'
and'u'
作爲內聯標記是相互排斥的, 所以它們不能結合在一起,或者跟隨'-'
。 當他們中的某個出現在內聯組中,它就覆蓋了括號組內的匹配模式。在Unicode樣式中,(?a:...)
切換爲 只匹配ASCII,(?u:...)
切換爲Unicode匹配 (默認). 在byte樣式中(?L:...)
切換爲語言依賴模式,(?a:...)
切換爲 只匹配ASCII (默認)。這種方式只覆蓋組合內匹配,括號外的匹配模式不受影響。*3.6 新版功能.*在 3.7 版更改: 符號'a'
,'L'
和'u'
同樣可以用在一個組合內。 -
(?P…)
(命名組合)類似正則組合,但是匹配到的子串組在外部是通過定義的 name 來獲取的。組合名必須是有效的Python標識符,並且每個組合名只能用一個正則表達式定義,只能定義一次。一個符號組合同樣是一個數字組合,就像這個組合沒有被命名一樣。命名組合可以在三種上下文中引用。如果樣式是
(?P['"]).*?(?P=quote)
(也就是說,匹配單引號或者雙引號括起來的字符串):引用組合 “quote” 的上下文引用方法在正則式自身內(?P=quote)
(如示)\1
處理匹配對象 mm.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 往回看的。 -
`(?
匹配當前位置之前不是
...
的樣式。這個叫 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樣式匹配,將匹配''
或'[email protected]'
,但不會匹配' ,也不會匹配
‘[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) 樣式:匹配任何Unicode十進制數(就是在Unicode字符目錄[Nd]裏的字符)。這包括了
[0-9]
,和很多其他的數字字符。如果設置了ASCII
標誌,就只匹配[0-9]
。對於8位(bytes)樣式:匹配任何十進制數,就是[0-9]
。 -
\D
匹配任何非十進制數字的字符。就是
\d
取非。 如果設置了ASCII
標誌,就相當於[^0-9]
。 -
\s
對於 Unicode (str) 樣式:匹配任何Unicode空白字符(包括
[ \t\n\r\f\v]
,還有很多其他字符,比如不同語言排版規則約定的不換行空格)。如果ASCII
被設置,就只匹配[ \t\n\r\f\v]
。對於8位(bytes)樣式:匹配ASCII中的空白字符,就是[ \t\n\r\f\v]
。 -
\S
匹配任何非空白字符。就是
\s
取非。如果設置了ASCII
標誌,就相當於[^ \t\n\r\f\v]
。 -
\w
對於 Unicode (str) 樣式:匹配Unicode詞語的字符,包含了可以構成詞語的絕大部分字符,也包括數字和下劃線。如果設置了
ASCII
標誌,就只匹配[a-zA-Z0-9_]
。對於8位(bytes)樣式:匹配ASCII字符中的數字和字母和下劃線,就是[a-zA-Z0-9_]
。如果設置了LOCALE
標記,就匹配當前語言區域的數字和字母和下劃線。 -
\W
匹配任何不是單詞字符的字符。 這與
\w
正相反。 如果使用了ASCII
旗標,這就等價於[^a-zA-Z0-9_]
。 如果使用了LOCALE
旗標,則會匹配在當前區域設置中不是字母數字又不是下劃線的字符。 -
\Z
只匹配字符串尾。
-
re.``A
-
re.``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.``I
-
re.``IGNORECASE
進行忽略大小寫匹配;表達式如
[A-Z]
也會匹配小寫字符。Unicode匹配(比如Ü
匹配ü
)同樣有用,除非設置了re.ASCII
標記來禁用非ASCII匹配。當前語言區域不會改變這個標記,除非設置了re.LOCALE
標記。這個相當於內聯標記(?i)
。注意,當設置了IGNORECASE
標記,搜索Unicode樣式[a-z]
或[A-Z]
的結合時,它將會匹配52個ASCII字符和4個額外的非ASCII字符: ‘İ’ (U+0130, 拉丁大寫的 I 帶個點在上面), ‘ı’ (U+0131, 拉丁小寫沒有點的 I ), ‘ſ’ (U+017F, 拉丁小寫長 s) and ‘K’ (U+212A, 開爾文符號).如果使用ASCII
標記,就只匹配 ‘a’ 到 ‘z’ 和 ‘A’ 到 ‘Z’ 。 -
re.``L
-
re.``LOCALE
由當前語言區域決定
\w
,\W
,\b
,\B
和大小寫敏感匹配。這個標記只能對byte樣式有效。這個標記不推薦使用,因爲語言區域機制很不可靠,它一次只能處理一個 "習慣”,而且只對8位字節有效。Unicode匹配在Python 3 裏默認啓用,並可以處理不同語言。 這個對應內聯標記(?L)
。在 3.6 版更改:re.LOCALE
只能用於byte樣式,而且不能和re.ASCII
一起用。在 3.7 版更改: 設置了re.LOCALE
標記的編譯正則對象不再在編譯時依賴語言區域設置。語言區域設置只在匹配的時候影響其結果。 -
re.``M
-
re.``MULTILINE
設置以後,樣式字符
'^'
匹配字符串的開始,和每一行的開始(換行符後面緊跟的符號);樣式字符'$'
匹配字符串尾,和每一行的結尾(換行符前面那個符號)。默認情況下,’^’
匹配字符串頭,'$'
匹配字符串尾。對應內聯標記(?m)
。 -
re.``S
-
re.``DOTALL
讓
'.'
特殊字符匹配任何字符,包括換行符;如果沒有這個標記,'.'
就匹配 除了 換行符的其他任意字符。對應內聯標記(?s)
。 -
re.``X
-
re.``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*")
python正則表達式中group
轉載龍蝦天天 最後發佈於2019-07-05 14:41:35 閱讀數 839 收藏
1 正則表達式中的(…)
用於匹配括號內的任何正則表達式,並且指明組的開始和結束位置;可以在執行匹配之後檢索組中的內容,並且可以在可以在字符串中使用\number來進行進一步的匹配,如下所述。當需要匹配字符’(‘或者’)’時,可以使用(和) ,或者[(]和[)]來實現。
以上便是Python3.6官方文檔對於(…)表達式的解說,該括號表達式用於定義一個group,一個正則表達式中可以有多個括號表達式,這就意味着匹配結果中可能有多個group,我們可以用group函數來定位到特定的group結果。
2 match.group([group1,…])
返回匹配結果中一個或多個group.如果該group函數僅僅有一個參數,那麼結果就是單個字符串;如果有多個參數,結果是每一個參數對應的group項的元組.如果沒有參數,那麼參數group1默認爲0(返回的結果就是整個匹配結果).如果參數值是0,那麼返回整個匹配結果的字符串;如果它是[1…99]之間的數字,則返回的是與對應括號組匹配的字符串(第一節中已經解釋了,正則表達式中的一個括號對應着一個group).如果組號爲負或大於模式中定義的組數,則會引發IndexError異常。如果某個group包含在匹配模式中但沒有找到相應的匹配,那麼對應的結果就是None.如果一個group是匹配模式中的一部分,並且匹配到了多次,那麼最後一次匹配結果將返回。
group和groups是兩個不同的函數。
一般,m.group(N) 返回第N組括號匹配的字符。
而m.group()
== m.group(0)
== 所有匹配的字符,與括號無關,這個是API規定的。m.groups() 返回所有括號匹配的字符,以tuple格式。
m.groups() == (m.group(1), m.group(2) ...)
import re
#定義了兩個group,因爲包含兩個括號
m = re.match("(\w+) (\w+)", "Isaac Newton, physicist")
#group(0)就是匹配的整個結果
print(m.group(0)) #輸出結果爲Isaac Newton
#group(1)是第一個group的值
print(m.group(1)) #輸出結果爲Isaac
#group(2)是第二個group的值
print(m.group(2)) #輸出結果爲Newton
#groups返回所有的group,以元組的形式
print(m.groups()) #輸出結果爲('Isaac','Newton')
1234567891011121314151617
3 findall(pattern, string, flag)
返回字符串中模式的所有非重疊匹配,結果以一個字符串list返回.字符串匹配過程是從左到右開始掃描,並且匹配結果按順序返回,如果該模式組存在一個或多個匹配,則返回匹配該組的一個列表;如果模式中有多個group,則返回的是一個元組的列表,元組中的元素依次對應各組的匹配結果.空的匹配包含在結果中除非它們開始了一個新的匹配過程。
可以看出,findall實質上也是一個group的匹配過程.其結果就是groups匹配結果的列表.
import re
if __name__ == '__main__':
#用於測試的字符串
str = "<h1>liuwei</h1><a href='www.baidu.com'></a><h1>zhangbin</h1><a href='www.love.com'></a>"
regex = re.compile("<h1>(.+?)</h1><a(.+?)></a>") #定義了兩個group,兩個括號
res = regex.search(str) #search用於找到第一個滿足匹配的子串,並返回
print("group1:%s" %res.group(1)) #輸出結果爲liuwei
print("group2:%s" %res.group(2)) #輸出結果爲href='www.baidu.com'
res1 = regex.findall(str) #findall輸出所有滿足的匹配
print("res1:%s" %res1)
print(res1[0]) #輸出結果爲('liuwei', 'www.baidu.com')
print(res1[1]) #輸出結果爲('zhangbin, 'www.love.com')
三 、 實例
檢查對子
例子
我們使用以下輔助函數來更好地顯示匹配對象:
def displaymatch(match):
if match is None:
return None
return '<Match: %r, groups=%r>' % (match.group(), match.groups())
假設你在寫一個撲克程序,一個玩家的一手牌爲五個字符的串,每個字符表示一張牌,“a” 就是 A, “k” K, “q” Q, “j” J, “t” 爲 10, “2” 到 “9” 表示2 到 9。
要看給定的字符串是否有效,我們可以按照以下步驟
ma4 = re.search(r'<([\w]+>)[\w]+</\1','<book>python</book>')
#\1表示使用編號爲1的分組,在前面有個括號([\w]+>) 這個括號內的就表示是編號爲1的分組,如果這個正則表達式中#有多個括號,就是說有多個分組,然後想複用第n個分組,就加一個\n,就OK了,不知道有沒有說清楚
ma4.group()
Out[63]: '<book>python</book>'
ma4.groups()
Out[64]: ('book>',)
>>> 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',)>"
要找出對子由什麼牌組成,開發者可以按照下面的方式來使用匹配對象的 group()
方法:
>>>
>>> pair = re.compile(r".*(.).*\1")
>>> 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>", line 1, 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 - 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
<re.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
<re.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
<re.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) 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 object; span=(0, 4), match=' ff '>
>>> re.match("\\W(.)\\1\\W", " ff ")
<re.Match object; span=(0, 4), match=' ff '>
當需要匹配一個字符反斜槓,它必須在正則表達式中轉義。在原始字符串記法,就是 r"\\"
。否則就必須用 "\\\\"
,來表示同樣的意思
>>>
>>> re.match(r"\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>
>>> re.match("\\\\", r"\\")
<re.Match object; span=(0, 1), match='\\'>
寫一個詞法分析器
一個 詞法器或詞法分析器 分析字符串,並分類成目錄組。 這是寫一個編譯器或解釋器的第一步。
文字目錄是由正則表達式指定的。這個技術是通過將這些樣式合併爲一個主正則式,並且循環匹配來實現的
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>%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)