正則表達式有很多流派尚揣,也有很多的特性涌矢,不同的語言支持度也是不一樣的。本篇文章是寫Python中的正則表達式的用法的快骗,介紹了一些可用特性娜庇,也指出了某些特性是不支持的。
本篇文章僅為學(xué)習(xí)筆記方篮,不對正則表達式做深入研究名秀,只想用最短的時間入門正則。
實踐是檢驗真理的唯一標準藕溅。你以為的并不是你以為的匕得,如有問題還是要寫一個例子用多個工具驗證下最好。
字符組 [ ]
在同一位置可能出現(xiàn)的各種字符巾表,也可以稱之為字符集汁掠。
寫法有很多種。有原始寫法集币,然后是范圍表示法考阱,然后是排除型字符組,然后是字符組簡記法鞠苟,最后還有一個字符組運算(Python不支持)乞榨。
[3645970128] --> [0123456789] --> [0-9] --> \d
[0-9A-Za-z_] --> [\dA-Za-z_] --> \w
"-"用來表示范圍 范圍的確定依靠碼值來確定,一般據(jù)ASCⅡ表值当娱。
排除型字符組由脫字符"^"來指定吃既,"^"排在第一位,緊靠在"["之后跨细,需要排除的按上面寫法寫在"^"之后鹦倚。
[^0-9] # 排除數(shù)字,也可以寫成[^\d]
如果需要排除"-"冀惭,則必須將"-"寫在開頭"^"之后申鱼,如果一定要寫在后面,則必須用"\"轉(zhuǎn)義云头。
字符組簡記 | 說明 |
---|---|
\d | 數(shù)字(digit) [0-9] |
\w | 單詞(word) [0-9A-Za-z_] 字母數(shù)字下劃線 |
\s | 空白(space) [ \t\r\n\f\v] 空格、制表符淫半、回車溃槐、換行等顯示空白字符 |
\D 、\W 科吭、\S是與之對應(yīng)的排除型字符組簡記昏滴。
以上說的\d 猴鲫、\w 、\s的匹配規(guī)則都是針對ASCⅡ編碼規(guī)則而言的谣殊,也叫ASCⅡ匹配規(guī)則拂共。在Unicode編碼中,全角數(shù)字0姻几、1宜狐、2之類的也算數(shù)字,也可以由\d匹配蛇捌;中文字符也可以算是單詞字符抚恒,由\w匹配;同樣全角空格也由\s匹配络拌。
Python3默認采用Unicode匹配規(guī)則俭驮,但也可以顯示指定采用ASCⅡ匹配規(guī)則(表達式最開始用(?a)指定ASCⅡ模式)。
另外還有一個POSIX字符組(POSIX Character Class)春贸。POSIX(Portable Operating System Interface for uniX)混萝,它是含有正則表達式規(guī)范的一系列規(guī)范。這種字符組只在java萍恕、php逸嘀、ruby中支持,經(jīng)過測試以及查閱資料并沒有顯示Python是支持的雄坪,不過也有可能是POSIX字符組需要變化一下才能使用厘熟,就像其他語言那樣。
量詞
以上說的都是匹配單個字符维哈,如果多了怎么辦绳姨。所以就出現(xiàn)了量詞。
量詞限定之前的元素的出現(xiàn)阔挠,這個元素可能是一個字符飘庄,也可能是一個字符組,還可以是一個表達式购撼。
一般形式量詞 | 說明 |
---|---|
{n} | 之前的元素必須出現(xiàn)n次 |
{m,n} | 之前的元素最少出現(xiàn)m次跪削,最多出現(xiàn)n次 |
{m,} | 之前的元素最少出現(xiàn)m次,最高沒有限制 |
{0,n} | 之前的元素可以不出現(xiàn)迂求,也可以出現(xiàn)碾盐,最多出現(xiàn)n次 |
常用量詞 | 等價形式 | 說明 |
---|---|---|
* | {0,} | 可能出現(xiàn),也可能不出現(xiàn)揩局,出現(xiàn)次數(shù)沒有上限 |
+ | {1,} | 至少出現(xiàn)1次毫玖,最多沒有限制 |
? | {0,1} | 可以不出現(xiàn),也可以就出現(xiàn)1次 |
所有的量詞默認都是貪婪量詞,能匹配的付枫,絕不停止匹配烹玉。它會優(yōu)先嘗試匹配,并記下這個狀態(tài)阐滩,以便將來反悔二打。這個返回的過程稱之為回溯。
量詞也可以變成懶惰的掂榔,稱之為懶惰量詞继效,能不匹配就不匹配,如果之后的表達式匹配失敗衅疙,則會回溯進行匹配莲趣。變成懶惰量詞是在現(xiàn)有量詞后加上?
。
還有一種量詞稱之為占有量詞饱溢。它和貪婪量詞很像喧伞,但是占有量詞不會進行回溯,優(yōu)點是速度快绩郎,但是失敗也會很快潘鲫。變成占有量詞是在現(xiàn)有量詞后加上+
。
分組 ( )
分組肋杖,顧名思義就是將某些東西分成一組溉仑,對外來看是一個整體。通過()
將表達式括起來状植,此時表達式就是一個整體一個可整體操作的元素浊竟。括號內(nèi)的表達式稱之為“子表達式”。
多選結(jié)構(gòu)
多選結(jié)構(gòu)的形式是(···|···)
津畸,在括號中以豎線|
分隔開多個子表達式振定,這些子表達式也叫多選分支;在一個多選結(jié)構(gòu)中肉拓,多選分支的數(shù)目沒有限制后频。在匹配時,整個多選結(jié)構(gòu)被視為單個元素暖途,只要某個子表達式能夠匹配卑惜,整個多選結(jié)構(gòu)的匹配就成功。
一般而言ab|cd
依舊是多選結(jié)構(gòu)驻售,等價于(ab|cd)
露久,建議使用后者,更加明確欺栗,也能夠避免一些錯誤抱环。例如^ab|cd$
其實是(^ab|cd$)
而不是^(ab|cd)$
壳快,因為豎線|
的優(yōu)先級很低。
多選結(jié)構(gòu)的匹配順序問題镇草。比如表達式(aaa|aaabbb)去匹配aaabbb,匹配結(jié)果到底是aaa還是aaabbb呢瘤旨?Python中多選結(jié)構(gòu)都會優(yōu)先選擇最左側(cè)的分支梯啤,所以匹配出來是aaa。不過在實際使用中請避免這種情況存哲,分支中存在重復(fù)會大大增加回溯的計算量因宇。
引用分組
分組之后,正則表達式會保存每一個分組真正匹配的文本(不是正則表達式可匹配的文本)祟偷,匹配完成后察滑,可通過編號進行引用當(dāng)時捕獲的內(nèi)容。
如果出現(xiàn)多個嵌套括號修肠,那編號的計數(shù)規(guī)則是怎樣的呢贺辰?首先整個正則表達式默認存在一個的編號為0的分組,其他分組的編號是依據(jù)從左到右開括號出現(xiàn)的順序決定的嵌施,第幾個括號就是第幾組饲化。
-
反向引用
在正則表達式內(nèi)部引用之前的捕獲分組匹配的文本,之前捕獲分組中錨點表示的位置信息不會被保留下來吗伤。
引用形式:
\num
吃靠,其中num表示所引用分組的編號。 -
sub替換中引用
re.sub(pattern,replacement,string)足淆,在replacement中使用引用分組巢块。引用形式同樣是:
\num
。 -
匹配結(jié)果中g(shù)roup引用
對匹配完成后巧号,可對匹配結(jié)果對象調(diào)用group(num)獲取分組中匹配的數(shù)據(jù)族奢。
\num引用的二義性問題:\10
是第10個引用還是第1個引用后再加一個0呢?
Python中將其解釋為第10個引用裂逐,可通過\g<num>
歹鱼,消除二義性。
注:反向引用只引用之前的捕獲分組匹配的文本卜高,之前捕獲分組中的錨點表示的位置信息不會被保留下來弥姻。
命名分組
鑒于數(shù)字編號不夠直觀,并且括號多了難免混淆掺涛,所以就有了命名分組的出現(xiàn)庭敦。Python中用(?P<name>...)
來分組,其中name是分組的名稱薪缆。
反向引用必須使用(?P=name)
來引用秧廉;替換則需要寫作\g<name>
伞广;匹配結(jié)果中則使用group(name)
。
非捕獲分組
無論是否需要分組疼电,只要出現(xiàn)了括號嚼锄,正則表達式再匹配的時候就會把括號內(nèi)的子表達式存儲起來,提供引用蔽豺。如果并不需要引用区丑,保存這些信息無疑會影響正則表達式的性能。
正則表達式提供了非捕獲分組(?:...)
修陡,這樣的括號叫做非捕獲型括號沧侥,他只能限定量詞的作用范圍,不捕獲任何文本魄鸦。在引用分組時宴杀,分組的編號同樣會按照開括號出現(xiàn)的順序從左到右計數(shù),非捕獲分組會略過拾因。
原子分組
另一種非捕獲分組就是原子分組(?>...)
旺罢。這種分組可以將回溯操作關(guān)閉,但它只針對原子分組內(nèi)的部分盾致,而不針對整個正則表達式主经。經(jīng)過測試發(fā)現(xiàn)這個表達式?>
是不識別的。但是一篇文章(英文原版)給出了一個等價方案:
可以通過使用零寬度先行assert(
(?= RE)
)來模擬它,它從當(dāng)前點匹配相同的語義想要,將一個命名的組((?P RE)
)放在lookahead中,然后使用一個命名的反引用((?P = name)
恰恰是零寬度斷言匹配.組合在一起,這給了你相同的語義,代價是創(chuàng)建一個額外的匹配組和很多語法.
匹配模式
所謂匹配模式庭惜,指的是匹配時使用的規(guī)則罩驻。設(shè)定特定的模式,可能會改變對正則表達式的識別护赊,也可能會改變正則表達式中字符的匹配規(guī)定惠遏。常用的匹配模式一共有4種:不區(qū)分大小寫模式、單行模式骏啰、多行模式节吮、注釋模式。通常有兩種指定方式:以模式修飾符指定(?modifier)
判耕,或者以預(yù)定義的常量作為特殊參數(shù)傳入來指定透绩。
Python中模式修飾符只要出現(xiàn),無論在什么位置壁熄,都對整個正則表達式生效帚豪。Python中不支持失效修飾符(?-modifier)
。
不區(qū)分大小寫模式
不區(qū)分大小寫模式的修飾符是i
(case Insensitive)草丧,則(?i)it
可以匹配的有it It iT IT
狸臣。
單行模式
修飾符是s
(Single line)。
這種模式下所有的文本只在一行里昌执,換行符只是其中一個普通字符烛亦。單行模式影響的是點號.
的匹配規(guī)則诈泼。
多行模式
修飾符是m
(Multiline)。
多行模式影響的是^
和$
的匹配規(guī)則煤禽。
注釋模式
修飾符是x
(eXtended mode 擴展模式)铐达。
有時正則表達式可能非常復(fù)雜,不但難于編寫和閱讀檬果,也難以維護娶桦。python中支持使用(?#comment)
的記法添加注釋;還有一種是使用注釋模式汁汗,此時正則表達式對應(yīng)的字符串可以跨越多行,注釋則以#comment
的形式添加在正則表達式內(nèi)部栗涂,每一條注釋從#
開始知牌,到行末結(jié)束,同時還可以使用縮進表示層級結(jié)構(gòu)更加方便閱讀和維護斤程。
斷言
常見的斷言有三類:單詞邊界角寸、行起始/結(jié)束位置、環(huán)視忿墅。斷言并不真正的匹配文本扁藕,他匹配的是位置,他只負責(zé)判斷在某個位置左/右側(cè)的文本是否符合要求疚脐。
單詞邊界
單詞邊界記為\b
亿柑,他匹配的是“單詞邊界”位置,而不是字符棍弄。也就是說\b
能夠匹配這樣的位置:一邊是單詞字符望薄,另一邊不是單詞字符(包括沒有字符)。不同的模式下\w
不同呼畸,所以\b
也就不同痕支。
另外還有\B
非單詞邊界。
行起始/結(jié)束為位置
^
匹配字符串開始的位置蛮原。
$
匹配字符串結(jié)束的位置卧须。
前面已經(jīng)說了:多行模式影響的是^
和$
的匹配規(guī)則。
在不指定多行模式的情況下:^
匹配的是整個字符串的開始位置儒陨,$
匹配的是整個字符串的結(jié)束位置花嘶。
當(dāng)在多行模式下:^
匹配的是字符串的起始位置以及換行之后行終止符之后的位置,如果字符串末尾出現(xiàn)了行終止符框全,則依舊匹配行終止符之后的那個位置察绷;$
匹配的是字符串的結(jié)束位置,但是這個位置是在字符結(jié)束之后行終止符之前的位置津辩。
python中還有一個特殊標記\Z
拆撼,它和$
類似容劳,但是他不受多行模式的影響,\Z
不管行終止符闸度,他匹配的是整個字符串的結(jié)束位置竭贩,在行終止符之后的位置。
^
和$
的另一個特點是在進行正則表達式替換的時候并不會被替換莺禁。也就是說留量,在起始和結(jié)束位置進行替換,只會在起始和結(jié)束位置添加一些字符哟冬,位置本身依然存在楼熄。
環(huán)視
環(huán)視是對匹配字符周圍字符的驗證,他匹配的并不是字符浩峡,而是位置可岂。
環(huán)視大致分為以下四種:
名稱 | 又稱 | 說明 |
---|---|---|
肯定順序環(huán)視 | 正前瞻 | ...(?=...) |
否定順序環(huán)視 | 反前瞻 | ...(?!...) |
肯定逆序環(huán)視 | 正后顧 | (?<=...)... |
否定逆序環(huán)視 | 反后顧 | (?<!...)... |
肯定環(huán)視和否定環(huán)視的區(qū)別就是:肯定環(huán)視要想判斷成功,字符串中必須有字符由環(huán)視結(jié)構(gòu)中的表達式匹配翰灾;而否定環(huán)視要判斷成功卻有兩種情況1.字符串的字符不能匹配2.根本就沒有字符缕粹,這個位置可能是起始或結(jié)束位置。
環(huán)視的組合:
-
包含
環(huán)視中包含環(huán)視纸淮,這個和環(huán)視的并列還不太一樣平斩,這個外層環(huán)視是限定的位置,但是這個內(nèi)層環(huán)視是限定外層環(huán)視的咽块。
(?=...(?!...))
-
多環(huán)視并列
環(huán)視的并列要求每一個環(huán)視對當(dāng)前位置的匹配都必須成功绘面,而各個環(huán)視之間是沒有任何聯(lián)系的,他們都將作用于該位置糜芳。
-
環(huán)視作為多選分支
將若干個環(huán)視作為多選分支排列在多選結(jié)構(gòu)中飒货,只要有一個環(huán)視分支成立,整個多選就成立峭竣。
分組的編號只與捕獲型括號相關(guān)塘辅,而不受其他任何括號類型的影響,所以環(huán)視結(jié)構(gòu)的括號并不影響分組皆撩。但是環(huán)視結(jié)構(gòu)中出現(xiàn)了捕獲型括號扣墩,則會影響分組。
環(huán)視結(jié)構(gòu)中的捕獲型括號一旦匹配完成扛吞,就不能回溯呻惕。e.g.(?<=(\d+))\w+\1
無法在123a12
中找到匹配。
Python支持順序環(huán)視滥比,但對于逆序環(huán)視亚脆,Python只支持匹配固定長度文本表達式。(?<=dogs?)
和(?<=(dog|cats))
都是不合法的盲泛,遇到這樣的可與選擇多選結(jié)構(gòu)來改造濒持,但是如果出現(xiàn)*和+之類的量詞键耕,就不能用多選了。
至此柑营,整個正則表達式部分就介紹完畢屈雄,以下部分Python正則API。
Python中的正則表達式模塊是re模塊官套,通過import re 導(dǎo)入re模塊來使用正則表達式酒奶。
在正則表達式中,大多數(shù)的函數(shù)是類似re.search(pattern,string)的形式奶赔,其中pattern通常是正則表達式的字符串惋嚎,這可以視為“靜態(tài)方法”。其實也可以將RegexObject對象作為pattern參數(shù)傳入站刑,或者直接對RegexObject對象調(diào)用相同名字的成員方法瘸彤,結(jié)果相同,但是后面這兩個方法可以顯示的緩存RegexObject對象笛钝。
除了正則表達式對象RegexObject,還有一個MatchObject對象愕宋,MatchObject對象提供了一些方法和屬性玻靡,通過他們可以獲取匹配的信息。
方法 | 描述 | 注釋 |
---|---|---|
start(n) | 返回編號為n的捕獲分組匹配的開始位置中贝,如果對應(yīng)分組不存在或者未匹配囤捻,則返回-1 | 如果沒有設(shè)定n,則返回整個表達式匹配的開始位置邻寿,此時n等于0 |
pos | 返回整個表達式匹配的開始位置 | |
end(n) | 返回編號為n的捕獲分組匹配的結(jié)束位置蝎土,如果對應(yīng)分組不存在或未匹配,則返回-1 | 如果沒有設(shè)定n形病,則返回整個表達式匹配的結(jié)束位置锥累,此時n等價于0 |
endpos | 返回整個表達式匹配的結(jié)束位置 | 等價于end(0) |
group(n) | 返回編號為n的捕獲分組匹配的文本 | 如果沒有設(shè)定n奇颠,則返回整個表達式匹配的文本,此時n等價于0 |
groups() | 返回各捕獲分組匹配的文本構(gòu)成的tuple | 如果表達式中不存在捕獲分組暴构,則返回空tuple |
span(n) | 返回編號為n的捕獲分組匹配的開始-結(jié)束位置 | 如果沒有設(shè)定n,則返回整個表達式匹配的開始-結(jié)束位置段磨,此時n等價于0 |
re | 返回匹配時所使用的正則表達式對象 | |
lastindex | 返回匹配成功的編號最大的捕獲分組的編號 | 如果沒有取逾,則返回None |
string | 返回用來嘗試用正則表達式來匹配的字符串 | |
expand(str) | 返回一個字符串,可以在其中引用捕獲分組匹配的文本 |
-
re.compile(regex [, flags])
這個方法用于顯示“編譯”正則表達式對象苹支。如果需要多次使用同一個正則表達式砾隅,那么每次使用編譯好的正則表達式對象比每次臨時編譯表達式對象要快得多。另外compile()還有一個功能债蜜,那就是添加第二個參數(shù)re.DEBUG來觀察整個正則表達式的詳細信息晴埂。
-
re.search(pattern, string [, flags])
這個方法用來測試正則表達式pattern能否在string中找到匹配究反,flags是可選參數(shù)是匹配模式(aiLmsux)的按位OR結(jié)果。如果能找到匹配邑时,則返回MatchObject奴紧,否則返回None。
-
re.match(pattern, string [, flags])
re.match()和re.search()非常相似晶丘,參數(shù)相同黍氮,返回值也行通,非常容易混淆浅浮,唯一的區(qū)別在于:如果re.match()匹配成功沫浆,那么pattern匹配的字符串必須開始于string的最左端。也就是說滚秩,re.match()只會在字符串的開始的位置嘗試匹配专执,re.search()則沒有這個限制。MULTILINE 多行模式下郁油,match也只匹配string的開頭部分本股。
-
re.findall(pattern, string [, flags])
這個函數(shù)會一次性的找出正則表達式pattern在string中的所有匹配,返回一個list桐腌。
如果pattern中存在捕獲分組拄显,則返回list的元素并非整個表達式所匹配的文本,而是各個捕獲分組所匹配文本構(gòu)成的tuple案站。返回的tuple中是沒有默認的分組編號0所匹配的文本元素的躬审,如果需要返回可以在整個正則表達式外再加一個括號。tuple中文本的順序就是分組編號的順序蟆盐。
-
re.finditer(pattern, string [, flags])
如果文本內(nèi)容很多承边,使用re.findall()一次性找到所有匹配的成本可能很高,使用re.finditer()可以返回一個迭代器(iterator)石挂,他可以按照從左到右的順序逐個遍歷每次匹配的MatchObject對象博助,依次進行處理或檢驗。
-
re.split(pattern, string [, maxsplit=0, flags=0])
這個函數(shù)用正則表達式pattern能夠匹配的文本切分字符串string痹愚。如果正則表達式在開頭或者結(jié)尾位置找到匹配翔始,那么結(jié)果的開頭或結(jié)尾會出現(xiàn)空字符串。也可以明確設(shè)定maxsplit里伯。在Python中這個參數(shù)表示切分的次數(shù)城瞎。也就是說,返回數(shù)組一般會包含maxsplit+1個元素疾瓮。如果maxsplit設(shè)定負數(shù)脖镀,則表示不做任何切分;若設(shè)定0,則等于沒有設(shè)定蜒灰;若設(shè)定的值為正數(shù)弦蹂,且小于實際可切分的次數(shù),則按設(shè)定的值進行切分强窖,若大于實際可切分的次數(shù)凸椿,則以實際可切分的次數(shù)為準。
-
re.sub(patern ,repl, string [, count=0, flags=0])
這個函數(shù)用來進行正則表達式替換翅溺,它將字符串string中的正則表達式pattern能夠匹配的文本替換為repl指定的文本脑漫。如果要在replacement字符串中引用之前的某個捕獲分組匹配的文本,則應(yīng)該使用
\num
咙崎。Python中也提供了跟清晰的方式在repl中引用分組优幸,寫作\g<num>
,這種記法也可以使用命名分組\g<name>
褪猛。repl也可以是一個函數(shù)网杆,他接受一個MatchObject對象,返回一個String對象伊滋。也可以設(shè)定count參數(shù)碳却,他指定替換操作最多能發(fā)生的次數(shù)。 -
re.subn(patern ,repl, string [, count=0, flags=0])
與sub()相同笑旺,但返回一個元祖追城,其中包含新字符串和替換次數(shù)。
-
re.escape(string)
返回一個字符串燥撞,其中所有的非字母數(shù)字字符都帶有反斜杠。