Python實(shí)用(正則表達(dá)式)

原文鏈接

I did what I could, and I survived, there gotta be a reason.
我竭盡全力活了下來(lái),那就不能白活。 —《深海浩劫》


clock

使用正則表達(dá)式可以很方便的過(guò)濾、篩選我們需要的信息。

學(xué)習(xí)正則表達(dá)式是一個(gè)并不太愉快的過(guò)程,因?yàn)樗_實(shí)有點(diǎn)像“火星語(yǔ)”。為方便查詢(xún),先列出快速的‘元字符’查詢(xún)表(你可以先跳過(guò)這個(gè)表來(lái)學(xué)習(xí)):

表示法 表述 正則表達(dá)式式例
literal 匹配文本字符串的字面值 foo
re1 I re2 匹配正則表達(dá)式re1或re2 foo l bar
. 匹配任何字符串(除了\n之外) b.b
^ 匹配字符串起始部分 ^Dear
$ 匹配字符串終止部分 /bin/*sh$
* 匹配0次或多次前面出現(xiàn)的正則表達(dá)式 [A-Za-z0-9]
+ 匹配1次或多次前面出現(xiàn)的正則表達(dá)式 [a-z]+.com
? 匹配0次或1次前面出現(xiàn)的正則表達(dá)式 goo?
{N} 匹配N(xiāo)次前面出現(xiàn)的正則表達(dá)式 [0-9]{3}
{M,N} 匹配M~N次前面出現(xiàn)的正則表達(dá)式 [0-9]{5,9}
[...] 匹配來(lái)自字符集的任意單一字符 [aeiou]
[..x-y..] 匹配x~y范圍中的任意單一字符 [0-9],[A-Za-z]
[^...] 不匹配此字符集中出現(xiàn)的任何一個(gè)字符,包括某一范圍的字符 [aeiou],[A-Za-z]
(*l+l?l{})? 用于匹配上面頻繁出現(xiàn)符號(hào)的非貪婪版本(*、+、?、{}) .*?[a-z]
(...) 匹配封閉的正則表達(dá)式,然后另存為子組 ([0-9]{3})?,f(oo l u)bar
特殊符號(hào)
\d 匹配任何十進(jìn)制數(shù)字,和[0-9]相同(\D與\d相反,不匹配任何非數(shù)值型的數(shù)字) data\d.txt
\w 匹配任何字母數(shù)字字符,與[A-Za-z0-9_]相同(\W與之相反) [A-Za-z_]\w+
\s 匹配任何空格字符,與[\n\t\r\n\f]相同(與\S相反) of\sthe
\b 匹配一個(gè)單詞邊界,也就是指單詞和空格間的位置。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”(與\B相反) \bThe\b
\B 匹配非單詞邊界 “er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”
\N 匹配已保存的子組N(參加上面的(...)) price:\16
\c 逐字匹配任何特殊字符c .,\,*
\A(\Z) 匹配字符串的起始(參見(jiàn)上面^$) \ADear
擴(kuò)展表示法
(?iLmsux) 在正則表達(dá)式中嵌入一個(gè)或者多個(gè)特殊“標(biāo)記”參數(shù)(或者通過(guò)函數(shù)/方法) (?x),(?im)
(?:...) 表示一個(gè)匹配不用保存的分組 (?:\w+.)*
(?P<name>...) 像一個(gè)僅有name標(biāo)識(shí)而不是數(shù)字ID標(biāo)識(shí)的正則分組匹配 (?P<data>)
(?P=name) 在同一字符串中匹配由(?P<name)分組的之前文本 (?P=data)
(?#...) 表示注釋?zhuān)袃?nèi)容都被忽略 (?#comment)
(?=...) 如果...跟在字符串后面才做匹配,非獲取匹配;稱(chēng)作正向前視斷言 (?=.com)
(?!...) 如果...不跟在后面才做匹配操作,非獲取匹配;稱(chēng)作負(fù)向前斷言 (?!.net)
(?<=...) 如果...出現(xiàn)在字符串前面才做匹配,稱(chēng)作正向后視斷言 (?<=800-)
(?<!...) 如果...不出現(xiàn)在字符串前面才做匹配,稱(chēng)作負(fù)向后斷言 (?<!192.168.)
(?(id/name)Y l N) 如果分組提供的id或name(名稱(chēng))存在,就返回正則表達(dá)式的條件匹配Y,如果不存在,就返回N;l N是可選項(xiàng) (?(1)y l x)

只看上面的表格,還是不太會(huì)用,那么咱們來(lái)分開(kāi)一個(gè)個(gè)的說(shuō)說(shuō):

1.擇一匹配符號(hào)“|”
表示從多個(gè)模式中選擇一個(gè),用于分割不同的正則表達(dá)式。例如:

at | home    #可以匹配字符串a(chǎn)t或home

2.任意字符匹配符號(hào)“.”
“.”號(hào)可以匹配除了換行符以為的任何字符(Python正則表達(dá)式有一個(gè)編譯標(biāo)記[S或DOTALL]能夠使“.”匹配換行符),要匹配“.”號(hào)自身,必須使用反斜線轉(zhuǎn)譯符號(hào)“\.”。例如:

f.o  #能夠匹配f和o之間加上任意一個(gè)字符的樣式,如:fao、f9o、f#o
..   #能夠匹配任意兩個(gè)字符

3.**匹配字符串開(kāi)始“^”或結(jié)尾“”** 匹配字符串以什么開(kāi)始的,可以使用脫字符“^”或\A; 匹配字符串以什么結(jié)束的,可以使用美元符“”或\Z;

^From  #任何以From開(kāi)始的字符串
tcsh$  #任何以tcsh結(jié)尾的字符串
^subject:hi$   #任何由單獨(dú)的字符串subject:hi構(gòu)成的字符串

4.匹配單詞邊界:“\b”、“\B”
\b:匹配單詞的邊界(單詞前或后),而不在乎單詞中間的字符
\B:匹配單詞中間的字符,而不在乎單詞邊界的字符

er\b   #可以匹配“never”中的“er”,但不能匹配“verb”中的“er”,只關(guān)心后邊
er\B   #能匹配“verb”中的“er”,但不能匹配“never”中的“er”,只關(guān)心中間
\bthe  #匹配任何以the開(kāi)頭的字符串

5.字符集“[ ]”
當(dāng)想要匹配指定的某些字符的時(shí)候,使用字符集是很方便的。
注意:字符集只適用于單字符的情況。也就是說(shuō)[ab]表示只從ab中選擇一個(gè)

b[ae]t  #匹配bat、或bet
[01][ab]  #匹配0a、0b、1a、1b

6.字符集中的范圍“-”和否定“[^]”

z.[0-9]   #字母z后面跟著任何一個(gè)字符,然后跟著一個(gè)數(shù)字
[^aeiou]  #一個(gè)非元音字符
[^\t\n]   #不匹配制表符或\n
["-a]   #在一個(gè)ASCII系統(tǒng)中,位于“"”和“a”之間的字符,即34-97之間的字符

7.特殊符號(hào)、+、?、{}*
*:匹配其左邊的正則表達(dá)式出現(xiàn)零次或多次的情況。
+:匹配一次或多次出現(xiàn)的正則表達(dá)式。
?:匹配零次或一次出現(xiàn)的正則表達(dá)式。
{N}、{M,N}: 匹配前面的正則表達(dá)式N次或M~N次

[dn]ot?  #字母d或n后面跟一個(gè)o,然后后面最多再跟一個(gè)t。例如:do、no、dot、not
0?[1-9] #一個(gè)1到9的數(shù)字,前面跟或不跟一個(gè)0
[0-9]{15,16}  #匹配15或16個(gè)數(shù)字。例如信用卡號(hào)碼
</?[^>]+>  #匹配全部有效的(和無(wú)效的)HTML標(biāo)簽

8.特殊字符

\d: 十進(jìn)制數(shù)字
\w: 全部字母數(shù)字,相當(dāng)于[A-Za-z0-9]
\s: 空格字符
這些字符的大些表示不匹配對(duì)應(yīng)小寫(xiě)的內(nèi)容。

\w+-\d+  #一個(gè)由字母數(shù)字組成的字符串和一串由連字符分隔的數(shù)字
[A-Za-z]\w* #第一個(gè)是字母,其余是字母或數(shù)字
\d{3}-\d{3}-\d{4}  #美國(guó)電話號(hào)碼格式,例如800-555-1212
\w+@\w+\.com  #以xxx@yyy.com格式表示的簡(jiǎn)單電子郵件弟子

9.圓括號(hào)指定分組

有時(shí)候除了進(jìn)行匹配操作外,我們還想要提取所匹配的子組,例如:\w+-\d+,這個(gè)正則表達(dá)式想要分別保存第一部分的字母和第二部分的數(shù)字,該怎么實(shí)現(xiàn)?我們可能這樣做的原因是對(duì)于任何成功的匹配,我們想要看到匹配的字符串究竟是什么。如果為兩個(gè)子模塊都加上圓括號(hào),例如(\w+)-(\d),然后就能夠分別訪問(wèn)每一個(gè)匹配的子組。

\d+(\.\d*)?  #匹配浮點(diǎn)數(shù)的字符串,如:“5”、“5.”、“5.009”等

10.擴(kuò)展表示法

可以參考上面表格的講解結(jié)合下面的例子就能懂了:

Windows(?=95|98|NT|2000)  #能匹配“Windows2000”中的“Windows”,但不能匹配“Windows3.1”中的“Windows”
Windows(?!95|98|NT|2000)  #能匹配“Windows3.1”中的“Windows”,但不能匹配“Windows2000”中的“Windows”
(?<=95|98|NT|2000)Windows  #能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”
(?<!95|98|NT|2000)Windows  #能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”
industr(?:y|ies)  #就是一個(gè)比“industry|industries”更簡(jiǎn)略的表達(dá)式
 
(?:\w+\.)*  #以點(diǎn)結(jié)尾的字符串,如google.
(?#comment)  #不做匹配,只做注釋
(?=.com)  #一個(gè)字符串后面跟著.com才做匹配
(?!.net)  #一個(gè)字符串后面跟的不是.net才做匹配
(?<=800-)  #字符串前面出現(xiàn)800-才做匹配
(?<!192\.168\.)  # 字符串前面不是192.168。才做匹配,過(guò)濾掉一類(lèi)ip地址
(?(1)y|x)  #如果匹配組1(\1)存在,就與y匹配,否則就與x匹配

正則表達(dá)式和Python語(yǔ)言

Python語(yǔ)言中使用re模塊的方法支持正則表達(dá)式。這里列出re模塊常見(jiàn)的函數(shù)以方便查詢(xún)(后面會(huì)介紹主要的函數(shù)使用方法)

函數(shù) 描述
僅僅是re模塊函數(shù)
compile(pattern, flags = 0) 使用任何可選的標(biāo)記來(lái)編譯正則表達(dá)式的模式,然后返回一個(gè)正則表達(dá)式對(duì)象
re模塊函數(shù)和正則表達(dá)式對(duì)象的方法
match(pattern, string, flags = 0) 嘗試使用帶有可選的標(biāo)記的正則表達(dá)式的模式來(lái)匹配字符串。如果匹配成功就返回匹配對(duì)象,如果失敗就返回None
search(pattern, string, flags = 0) 使用可選標(biāo)記搜索字符串中第一次出現(xiàn)的正則表達(dá)式模式。如果匹配成功就返回匹配對(duì)象,如果失敗就返回None
findall(pattern, string [, flags]) 查找字符串中所有(非重復(fù))出現(xiàn)的正則表達(dá)式模式,并返回一個(gè)匹配列表
finditer(pattern, string[, flags]) 與findall()函數(shù)相同,但返回的不是一個(gè)列表,而是一個(gè)迭代器。對(duì)于每一次匹配,迭代器都返回一個(gè)匹配對(duì)象
split(pattern, string, max=0) 根據(jù)正則表達(dá)式的模式分隔符,split函數(shù)將字符串分隔為列表,然后返回成功匹配的列表,分隔最多操作max次(默認(rèn)分隔所有成功匹配的位置)
sub(pattern, repl, string, count = 0) 使用repl替換所有正則表達(dá)式的模式在字符串中出現(xiàn)的位置,除非定義count,否則就將替換所有出現(xiàn)的位置
purge() 清除隱式編譯的正則表達(dá)式模式
常用匹配對(duì)象方法
group(num = 0) 返回整個(gè)匹配對(duì)象,或者編號(hào)為num的特性子組
groups(default = None) 返回一個(gè)包含所有匹配子組的元組(如果沒(méi)有成功匹配,則返回一個(gè)空元組)
groupdict(default = None) 返回一個(gè)包含所有匹配的命名子組的字典,所有的子組名稱(chēng)作為字典的鍵(如果沒(méi)有匹配成功,就返回一個(gè)空元組)
常用模塊屬性(用于大多數(shù)正則表達(dá)式函數(shù)的標(biāo)記)
re.I、re.IGNORECASE 不區(qū)分大小寫(xiě)的匹配
re.L、re.LOCALE 根據(jù)所使用的本地語(yǔ)言環(huán)境通過(guò)\w、\W、\b、\B、\s、\S實(shí)現(xiàn)匹配
re.M、re.MULTILINE ^和$分別匹配目標(biāo)字符串中行的起始和結(jié)尾,而不是嚴(yán)格匹配整個(gè)字符串本身的起始和結(jié)尾
re.S、re.DOTALL "."(點(diǎn)號(hào))通常匹配除了\n之外的所有單個(gè)字符,該標(biāo)記表示"."能夠匹配全部字符
re.X、re.VERBOSE 通過(guò)反斜線轉(zhuǎn)譯,否則所有空格加上#(以及在該行中所有后續(xù)文字)都被忽略,除非在一個(gè)字符類(lèi)中或者允許注釋并提高可讀性

下面將分開(kāi)解釋上面的部分函數(shù):

1.使用match()和search()匹配字符串,使用group()查看結(jié)果


match() :從字符串開(kāi)始的位置匹配,成功返回匹配的對(duì)象,失敗返回None
search(): 掃描整個(gè)字符串來(lái)進(jìn)行匹配,成功返回匹配的對(duì)象,失敗返回None

例1:比較match() 和 search()的區(qū)別

import re

m = re.match('foo', 'seafood')
if m is not None: print("match-" + m.group())

m = re.search('foo', 'seafood')
if m is not None: print("search-" + m.group())

#結(jié)果是:search-foo

例2: match()函數(shù)從起始位開(kāi)始匹配

import re

m = re.match('foo', 'foo')
if m is not None:
    print("能匹配-" + m.group())

m = re.match('foo', 'bar')
if m is not None: print("不能匹配-" + m.group())

m = re.match('foo', 'food on the table')
if m is not None: print("從開(kāi)始位置進(jìn)行匹配-" + m.group())

#能匹配-foo
#從開(kāi)始位置進(jìn)行匹配-foo

例3: 匹配多個(gè)值(使用擇一表達(dá)式"|")

import re

bt = 'bat|bet|bit'

m = re.match(bt, 'bat')
if m is not None:
    print("1能匹配-" + m.group())


m = re.match(bt, 'blt')
if m is not None:
    print("2能匹配-" + m.group())


m = re.match(bt, 'he bit me')
if m is not None:
    print("3能匹配-" + m.group())


m = re.search(bt, 'he bit me')
if m is not None:
    print("4能匹配-" + m.group())


#結(jié)果:
#   1能匹配-bat
#   4能匹配-bit

例4: 匹配任何單個(gè)字符
點(diǎn)號(hào)"."除了換行符\n和非字符,都能匹配

import re

bt = ".end"

m = re.match(bt, 'bend')
if m is not None:
    print("bend能匹配-" + m.group())


m = re.match(bt, 'end')
if m is not None:
    print("end能匹配-" + m.group())


m = re.match(bt, '\nend')
if m is not None:
    print("\nend能匹配-" + m.group())


m = re.search(bt, 'the end.')
if m is not None:
    print("the end.能匹配-" + m.group())

#結(jié)果:
#   bend能匹配-bend
#   the end.能匹配- end

例5: 匹配小數(shù)點(diǎn)

import re

bt = "3.14"
pi_bt = "3\.14"  #表示字面量的點(diǎn)號(hào) (dec.point)

m = re.match(bt, '3.14')    #點(diǎn)號(hào)匹配
if m is not None:
    print("3.14能匹配-" + m.group())


m = re.match(pi_bt, '3.14')  #精確匹配
if m is not None:
    print("精確匹配-" + m.group())


m = re.match(bt, '3014')    #點(diǎn)號(hào)匹配0
if m is not None:
    print("3014能匹配-" + m.group())


#結(jié)果:
# 3.14能匹配-3.14
# 精確匹配-3.14
# 3014能匹配-3014

例6: 使用字符集"[ ]"

import re

bt = "[cr][23][dp][o2]"

m = re.match(bt, 'c3po')    #點(diǎn)號(hào)匹配
if m is not None:
    print("c3po能匹配-" + m.group())


#結(jié)果:
# c3po能匹配-c3po

例7: 重復(fù)、特殊字符

正則表達(dá)式: \w+@\w+.com可以匹配類(lèi)似nobody@xxx.com的郵箱地址,但是類(lèi)似nobody@xxx.yyy.aaa.com的地址就不能匹配了。這時(shí)候我們可以使用* 操作符來(lái)表示該模式出現(xiàn)零次或者多次:\w+@(\w+.)*\w+.com

例8: 分組

group()可以訪問(wèn)每個(gè)獨(dú)立的子組
groups()獲取一個(gè)包含所有匹配子組的元組

>>> import re
>>> m = re.match('(\w\w\w)-(\d\d\d)', 'abc-123')
>>> m.group()
'abc-123'
>>> m.group(1)
'abc'
>>> m.group(2)
'123'
>>> m.groups()
('abc', '123')

>>> m = re.match('ab', 'ab')
>>> m.group()
'ab'
>>> m.groups()
()

例9: 匹配字符串起始和結(jié)尾

m = re.search('^the','the end.')
>>> m.group()
'the'
>>> m = re.search('^the','sthe end.')
>>> m.group()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
>>> m = re.search(r'\bthe','bite the dog')
>>> m.group()
'the'

>>> m = re.search(r'\bthe','bitethe dog')
>>> m.group()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'

>>> m = re.search(r'\Bthe','bitethe dog')
>>> m.group()
'the'

2.使用findall()、finditer()查找每一次出現(xiàn)的位置

final() 以列表的形式返回所有能匹配的結(jié)果

>>> import re
>>> re.findall('car', 'car sscare')
['car', 'car']

finaliter()返回一個(gè)順序訪問(wèn)每一個(gè)匹配結(jié)果(Match對(duì)象)的迭代器

>>> re.finditer(r'(th\w+) and (th\w+)',s, re.I).next().group(1)
'This'
>>> re.finditer(r'(th\w+) and (th\w+)',s, re.I).next().group(2)
'That'

3.使用sub()和subn()搜索和替換

兩個(gè)函數(shù)都可以實(shí)現(xiàn)搜索和替換功能,將某字符串中所有匹配正則表達(dá)式的部分進(jìn)行某種形式的替換。不同點(diǎn)是subn()還返回一個(gè)表示替換了多少次的總數(shù),和返回結(jié)果一起以元組的形式返回。

>>> re.sub('[ae]','X','abcdef')
'XbcdXf'
>>> re.subn('[ae]','X','abcdef')
('XbcdXf', 2)

進(jìn)行替換的時(shí)候,還可以指定替換的順序,原理是使用匹配對(duì)象的group()方法除了能夠獲取匹配分組編號(hào)外,還可以使用\N,其中N表示要替換字符串中的分組的編號(hào),通過(guò)編號(hào)就能指定替換的順序。
例如:將美式日期MM/DD/YY{,YY}格式轉(zhuǎn)換成DD/MM/YY{,YY}格式

>>> re.sub(r'(\d{1,2})/(\d{1,2})/(\d{2}|\d{4})',r'\2/\1/\3','2/20/91')
'20/2/91'
>>> re.sub(r'(\d{1,2})/(\d{1,2})/(\d{2}|\d{4})',r'\2/\1/\3','2/20/1991')
'20/2/1991'

4.在限定模式上使用split()分隔字符串

re模塊的split()可以基于正則表達(dá)式的模式分隔字符串。但是當(dāng)處理的不是特殊符號(hào)匹配多重模式的正則表達(dá)式時(shí),re.split()和str.split()的工作方式相同,如下所示:

>>> re.split(':', 'str1:str2')
['str1', 'str2']
>>> 'str1:str2'.split(':')
['str1', 'str2']

但當(dāng)處理復(fù)雜的分隔時(shí),就需要比普通字符串分隔更強(qiáng)大的處理方式,例如下面匹配復(fù)雜情況:

>>> DATA = ('Mountation View, CA 94040', 'sunnyvale, CA', 'Los Altos, 94023', 'Palo Alto CA','Cupertino 95014')
>>> for datum in DATA: print(re.split(', |(?= (?:\d{5}|[A-Z]{2})) ',datum))
... 
['Mountation View', 'CA', '94040']
['sunnyvale', 'CA']
['Los Altos', '94023']
['Palo Alto', 'CA']
['Cupertino', '95014']

上述的正則表達(dá)式:當(dāng)一個(gè)空格緊跟在5個(gè)數(shù)字或2個(gè)字母后面時(shí)就用split語(yǔ)句分隔。當(dāng)遇到“,”也用split函數(shù)分隔。

5.擴(kuò)展符號(hào)

通過(guò)使用(?iLmsux)系列選項(xiàng),可以直接在正則表達(dá)式里面指定一個(gè)活著多個(gè)標(biāo)記。以下是使用re.I/IGNORECASE的示例,第二個(gè)是使用re.M/MULTILINE實(shí)現(xiàn)多行混合。

>>> re.findall(r'(?i)yes','yes? Yes. YES!!!')
['yes', 'Yes', 'YES']
>>> re.findall(r'(?i)th\w+','The quickest way is through this tunnel.')
['The', 'through', 'this']
>>> re.findall(r'(?im)(^th[\w ]+)', """
... This is the first,
... another line,
... that line,it's the best
... """)
['This is the first', 'that line']

通過(guò)使用“多行”,能夠在目標(biāo)字符串中實(shí)現(xiàn)跨行搜索,而不必將整個(gè)字符串視為單個(gè)實(shí)體。

下一個(gè)例子用來(lái)演示re.S/DOTALL,該標(biāo)記表示點(diǎn)號(hào)(.)能夠用來(lái)表示\n符號(hào)。

>>> re.findall(r'th.+',"""
... The first line
... the second line
... the third line
... """)
['the second line', 'the third line']
>>> re.findall(r'(?s)th.+',"""
... The first line
... the second line
... the third line
... """)
['the second line\nthe third line\n']

re.X/VERBOSE標(biāo)記允許用戶通過(guò)抑制在正則表達(dá)式中使用空白符來(lái)創(chuàng)建更易讀的正則表達(dá)式。

>>> re.search(r'''(?x)
... \((\d{3})\) #區(qū)號(hào)
... [ ]  #空白符
... (\d{3}) #前綴
... -  #橫線
... (\d{4}) #終點(diǎn)數(shù)字
... ''','(800) 555-1212').groups()
('800', '555', '1212')

(?:...)符號(hào)可以對(duì)部分正則表達(dá)式進(jìn)行分組,但是不會(huì)保存該分組用于后續(xù)的檢索或應(yīng)用。

>>> re.findall(r'http://(?:\w+\.)*(\w+\.com)',
... 'http://google.com http://www.google.com http://code.google.com')
['google.com', 'google.com', 'google.com']
>>> re.search(r'\((?P<areacode>\d{3})\) (?P<prefix>\d{3})-(?:\d{4})',
... '(800) 555-1212').groupdict()
{'areacode': '800', 'prefix': '555'}

可以同時(shí)使用(?P<name>)和(?P=name)符號(hào)。前者通過(guò)使用一個(gè)名稱(chēng)標(biāo)識(shí)符而不是使用從1開(kāi)始增加到N的增量數(shù)字來(lái)保存匹配,如果使用數(shù)字來(lái)保存匹配結(jié)果,我們就可以通過(guò)使用\1、\2、...,\N來(lái)索引,如下所示,可以使用一個(gè)類(lèi)似風(fēng)格的\g<name>來(lái)檢索它們。

>>> re.sub(r'\((?P<areacode>\d{3})\) (?P<prefix>\d{3})-(?:\d{4})',
... '(\g<areacode>) \g<prefix>-xxxx', '(800) 555-1212')
'(800) 555-xxxx'

使用后者,可以在同一個(gè)正則表達(dá)式中重用模式。例如,驗(yàn)證一些電話號(hào)碼的規(guī)范化。

bool(re.match(r'\((?P<areacode>\d{3})\) (?P<prefix>\d{3})-(?P<number>\d{4}) (?P=areacode)-(?P=prefix)-(?P=number) 1(?P=areacode)(?P=prefix)(?P=number)', '(800) 555-1212 800-555-1212 18005551212'))
True

使用(?x)使代碼更易讀:

>>> bool(re.match(r'''(?x)
... \((?P<areacode>\d{3})\)[ ](?P<prefix>\d{3})-(?P<number>\d{4})
... [ ]
... (?P=areacode)-(?P=prefix)-(?P=number)
... [ ]
... 1(?P=areacode)(?P=prefix)(?P=number)
... ''','(800) 555-1212 800-555-1212 18005551212'))
True

可以使用(?=...)和(?!...)符號(hào)在目標(biāo)字符串中實(shí)現(xiàn)一個(gè)前視匹配:

(?=...)字符串后面跟著...才適配

>>> re.findall(r'\w+(?= van Rossum)',
... '''
... Guido van Rossum
... Tim Peters
... Alex Martelli
... Just van Rossum
... Raymond Hettinger
... ''')
['Guido', 'Just']

(?!...)字符串后面不跟著...才適配:

>>> re.findall(r'(?m)^\s+(?!noreply|postmaster)(\w+)',
... '''
...  sales@phptr.com
...  postmaster@phptr.com
...  eng@phptr.com
...  noreply@phptr.com
...  admin@phptr.com
... ''')
['sales', 'eng', 'admin']

比較re.findall()和re.finditer()

>>> ['%s@awcom' % e.group(1) for e in re.finditer(r'(?m)^\s+(?!noreply|postmaster)(\w+)',
... '''
...  postmaster@phptr.com
...  noreply@phptr.com
...  admin@phptr.com
...  eng@phptr.com
...  sales@phptr.com
... ''')]
['admin@awcom', 'eng@awcom', 'sales@awcom']

條件正則表達(dá)式匹配,假定擁有一個(gè)特殊字符,它僅僅包含字母x和y,兩個(gè)字母必須由一個(gè)跟著另外一個(gè),不能同時(shí)擁有相同的兩個(gè)字母:

>>> bool(re.search(r'(?:(x)|y)(?(1)y|x)', 'xy'))
True
>>> bool(re.search(r'(?:(x)|y)(?(1)y|x)', 'xx'))
False

實(shí)例

在UNIX系統(tǒng)中,who命令會(huì)展示登錄的用戶信息。例如:

?  ~ who
sl       console  Nov 21 08:59 
sl       ttys000  Nov 21 09:09 
sl       ttys001  Nov 21 10:30 
?  ~ 

如果想按照空格(多個(gè),數(shù)量不確定)分隔的話,可以使用\s\s+,下面創(chuàng)建一個(gè)程序,將保存在文件whodata.txt中的數(shù)據(jù)讀出來(lái):
先將who的數(shù)據(jù)保存在whodata.txt文件中:

?  ~ who > /Users/sl/Desktop/whodata.txt

然后執(zhí)行下面的程序:

import re


f = open('whodata.txt','r')
for eachLine in f:
    print(re.split(r'\s\s+', eachLine))
f.close()

執(zhí)行結(jié)果:

['sl', 'console', 'Nov 21 08:59', '']
['sl', 'ttys000', 'Nov 21 09:09', '']
['sl', 'ttys001', 'Nov 21 10:30', '']

優(yōu)化上面的程序:

上面的程序,who命令是在腳本外部執(zhí)行的,每次手動(dòng)重復(fù)做這件事讓人很厭倦,我們可以通過(guò)調(diào)用os.popen()命令(現(xiàn)在已經(jīng)被subprocess模塊替代)將這個(gè)命令的執(zhí)行在腳本內(nèi)部實(shí)現(xiàn)。另外我們使用str.rstrip()去除尾部的\n,程序如下:

import re
import os

f = os.popen('who', 'r')
for eachLine in f:
    print(re.split(r'\s\s+|\t', eachLine.rstrip()))
f.close()

#結(jié)果:
['sl', 'console', 'Nov 21 08:59']
['sl', 'ttys000', 'Nov 21 09:09']
['sl', 'ttys001', 'Nov 21 10:30']

還可以使用with語(yǔ)句,可以使上下文管理對(duì)象變得更簡(jiǎn)易:

import re
import os

with os.popen('who', 'r') as f:
    for eachLine in f:
        print(re.split(r'\s\s+|\t', eachLine.rstrip()))

如果要適配python2和python3的話,可以避免使用print(),而使用兩個(gè)版本中都有的函數(shù)distutils.log.warn(),并將其轉(zhuǎn)換成printf名來(lái)使用。

import re
import os
from distutils.log import warn as printf
with os.popen('who', 'r') as f:
    for eachLine in f:
        printf(re.split(r'\s\s+|\t', eachLine.rstrip()))

生成隨機(jī)數(shù)的例子,用于希望練習(xí)從中匹配、搜索正則表達(dá)式使用:

from random import randrange, choice
from string import ascii_lowercase as lc
from sys import maxsize
from time import ctime

tlds = ('com', 'edo', 'net', 'org', 'gov')

for i in range(randrange(5, 11)):
    dtint = randrange(maxsize) / 3000000000
    dtstr = ctime(dtint)
    llen = randrange(4, 8)
    login = ''.join(choice(lc) for j in range(llen))
    dlen = randrange(llen, 13)
    dom = ''.join(choice(lc) for j in range(dlen))
    print('%s::%s@%s.%s::%d-%d-%d' % (dtstr, login, dom, choice(tlds), dtint, llen, dlen))

#隨機(jī)產(chǎn)生的結(jié)果
Mon Jun 24 08:07:21 2024::nunzkre@iqdhccpw.gov::1719187641-7-8
Sun May 21 12:23:33 2062::dxlyq@kupbixskweqj.edo::2915411013-5-12
Thu Sep  2 18:27:12 1999::vuhihly@hdgaimdma.com::936268032-7-9
Mon Jul 30 16:45:03 2007::vygxw@diwdeqkq.net::1185785103-5-8
Thu Jul  8 01:50:54 1971::mjxs@dmcuo.com::47757054-4-5
Thu Sep  1 03:02:30 2005::djld@eohculuz.gov::1125514950-4-8
Sun Nov 27 01:23:35 2011::cjvf@atvmdgxupi.gov::1322328215-4-10
Thu Aug  1 18:36:36 2024::phasko@flcfkvb.org::1722508596-6-7

想深入學(xué)習(xí)正則表達(dá)式高級(jí)用法推薦

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容