《精通正則表示式》學習筆記
一個重要且常見的問題:
寫正則表示式時,我們需要在對欲檢索的文字的瞭解程度和檢索精準度之間求得平衡。: 越精準,容錯性越差;但是容錯性越好,越容易出現異常。所以,我們需要匹配所有需要匹配的,同時,忽略掉所有不需要匹配的。好像是句廢話,實際是精髓。
一,正則表示式入門:
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} 量詞的非貪婪模式。下章介紹。
第三章結束。