1. 正則表達式基礎 1.1. 簡單介紹 正則表達式並不是Python的一部分。正則表達式是用於處理字元串的強大工具,擁有自己獨特的語法以及一個獨立的處理引擎,效率上可能不如str自帶的方法,但功能十分強大。得益於這一點,在提供了正則表達式的語言里,正則表達式的語法都是一樣的,區別隻在於不同的編程語 ...
1. 正則表達式基礎
1.1. 簡單介紹
正則表達式並不是Python的一部分。正則表達式是用於處理字元串的強大工具,擁有自己獨特的語法以及一個獨立的處理引擎,效率上可能不如str自帶的方法,但功能十分強大。得益於這一點,在提供了正則表達式的語言里,正則表達式的語法都是一樣的,區別隻在於不同的編程語言實現支持的語法數量不同;但不用擔心,不被支持的語法通常是不常用的部分。如果已經在其他語言里使用過正則表達式,只需要簡單看一看就可以上手了。
下圖展示了使用正則表達式進行匹配的流程: http://www.cnblogs.com/huxi/archive/2010/07/04/1771073.html
正則表達式的大致匹配過程是:依次拿出表達式和文本中的字元比較,如果每一個字元都能匹配,則匹配成功;一旦有匹配不成功的字元則匹配失敗。如果表達式中有量詞或邊界,這個過程會稍微有一些不同,但也是很好理解的,看下圖中的示例以及自己多使用幾次就能明白。
下圖列出了Python支持的正則表達式元字元和語法:
1.”.“匹配任意除換行符以外的字元
>>> string = "abafsdafd\nafdasfd" #包含換行符
>>> string1 = "adfasdfadfasdfwer12345656耿" #不包含換行符
>>> import re
>>> m = re.search(".+",string) #驗證是否可以匹配換行符
>>> m.group()
'abafsdafd'
>>> n = re.search(".+",string1)
>>> n.group()
'adfasdfadfasdfwer12345656耿'
從上面輸出結果可以看出,”.“是匹配任意除了換行符的字元。遇到"\n"換行符即終止匹配。
2.”\"轉義字元
轉義字元,使後一個字元改變原來的意思。如果字元串中有*號需要匹配,可以使用\*或者字元集[*],"a\.c"代表匹配a.c "a\\c"代表匹配a\c
>>> str_num = "22.567979mafdasdf"
>>> m = re.search("\d+\.\d+",str_num)
>>> m.group()
'22.567979'
我們知道,"."在python中代表的含義是除了"\n"之外的所有字元,如果這裡不進行轉義的話,匹配出來的就是任意非"\n"字元,因此要使用"\"進行轉義。
>>> string = "dfafdasfd\fafdasfda"
>>> string
'dfafdasfd\x0cafdasfda'
在python中,如果字元串中包含"\",有時候會顯示不出來,或者修改後面的內容,把別人改變了,這個不知道在Linux平臺上是怎麼回事。
3.[...]字元集(字元類)
[...]:字元集(字元類)對應的位置可以是字元集中任意字元。字元集中的字元可以逐個列出[0,1,2,3,4,5,6,7,8,9],也可以給出範圍[0-9]。第一個字元集如果是^表示取反,如[^abc]表示不是abc的其他字元。
所有的特殊字元在字元集中都失去其原有的特殊意義。在字元集中如果要使用]、或^,可以在前面加上"\"反斜杠,或把]\、-放在第一個字元,把^放在非第一個字元。
>>> string = "dafdafdasf[adfas^fad"
>>> m = re.search("[a-z]+\[",string)
>>> m.group()
'dafdafdasf['
從上面腳本可以看出,如果要匹配[要在前面加上"\"轉義字元。
>>> m = re.search("\w+[[]",string) (1)在字元集中匹配"["
>>> m.group()
'dafdafdasf['
>>> n = re.search("w+[\[]",string) (2)轉義匹配,驗證在字元集中匹配[是否需要加[\]
>>> n.group()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
在字元集中如果要使用]、或^,可以在前面加上"\"反斜杠,或把]\、-放在第一個字元,把^放在非第一個字元。
預定義字元集
1.\d數字
>>> m = re.search("\d",string)
>>> m.group()
'1'
\d是匹配數字,等價於[0-9]
2.\D非數字 等價於[^\d]
>>> string = "dfaMNA12581耿fa"
>>> m = re.search("\D",string)
>>> m.group()
'd'
>>> n = re.search("[^\d]",string)
>>> n.group()
'd'
>>> l = re.search("[^0-9]",string)
>>> l.group()
'd'
從上面可以看出,“\D”是用來匹配非數字的,匹配除了數字0-9意外的任意字元,等價於[^0-9]或[^\d]
3.\s空白字元 [<空格>\r\t\n\f\v]
“\s”是匹配任意非空字元,如[<空格>\r\t\n\f\v]等,實例如下:
>>> m = re.search("\s+",string) #\s進行匹配空白字元
>>> m.group()
' \t \n \x0c \x0b \r'
從上面可以看出,\s是匹配任意空白字元,如空格、\r、\t、\n、\f、\v等
4.\S非空白字元 \S與\s正好相反,是匹配任意非空字元。等價於[^\s]匹配任意非空字元
>>> string = "faMM耿 \t \n \f \v \rDASDF"
>>> n = re.search("\S+",string)
>>> n.group()
'faMM耿'
從上面可以看出"\S"是匹配任意非空字元,遇到空的字元即停止了,匹配任意非空字元,"."匹配任意字元,除了換行符之外。
>>> m = re.search(".+",string)
>>> m.group()
'faMM耿 \t '
從上面看出,“\S”和“.”還是有差別的,一個是任意非空字元,一個是匹配任意除了"\n"意外任意非空字元。
5.\w 單詞字元[A-Z0-9a-z_]
\w是匹配單詞字元,下麵來看下能不能匹配漢字或其他:
>>> string = "faMM耿 \t \n \f \v \rDASDF"
>>> m = re.search("\w+",string)
>>> m.group()
'faMM耿' (1)腳本
>>> format_string = "fdMM更KKMM長 /大MM \n \tMMKDSI"
>>> m = re.search("\w+",format_string)
>>> m.group()
'fdMM更KKMM長' (2)腳本
可以看出,"\w"是可以匹配漢字的,不能匹配空格,換行符這些,但是能夠匹配漢字。
6.\W 等價於非單詞字元 [^\w]
>>> import re
>>> string = "naefda曾 LmKDS 1316547\n\t\r@@3$&^$"
>>> m = re.search("\W+",string)
>>> m.group()
' '
從上面可以看出"\W "匹配出來了空,說明" "不是單詞字元,"\W"是匹配非單詞字元。
數量詞(用在字元或(...)之後)
1."*" 匹配前一個字元0或無限次 前一個字元
"*"是匹配前一個字元0或無限次
>>> import re
>>> string = "naefda曾 LmKDS 1316547\n\t\r@@3$&^$"
>>> m = re.search("\w*",string)
>>> m.group()
'naefda曾'
>>> n = re.search("\d*",string)
>>> n.group()
''
>>> n = re.search("耿",string)
>>> n
>>> n.group()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
從上面腳本代碼可以看出,當使用*要查找的字元在開頭的時候就會匹配到,在中間就匹配不到。這種懶惰的匹配方式,如果其他情況下匹配不到,則會返回錯誤。
2.“+” 匹配前一個字元一次或無限次 前一個字元(牢記,只是匹配前面一個)
>>> import re
>>> string = "naefda曾 LmKDS 1316547\n\t\r@@3$&^$"
>>> m = re.search("\w+",string)
>>> m.group()
'naefda曾'
>>> n = re.search("\s+",string)
>>> n.group()
' '
>>> d = re.search("更",string)
>>> d.group()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
從上面可以看出,"+"是匹配前一個字元一次或無限次,如果匹配不到,則返回None
3."?" 匹配前一個字元0次或一次 前一個字元(牢記,只是匹配前面一個)
>>> import re
>>> string = "naefda曾 LmKDS 1316547\n\t\r@@3$&^$"
>>> m = re.search("\w?",string)
>>> m.group()
'n'
>>> n = re.search("f?",string)
>>> n.group()
''
從上面可以看出,?是以貪婪的方式進行匹配。?是匹配前一個字元0次或1次。從上面的例子中發現一個問題,即"?"和"*"都是從頭開始進行匹配,如果開頭匹配不到,就返回"",等價於如果使用search()出現”?"和“*”等價於使用match()從頭開始匹配,找不到則不找了,不一樣的是match()返回的是None,而seach()返回的是""。
4.{m} 代表匹配前一個字元m次 前一個字元(牢記,只是匹配前面一個)
>>> import re
>>> string = "dafMM\n更1134657Qqcd m,l#!"
>>> m = re.search("\d{4}",string) #代表匹配數字四次
>>> m.group()
'1134'
>>> n = re.search("\d{10}",string)
>>> n.group()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'group'
從上面可以看出,如果匹配不到,則返回None;{m}是代表匹配前一個字元m次,要註意在匹配的時候,我們可以像下麵一樣設定一個匹配區間,這樣就不會出現匹配超標的情況。
5.{m,n} 匹配一個字元m次至n次 前一個字元(牢記,只是匹配前面一個)
m和n可以省略,如省略m({,n})則匹配0到n次;若省略n({m,}),則匹配m至無限次。
>>> import re
>>> string = "dafMM\n更1134657Qqcd m,l#!"
>>> m = re.search("\d{1,10}",string)
>>> m.group()
'1134657'
>>> n = re.search("\d{,5}",string)
>>> n.group()
''
>>> d = re.search("\d{4,}",string)
>>> d.group()
'1134657'
從上面可以看出,{m,n}是匹配在一個範圍內的個數,但是我們也發現了一個問題,千萬不要讓正則表達式匹配包含0次的情況,一旦匹配0次,那麼就會出現如果開頭匹配不到之後,就不匹配的情況,直接返回""。
*? +? ?? {m,n}?使*,+,?,{m,n}變成非貪婪模式。
邊界匹配
1.^ 匹配字元串開頭,在多行模式中,匹配第一行的開頭
>>> import re
>>> string = "dafMM\n更1134657Qqcd m,l#!"
>>> m = re.search("^da",string)
>>> m.group()
'da'
開頭匹配,相當於使用match()進行匹配了。
2.$ 匹配字元串末尾,在多行模式中匹配每一行的末尾
>>> import re
>>> string = "dafMM\n更1134657Qqcd m,l#!"
>>> m = re.search("#!",string)
>>> m.group()
'#!'
"$"是匹配字元串的末尾,不管前面,值匹配末尾是否是要匹配的內容。
3.\A 僅匹配字元串開頭
>>> import re
>>> string = "dafMM\n更1134657Qqcd m,l#!"
>>> m = re.search("\Adaf",string)
>>> m.group()
'daf'
“\A”是僅匹配字元串開頭,即僅僅從字元串的開頭進行匹配。
4.\Z 僅匹配字元串末尾
>>> import re
>>> string = "dafMM\n更1134657Qqcd m,l#!"
>>> n = re.search("l#!",string)
>>> n.group()
'l#!'
"\Z"是僅匹配字元串末尾,僅從末尾進行匹配,可能\Z和$的區別就是,$是匹配每行的末尾,而\Z是僅匹配字元串的末尾。
5.\b 匹配\w和\W之間的字元
6.\B 匹配非\w和\W之間的字元 即[^\b]
邏輯分組
1.“|” 代表左右表達式任意匹配一個
它總是先嘗試匹配左邊的表達式,一旦成功則跳過右邊的表達式。如果|沒有被包含在()中,則它的範圍是整個正則表達式
>>> import re
>>> m = re.search("(?P<province>[0-9]{4})(?P<city>[0-9]{2})(?P<birthday>[0-9]{4})","371481199306143242").groupdict()
>>> m
{'city': '81', 'birthday': '1993', 'province': '3714'}
可以依次分組匹配,生成一個字典,groupdict(),分組匹配,給匹配到的字元串起名字。
1.2. 數量詞的貪婪模式與非貪婪模式
正則表達式通常用於在文本中查找匹配的字元串。Python里數量詞預設是貪婪的(在少數語言里也可能是預設非貪婪),總是嘗試匹配儘可能多的字元;非貪婪的則相反,總是嘗試匹配儘可能少的字元。例如:正則表達式"ab*"如果用於查找"abbbc",將找到"abbb"。而如果使用非貪婪的數量詞"ab*?",將找到"a"。
1.3. 反斜杠的困擾
與大多數編程語言相同,正則表達式里使用"\"作為轉義字元,這就可能造成反斜杠困擾。假如你需要匹配文本中的字元"\",那麼使用編程語言表示的正則表達式里將需要4個反斜杠"\\\\":前兩個和後兩個分別用於在編程語言里轉義成反斜杠,轉換成兩個反斜杠後再在正則表達式里轉義成一個反斜杠。Python里的原生字元串很好地解決了這個問題,這個例子中的正則表達式可以使用r"\\"表示。同樣,匹配一個數字的"\\d"可以寫成r"\d"。有了原生字元串,你再也不用擔心是不是漏寫了反斜杠,寫出來的表達式也更直觀。
1.4. 匹配模式
正則表達式提供了一些可用的匹配模式,比如忽略大小寫、多行匹配等,這部分內容將在Pattern類的工廠方法re.compile(pattern[, flags])中一起介紹。
2. re模塊
2.1. 開始使用re
Python通過re模塊提供對正則表達式的支持。使用re的一般步驟是先將正則表達式的字元串形式編譯為Pattern實例,然後使用Pattern實例處理文本並獲得匹配結果(一個Match實例),最後使用Match實例獲得信息,進行其他的操作。
import re #將正則表達式編譯成Pattern對象 pattern = re.compile(r'hello') #使用pattern匹配文本,獲得匹配結果,無法匹配時將返回None match = pattern.match("hello world!") if match: #使用match獲得分組信息 print(match.group())
上面正則匹配中,首先進行了編譯,編譯成正則格式,然後進行匹配。
re.compile(strPattern[, flag]):
這個方法是Pattern類的工廠方法,用於將字元串形式的正則表達式編譯為Pattern對象。 第二個參數flag是匹配模式,取值可以使用按位或運算符'|'表示同時生效,比如re.I | re.M。另外,你也可以在regex字元串中指定模式,比如re.compile('pattern', re.I | re.M)與re.compile('(?im)pattern')是等價的。
可選值有:
re模塊還提供了一個方法escape(string),用於將string中的正則表達式元字元如*/+/?等之前加上轉義符再返回,在需要大量匹配元字元時有那麼一點用。
2.2. Match
>>> string = "aafaaMMaaaa"
>>> m = re.search("aa?",string)
>>> m.group()
'aa'
>>> n = re.search("aaa?",string)
>>> n.group()
'aa'
>>> d = re.search("aaaa?",string)
>>> d.group()
'aaaa'
上面代碼中,"?"的作用是匹配前一個字元,就是這個正則符號前面的那個字元0次或一次。