1. 程式人生 > >《精通正則表示式》學習筆記

《精通正則表示式》學習筆記

一個重要且常見的問題:

寫正則表示式時,我們需要在對欲檢索的文字的瞭解程度和檢索精準度之間求得平衡。: 越精準,容錯性越差;但是容錯性越好,越容易出現異常。所以,我們需要匹配所有需要匹配的,同時,忽略掉所有不需要匹配的。好像是句廢話,實際是精髓。

一,正則表示式入門:

1.      字元組:

[123] : 1或2或3

[^123] :除123之外的任何字元,這裡的字元是指廣義字元。包括:[email protected]#$%^&*()等。

[^1] : 意義是:匹配一個除1之外的字元,而不是不要匹配字元1.

舉例,請注意二者的差別:

re.findall('(f[^1])','asdfasdf')

['fa']

re.findall('(f[^1])','asdfasdf\n')

['fa', 'f\n']

[^-k] : 當”-”出現在[或[^之後,元字元”-”失效。

舉例:

re.findall('([^-k])','dfdfdf')

['d', 'f', 'd', 'f', 'd', 'f']

2.      多選結構:“|”

‘123|456’ : 123或456

grey|gray : grey 或 gray

舉例:

re.findall('grey|gray','graygrey')

['gray', 'grey'] : grey 或 gray

re.findall('gr[ea]y','graygrey')

['gray', 'grey'] : [ea]:e或a

re.findall('gre|ay','graygrey')

['ay', 'gre'] : 'gre’ 或 ’ay’

re.findall('(gr(?:e|a)y)','graygrey')

['gray', 'grey'] : (gr(?:e|a)y) 相當於gr(e|a)y :grey 或 gray

re.findall('gr[e|a]y','graygrey')

['gray', 'grey'] : [e|a] 相當於 e或”|”或a

re.findall('gr[e|a]y','graygreygr|y')

['gray', 'grey', 'gr|y']

注意re.compile(‘^a|b|c’)和re.compile(‘^(a|b|c)’)之間的差別。

前者指’^a’或’b’或’c’。

後者指’^a’或’^b’或’^c。

3.      正則中還有一個“單詞錨定”的概念,但python似乎不支援,也許我沒找到。

4.      量詞? * +

re.compile(‘4(th)*’) 若干個th

5.      括號及反向引用

前文所述,括號有兩個作用:

a)        re.compile(‘^(1|2|3)’) 針對多選項

b)        re.compile(‘4(th)?’) 針對量詞

括號的第三個作用:反向引用

直接上例子:

('1',)

後面的\1匹配前面括號中匹配的東西”\d”. 有點繞口,在上幾個例子:

re.search(r'(\d)abc\1','2abc2').groups()

('2',)

re.search(r'(\d)abc\1','1abc2').groups()

Traceback (most recentcall last):

  File "<interactive input>",line 1, in <module>

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

沒匹配上,出了異常,因為前面是1後面是2. '1abc2'

6.      python中反向引用的擴充套件:

re.compile(r'(?P<kk>\d)abc(?P=kk)').search('1abc1').groups()

('1',)

(?P<kk>\d):給這個group加一個名字<kk>

(?P=kk):獲取已匹配的內容。

在來一個例子:

re.compile(r'(?P<asdf>\d)abc(?P=asdf)').search('1abc1').groups()

('1',)

在一個group裡面的前兩個字元是”?:”,那麼這個括號不被正則引擎記入。。。有點繞後,希望這個例子能說明白。

re.compile(r'(?:\d)(\d)abc\1').search('12abc22').groups()

('2',)

例子中的\1 匹配的是第二個括號中的\d而不是第一個。因為第一個group中有一個”?:”被忽略掉了。

這個例子舉的不好。因為這樣也可以:

re.compile(r'\d(\d)abc\1').search('12abc22').groups()

('2',)

實際上”?:”這個東西主要用在上文中括號的第一和第二個作用(繫結/捆綁),又不想被記入第三個功能(反向引用)的時候使用的。。。希望我說明白了。 

小結:

二,擴充套件

順序環視 lookahead:

    肯定型順序環視:

逆序環視:

    否定性逆序環視:

這塊兒有點煩。1,難。2,用的不多。3,但很重要。

舉例之前,還是簡單介紹一下吧。

前文曾經介紹過元字元^和&.

^表示 待匹配文字的開始

$表示 待匹配文字的結尾

像^,$這種元字元和perl中的\b,叫做錨定位。錨定就是錨一個位置,定住(廢話!)。錨定位就是一個位置識別符號,但是並不佔用待匹配的內容。我舉個例子:

re.compile('^qweqweqwe')

這句話的意思是說匹配以字母q開頭,後面跟著weqweqwe。^只是一個位置識別符號,表示起始位置,並不佔用內容。

如果你想匹配” qweqweqwe”。正確。可匹配成功。

如果你想匹配”aqweqweqwe”.錯誤。因為開頭是字母a 不是q。

不知道是否有同學會思考,如果我想匹配”^qweqweqwe”怎麼辦?就不告訴你~!

上段介紹了錨定的概念是為了引出這節將要介紹的環視。

其實環視我個人的理解就是一個變相的錨定。

舉例子:

肯定型順序環視:

re.search('(?=Linshuhao)Linsh','sjkjsLinshuhao')

<_sre.SRE_Match object at 0x029BA1E0>

匹配成功。

解釋一下:

'(?=Linshuhao)Linsh' 這個是正則的定義。我們把它拆開:

(?=Linshuhao) 這個就是上文開頭那個表中的第一項:肯定型順序環視。

Linsh 這個就是想要去匹配的字元了。

'sjkjsLinshuhao' 這個是被匹配的文字。我要拿正則去匹配它。

放一起,解釋一遍:

(?=Linshuhao)這個意思是錨定一個位置,哪個位置呢:'sjkjsLinshuhao' 就是我剪頭指向的位置。

那麼'(?=Linshuhao)Linsh 的意思就是在這個位置從左向右匹配Linsh。

舉個反例:

re.search('Linsh','sjkjsLinshaahahahahha')== None

False

False意味著不等於None,意味著匹配成功了。

因為'sjkjsLinshaahahahahha'這個字串中確實存在Linsh這個字串。

re.search('(?=Linshuhao)Linsh','sjkjsLinshaahahahahha')== None

True

但是這個失敗了。因為(?=Linshuhao),的意思是錨定位置到Linshuhao的起始位置。但是整個文字並沒有這個位置。

在舉一組例子:

記住,錨定位置!!!

re.search('123(?=Linshuhao)','sdakj123Linshuhao')== None

False

匹配成功。

兩種理解方法,一個是官方的,一個是我的。

先說官方的:

首先錨定位置,(?=Linshuhao) 錨定的就是Linshuhao的前端,就是箭頭所指的位置:

'sdakj123 Linshuhao'

'123(?=Linshuhao)' 整個放一起的意思就是123後面是那個錨定的位置。哪個錨定的位置?就是Linshuhao的起始位置。也就是箭頭指向的那個位置。

我的理解:

'123(?=Linshuhao)'  123後面必須是Linshuhao,別的不匹配。

re.search('123(?=Linshuhao)','sdakj123fsdsaLinshuhao')== None

True

總結:(?=Jeffrey)Jeff 的匹配:

否定型順序環視:

False

還拿這個舉例子

肯定型順序環視是指123後面必須是Linshuhao。否定型的意思就是123後面必須不是Linshuhao。

否定型的表示式是這樣的:

re.search('123(?!Linshuhao)','sdakj123Linshuhao')== None

True

re.search('123(?!Linshuhao)','sdakj123sdlLinshuhao')== None

False

自己感受下。。。

思考題:

re.search('123(?!=Linshuhao)','sdakj123Linshuhao')== None

True or False?

肯定型/否定型逆序環視:

下面重點介紹逆序環視:

<_sre.SRE_Match object at 0x029BA758>

匹配成功了。逆序環視匹配的是'Linshuhao123'

為了方便對比給出順序環視的錨定位:

re.search('123 (?=Linshuhao) ', ‘123Linshuhao')

'123Linshuhao'

肯定型和否定型大家應該很清楚了。給一個否定型的寫法吧。

re.search('(?<!Linshuhao)123','Linshuhao123')

環視的實際應用:

在《精通正則學習筆記1》中,我沒有找到python支援的單詞錨定元字元。在這裡。我們可以應用環視,人為寫出,相當於perl的\b的表示式:

1.表示字元的前端的錨定位

re.compile(r'(?<!\w)(?=\w)123')

<_sre.SRE_Pattern object at0x029D04D0>

2.表示字元的後端的錨定位

re.compile(r'123(?<=\w)(?!\w)')

<_sre.SRE_Pattern object at0x029C7340>

3.相容前端和後端

re.compile(r'(?:(?<!\w)(?=\w))|(?:(?<=\w)(?!\w)'))

第二章,到此結束。

三,正則表示式的特性和流派概覽

1.      正則模式和匹配模式

兩種表達方式,各舉一個例子:

a.      re.compile('123123',re.I|re.M)

b.      re.search('(?im)123123','123123123123123')

下面分別解釋python支援的各個模式的意義:

re.I (ignore case):忽略大小寫。

re.L (locale dependent):和re.U對應。一個是local 一個是unicode。本人猜測預設應該是local的。Unicode模式是指,unicode格式,兩個字元連一起組成組合表示一個字元。從而影響到\w, \W,\b, \B, \d,\D, \s and \S。例如,\w可以匹配兩個字元。猜測是這個意思。以後如果遇到,再說。

re.M (multi-line):多行模式,又叫增強的行錨點模式。呸呸呸。

^和$ 不在這個模式下,只匹配字串的開始和結束位置。

但在多行模式下,^匹配的是一行的開始位置。同理$匹配一行的結束位置。

舉例:

re.findall('(?m)^(\d{3})$','123\n456\n789')

['123', '456', '789']

re.findall('^(\d{3})$','123\n456\n789')

[]

自己體會一下。

多行匹配模式,是所有模式中應用最廣泛的。

猶豫在該模式下,^和$只能匹配行首和行尾。字串的首尾由元字元\A,\Z替換:

\A:指定匹配必須出現在字串的開頭(忽略  Multiline   選項)。

\Z:指定匹配必須出現在字串的結尾(忽略  Multiline   選項)。

總結一下:

^:行首。&:行尾。\A:總字串首。\Z:總字串尾。

自己體會一下。

re.S (dot matches all):點號通配模式。

通常情況下“.”匹配所有字元,除了\n。

但是在點號通配模式下。”.” 匹配所有字元,包括\n.

舉例:

這條語句曾經出現在driver management中的*common.py中,但是被扼殺了。

re.search('(?ms).*DeviceDesc%=.+?,(.*?)\s.*[Strings].*DeviceDesc\s*=\s*"(.*?)"\r\n',tmpString).groups()

re.X (verbose):

回車換行,空格都無效。

下面兩句話表示一個意思。

a = re.compile(r"""\d +  # the integral part

                  \.    # the decimal point

                  \d *  # some fractionaldigits""", re.X)

b = re.compile(r"\d+\.\d*")

2.      元字元+一些關鍵字總結:

a.      字元組:強調幾點。

1.元字元在字元組內/外意義不同。也可能失效。例如[*]只代表”*”並不是量詞。[\b]表示退格。\b表示。。。。

2.[^asdasd],字元組中的字元是指廣義字元,包括且不限於@#$%^&*()_

3.排除型字元組[^asdasd]也表示仍然需要匹配一個字元。

b.      點號.:在大多數情況下,“.” == [^\n].在多行模式下“.”可以匹配任何字元(廣義)。

c.      \d \D \w \W \s \S:自己百度。

d.      錨點: ^ $ \A \Z

e.      單詞分解符 \b \B ,這裡要解釋一下,在《1》中曾經說python 不支援單詞分解符,其實是支援的。

f.       環視 (?= … ) (?! …) (?<= …) (?<! …)

g.      (?: …) 只分組不捕獲

h.      命名捕獲:兩種情況,

1,需成對出現:

re.search('((?P<qwe>\d))kk(?P=qwe)','1kk1').groups()

('1', '1')

re.search('(?P<qwe>\d)kk(?P=qwe)','1kk1').groups()

('1',)

re.search('(?P<qwe>\d)kk((?P=qwe))','1kk1').groups()

('1', '1')

re.search('(?P<qwe>\d)kk(?:(?P=qwe))','1kk1').groups()

('1',)

re.search('(?:(?P<qwe>\d))kk(?:(?P=qwe))','1kk1').groups()

('1',)

re.search('(?:(?P<qwe>\d))kk','1kk1').groups()

('1',)

2,單獨出現亦可:

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

m.group('first_name')

'Malcolm'

m.group('last_name')

'Reynolds'

re.search('(?P<qwe>\d)kk','1kk1').groupdict()

{'qwe': '1'}

i.        固化分組,和正則引擎的機制有關(第四章介紹),吃進去的東西不會吐出來。

可惜,python不支援,但是可以通過環視模擬。nice!

'(?=(\w+))\1'

感受一下。

j.        多選結構或: “|”

k.      條件判斷,針對正則表示式的條件判斷:

re.search(r'(?P<tt>\d)asdasd(?(tt)\d|abc)','1asdasd1').groupdict()

{'tt': '1'}

re.search(r'(?P<tt>\d)?asdasd(?(tt)\d|)','rasdasdabc').groupdict()

{'tt': None}

re.search(r'(\d)asdasd(?(1)\d|abc)','1asdasd1').groups()

('1',)

l.        量詞 ? * + {1,5}

m.    ?? *? +? {1,5} 量詞的非貪婪模式。下章介紹。

第三章結束。