1. 正則表達(dá)式基礎(chǔ)1.1. 簡單介紹正則表達(dá)式并不是Python的一部分。正則表達(dá)式是用于處 理字符串的強(qiáng)大工具,擁有自己獨(dú)特的語法以及一個(gè)獨(dú)立的處理引擎,效率上可能不如str自帶的方法,但功能十分強(qiáng)大。得益于這一點(diǎn),在提供了正則表達(dá)式的 語言里,正則表達(dá)式的語法都是一樣的,區(qū)別只在于不同的編程語言實(shí)現(xiàn)支持的語法數(shù)量不同;但不用擔(dān)心,不被支持的語法通常是不常用的部分。如果已經(jīng)在其他 語言里使用過正則表達(dá)式,只需要簡單看一看就可以上手了。 下圖展示了使用正則表達(dá)式進(jìn)行匹配的流程: 正則表達(dá)式的大致匹配過程是:依次拿出表達(dá)式和文本中的字符比較,如果每一個(gè)字符都能匹配,則匹配成功;一旦有匹配不成功的字符則匹配失敗。如果表達(dá)式中有量詞或邊界,這個(gè)過程會稍微有一些不同,但也是很好理解的,看下圖中的示例以及自己多使用幾次就能明白。 下圖列出了Python支持的正則表達(dá)式元字符和語法: 1.2. 數(shù)量詞的貪婪模式與非貪婪模式正 則表達(dá)式通常用于在文本中查找匹配的字符串。Python里數(shù)量詞默認(rèn)是貪婪的(在少數(shù)語言里也可能是默認(rèn)非貪婪),總是嘗試匹配盡可能多的字符;非貪婪 的則相反,總是嘗試匹配盡可能少的字符。例如:正則表達(dá)式"ab*"如果用于查找"abbbc",將找到"abbb"。而如果使用非貪婪的數(shù)量 詞"ab*?",將找到"a"。 1.3. 反斜杠的困擾與大多數(shù)編程語言相同,正則表達(dá)式里使用"\"作為轉(zhuǎn)義 字符,這就可能造成反斜杠困擾。假如你需要匹配文本中的字符"\",那么使用編程語言表示的正則表達(dá)式里將需要4個(gè)反斜杠"\\\\":前兩個(gè)和后兩個(gè)分 別用于在編程語言里轉(zhuǎn)義成反斜杠,轉(zhuǎn)換成兩個(gè)反斜杠后再在正則表達(dá)式里轉(zhuǎn)義成一個(gè)反斜杠。Python里的原生字符串很好地解決了這個(gè)問題,這個(gè)例子中的 正則表達(dá)式可以使用r"\\"表示。同樣,匹配一個(gè)數(shù)字的"\\d"可以寫成r"\d"。有了原生字符串,你再也不用擔(dān)心是不是漏寫了反斜杠,寫出來的表 達(dá)式也更直觀。 1.4. 匹配模式正則表達(dá)式提供了一些可用的匹配模式,比如忽略大小寫、多行匹配等,這部分內(nèi)容將在Pattern類的工廠方法re.compile(pattern[, flags])中一起介紹。 2. re模塊2.1. 開始使用rePython通過re模塊提供對正則表達(dá)式的支持。使用re的一般步驟是先將正則表達(dá)式的字符串形式編譯為Pattern實(shí)例,然后使用Pattern實(shí)例處理文本并獲得匹配結(jié)果(一個(gè)Match實(shí)例),最后使用Match實(shí)例獲得信息,進(jìn)行其他的操作。 re.compile(strPattern[, flag]): 這個(gè)方法是Pattern類的工廠方法,用于將字符串形式的正則表達(dá)式編譯為Pattern對象。
第二個(gè)參數(shù)flag是匹配模式,取值可以使用按位或運(yùn)算符'|'表示同時(shí)生效,比如re.I |
re.M。另外,你也可以在regex字符串中指定模式,比如re.compile('pattern', re.I |
re.M)與re.compile('(?im)pattern')是等價(jià)的。
re提供了眾多模塊方法用于完成正則表達(dá)式的功能。這些方法可以使用Pattern實(shí)例的相應(yīng)方法替代,唯一的好處是少寫一行 re.compile()代碼,但同時(shí)也無法復(fù)用編譯后的Pattern對象。這些方法將在Pattern類的實(shí)例方法部分一起介紹。如上面這個(gè)例子可以 簡寫為: re模塊還提供了一個(gè)方法escape(string),用于將string中的正則表達(dá)式元字符如*/+/?等之前加上轉(zhuǎn)義符再返回,在需要大量匹配元字符時(shí)有那么一點(diǎn)用。 2.2. MatchMatch對象是一次匹配的結(jié)果,包含了很多關(guān)于此次匹配的信息,可以使用Match提供的可讀屬性或方法來獲取這些信息。 屬性:
方法:
2.3. PatternPattern對象是一個(gè)編譯好的正則表達(dá)式,通過Pattern提供的一系列方法可以對文本進(jìn)行匹配查找。 Pattern不能直接實(shí)例化,必須使用re.compile()進(jìn)行構(gòu)造。 Pattern提供了幾個(gè)可讀屬性用于獲取表達(dá)式的相關(guān)信息:
實(shí)例方法[ | re模塊方法]:
以上就是Python對于正則表達(dá)式的支持。熟練掌握正則表達(dá)式是每一個(gè)程序員必須具備的技能,這年頭沒有不與字符串打交道的程序了。筆者也處于初級階段,與君共勉,^_^ 另外,圖中的特殊構(gòu)造部分沒有舉出例子,用到這些的正則表達(dá)式是具有一定難度的。有興趣可以思考一下,如何匹配不是以abc開頭的單詞,^_^ 初學(xué)Python,對Python的文字處理能力有很深的印象,除了str對象自帶的一些方法外,就是正則表達(dá)式這個(gè)強(qiáng)大的模塊了。但是對 于初學(xué)者來說,要用好這個(gè)功能還是有點(diǎn)難度,我花了好長時(shí)間才摸出了點(diǎn)門道。由于我記性不好,很容易就忘事,所以還是寫下來比較好一些,同時(shí)也可以加深印 象,整理思路。 由于我是初學(xué),所以肯定會有些錯(cuò)誤,還望高手不吝賜教,指出我的錯(cuò)誤。 1 Python正則式的基本用法 Python的正則表達(dá)式的模塊是 ‘re’,它的基本語法規(guī)則就是指定一個(gè)字符序列,比如你要在一個(gè)字符串s=’123abc456’ 中查找字符串 ’abc’,只要這樣寫: >>> imp >>> s='123abc456eabc789' >>> re.findall(r’abc’,s) 結(jié)果就是: ['abc', 'abc'] 這里用到的函數(shù) ”findall(rule , target [,flag] )” 是個(gè)比較直觀的函數(shù),就是在目標(biāo)字符串中查找符合規(guī)則的字符串。第一個(gè)參數(shù)是規(guī)則,第二個(gè)參數(shù)是目標(biāo)字符串,后面還可以跟一個(gè)規(guī)則選項(xiàng)(選項(xiàng)功能將在 compile函數(shù)的說明中詳細(xì)說明)。返回結(jié)果結(jié)果是一個(gè)列表,中間存放的是符合規(guī)則的字符串。如果沒有符合規(guī)則的字符串被找到,就返回一個(gè)空列表。 為什么要用r’ ..‘字符串(raw字符串)? 由于正則式的規(guī)則也是由一個(gè)字符串定義的,而在正則式中大量使用轉(zhuǎn)義字符’\’,如果不用raw字符串,則在需要寫一個(gè)’\’的地方,你必須得寫成’ \\’,那么在要從目標(biāo)字符串中匹配一個(gè)’\’的時(shí)候,你就得寫上4個(gè)’\’成為’\\\\’!這當(dāng)然很麻煩,也不直觀,所以一般都使用r’’來定義規(guī)則 字符串。當(dāng)然,某些情況下,可能不用raw字符串比較好。 以上是個(gè)最簡單的例子。當(dāng)然實(shí)際中這么簡單的用法幾乎沒有意義。為了實(shí)現(xiàn)復(fù)雜的規(guī)則查找,re規(guī)定了若干語法規(guī)則。它們分為這么幾類: 功能字符 : ‘.’ ‘*’ ‘+’ ‘|’ ‘?’ ‘^’ ‘$’ ‘\’ 等,它們有特殊的功能含義。特別是’\’字符,它是轉(zhuǎn)義引導(dǎo)符號,跟在它后面的字符一般有特殊的含義。 規(guī)則分界符: ‘[‘ ‘]’ ‘(’ ‘)’ ‘{‘ ‘}’ 等,也就是幾種括號了。 預(yù)定義轉(zhuǎn)義字符集: “\d” “\w” “\s” 等等,它們是以字符’\’開頭,后面接一個(gè)特定字符的形式,用來指示一個(gè)預(yù)定義好的含義。 其它特殊功能字符: ’#’ ‘!’ ‘:’ ‘-‘等,它們只在特定的情況下表示特殊的含義,比如(?# …)就表示一個(gè)注釋,里面的內(nèi)容會被忽略。 下面來一個(gè)一個(gè)的說明這些規(guī)則的含義,不過說明的順序并不是按照上面的順序來的,而是我認(rèn)為由淺入深,由基本到復(fù)雜的順序來編排的。同時(shí)為了直觀, 在說明的過程中盡量多舉些例子以方便理解。 1.1 基本規(guī)則 ‘[‘ ‘]’ 字符集合設(shè)定符 首先說明一下字符集合設(shè)定的方法。由一對方括號括起來的字符,表明一個(gè)字符集合,能夠匹配包含在其中的任意一個(gè)字符。比如 [abc123],表明字符’a’ ‘b’ ‘c’ ‘1’ ‘2’ ‘3’都符合它的要求。可以被匹配。 在’[‘ ‘]’中還可以通過 ’-‘ 減號來指定一個(gè)字符集合的范圍,比如可以用[a-zA-Z]來指定所以英文字母的大小寫,因?yàn)橛⑽淖帜甘前凑諒男〉酱蟮捻樞騺砼诺摹D悴豢梢园汛笮〉捻樞? 顛倒了,比如寫成[z-a]就不對了。 如果在’[‘ ‘]’里面的開頭寫一個(gè) ‘^’ 號,則表示取非,即在括號里的字符都不匹配。如[^a-zA-Z]表明不匹配所有英文字母。但是如果 ‘^’不在開頭,則它就不再是表示取非,而表示其本身,如[a-z^A-Z]表明匹配所有的英文字母和字符’^’。 ‘|’ 或規(guī)則 將兩個(gè)規(guī)則并列起來,以‘|’連接,表示只要滿足其中之一就可以匹配。比如 [a-zA-Z]|[0-9] 表示滿足數(shù)字或字母就可以匹配,這個(gè)規(guī)則等價(jià)于 [a-zA-Z0-9] 注意:關(guān)于’|’要注意兩點(diǎn): 第一, 它在’[‘ ‘]’之中不再表示或,而表示他本身的字符。如果要在’[‘ ‘]’外面表示一個(gè)’|’字符,必須用反斜杠引導(dǎo),即 ’\|’ ; 第二, 它的有效范圍是它兩邊的整條規(guī)則,比如‘dog|cat’匹配的是‘dog’和’cat’,而不是’g’和’c’。如果想限定它的有效范圍,必需使用一個(gè) 無捕獲組 ‘(?: )’包起來。比如要匹配 ‘I have a dog’或’I have a cat’,需要寫成r’I have a (?:dog|cat)’ ,而不能寫成 r’I have a dog|cat’ 例 >>> s = ‘I have a dog , I have a cat’ >>> re.findall( r’I have a (?:dog|cat)’ , s ) ['I have a dog', 'I have a cat'] #正如我們所要的 下面再看看不用無捕獲組會是什么后果: >>> re.findall( r’I have a dog|cat’ , s ) ['I have a dog', 'cat'] #它將’I have a dog’ 和’cat’當(dāng)成兩個(gè)規(guī)則了 至于無捕獲組的使用,后面將仔細(xì)說明。這里先跳過。 ‘.’ 匹配所有字符 匹配除換行符’\n’外的所有字符。如果使用了’S’選項(xiàng),匹配包括’\n’的所有字符。 例: >>> s=’123 \n456 \n789’ >>> findall(r‘.+’,s) ['123', '456', '789'] >>> re.findall(r‘.+’ , s , re.S) ['123\n456\n789'] ‘^’和’$’ 匹配字符串開頭和結(jié)尾 注意’^’不能在‘[ ]’中,否則含意就發(fā)生變化,具體請看上面的’[‘ ‘]’說明。 在多行模式下,它們可以匹配每一行的行首和行尾。具體請看后面compile函數(shù)說明的’M’選項(xiàng)部分 ‘\d’ 匹配數(shù)字 這是一個(gè)以’\’開頭的轉(zhuǎn)義字符,’\d’表示匹配一個(gè)數(shù)字,即等價(jià)于[0-9] ‘\D’ 匹配非數(shù)字 這個(gè)是上面的反集,即匹配一個(gè)非數(shù)字的字符,等價(jià)于[^0-9]。注意它們的大小寫。下面我們還將看到Python的正則規(guī)則中很多轉(zhuǎn)義字符的大小 寫形式,代表互補(bǔ)的關(guān)系。這樣很好記。 ‘\w’ 匹配字母和數(shù)字 匹配所有的英文字母和數(shù)字,即等價(jià)于[a-zA-Z0-9]。 ‘\W’ 匹配非英文字母和數(shù)字 即’\w’的補(bǔ)集,等價(jià)于[^a-zA-Z0-9]。 ‘\s’ 匹配間隔符 即匹配空格符、制表符、回車符等表示分隔意義的字符,它等價(jià)于[ \t\r\n\f\v]。(注意最前面有個(gè)空格) ‘\S’ 匹配非間隔符 即間隔符的補(bǔ)集,等價(jià)于[^ \t\r\n\f\v] ‘\A’ 匹配字符串開頭 匹配字符串的開頭。它和’^’的區(qū)別是,’\A’只匹配整個(gè)字符串的開頭,即使在’M’模式下,它也不會匹配其它行的很首。 ‘\Z’ 匹配字符串結(jié)尾 匹配字符串的結(jié)尾。它和’$’的區(qū)別是,’\Z’只匹配整個(gè)字符串的結(jié)尾,即使在’M’模式下,它也不會匹配其它各行的行尾。 例: >>> s= '12 34\n56 78\n90' >>> re.findall( r'^\d+' , s , re.M ) #匹配位于行首的數(shù)字 ['12', '56', '90'] >>> re.findall( r’\A\d+’, s , re.M ) #匹配位于字符串開頭的數(shù)字 ['12'] >>> re.findall( r'\d+$' , s , re.M ) #匹配位于行尾的數(shù)字 ['34', '78', '90'] >>> re.findall( r’\d+\Z’ , s , re.M ) #匹配位于字符串尾的數(shù)字 ['90'] ‘\b’ 匹配單詞邊界 它匹配一個(gè)單詞的邊界,比如空格等,不過它是一個(gè)‘0’長度字符,它匹配完的字符串不會包括那個(gè)分界的字符。而如果用’\s’來匹配的話,則匹配出 的字符串中會包含那個(gè)分界符。 例: >>> s = 'abc abcde bc bcd' >>> re.findall( r’\bbc\b’ , s ) #匹配一個(gè)單獨(dú)的單詞 ‘bc’ ,而當(dāng)它是其它單詞的一部分的時(shí)候不匹配 ['bc'] #只找到了那個(gè)單獨(dú)的’bc’ >>> re.findall( r’\sbc\s’ , s ) #匹配一個(gè)單獨(dú)的單詞 ‘bc’ [' bc '] #只找到那個(gè)單獨(dú)的’bc’,不過注意前后有兩個(gè)空格,可能有點(diǎn)看不清楚 ‘\B’ 匹配非邊界 和’\b’相反,它只匹配非邊界的字符。它同樣是個(gè)0長度字符。 接上例: >>> re.findall( r’\Bbc\w+’ , s ) #匹配包含’bc’但不以’bc’為開頭的單詞 ['bcde'] #成功匹配了’abcde’中的’bcde’,而沒有匹配’bcd’ ‘(?:)’ 無捕獲組 當(dāng)你要將一部分規(guī)則作為一個(gè)整體對它進(jìn)行某些操作,比如指定其重復(fù)次數(shù)時(shí),你需要將這部分規(guī)則用’(?:’ ‘)’把它包圍起來,而不能僅僅只用一對括號,那樣將得到絕對出人意料的結(jié)果。 例:匹配字符串中重復(fù)的’ab’ >>> s=’ababab abbabb aabaab’ >>> re.findall( r’\b(?:ab)+\b’ , s ) ['ababab'] 如果僅使用一對括號,看看會是什么結(jié)果: >>> re.findall( r’\b(ab)+\b’ , s ) ['ab'] 這是因?yàn)槿绻皇褂靡粚ㄌ?,那么這就成為了一個(gè)組(group)。組的使用比較復(fù)雜,將在后面詳細(xì)講解。 ‘(?# )’ 注釋 Python允許你在正則表達(dá)式中寫入注釋,在’(?#’ ‘)’之間的內(nèi)容將被忽略。 (?iLmsux) 編譯選項(xiàng)指定 Python的正則式可以指定一些選項(xiàng),這個(gè)選項(xiàng)可以寫在findall或compile的參數(shù)中,也可以寫在正則式里,成為正則式的一部分。這在 某些情況下會便利一些。具體的選項(xiàng)含義請看后面的compile函數(shù)的說明。 此處編譯選項(xiàng)’i’ 等價(jià)于IGNORECASE ,L 等價(jià)于 LOCAL ,m 等價(jià)于 MULTILINE ,s 等價(jià)于 DOTALL ,u 等價(jià)于 UNICODE , x 等價(jià)于 VERBOSE 。 請注意它們的大小寫。在使用時(shí)可以只指定一部分,比如只指定忽略大小寫,可寫為 ‘(?i)’,要同時(shí)忽略大小寫并使用多行模式,可以寫為 ‘(?im)’。 另外要注意選項(xiàng)的有效范圍是整條規(guī)則,即寫在規(guī)則的任何地方,選項(xiàng)都會對全部整條正則式有效。 1.2 重復(fù) 正則式需要匹配不定長的字符串,那就一定需要表示重復(fù)的指示符。Python的正則式表示重復(fù)的功能很豐富靈活。重復(fù)規(guī)則的一般的形式是在一條字符 規(guī)則后面緊跟一個(gè)表示重復(fù)次數(shù)的規(guī)則,已表明需要重復(fù)前面的規(guī)則一定的次數(shù)。重復(fù)規(guī)則有: ‘*’ 0或多次匹配 表示匹配前面的規(guī)則0次或多次。 ‘+’ 1次或多次匹配 表示匹配前面的規(guī)則至少1次,可以多次匹配 例:匹配以下字符串中的前一部分是字母,后一部分是數(shù)字或沒有的變量名字 >>> s = ‘ aaa bbb111 cc22cc 33dd ‘ >>> re.findall( r’\b[a-z]+\d*\b’ , s ) #必須至少1個(gè)字母開頭,以連續(xù)數(shù)字結(jié)尾或沒有數(shù)字 ['aaa', 'bbb111'] 注意上例中規(guī)則前后加了表示單詞邊界的’\b’指示符,如果不加的話結(jié)果就會變成: >>> re.findall( r’[a-z]+\d*’ , s ) ['aaa', 'bbb111', 'cc22', 'cc', 'dd'] #把單詞給拆開了 大多數(shù)情況下這不是我們期望的結(jié)果。 ‘?’ 0或1次匹配 只匹配前面的規(guī)則0次或1次。 例,匹配一個(gè)數(shù)字,這個(gè)數(shù)字可以是一個(gè)整數(shù),也可以是一個(gè)科學(xué)計(jì)數(shù)法記錄的數(shù)字,比如123和10e3都是正確的數(shù)字。 >>> s = ‘ 123 10e3 20e4e4 30ee5 ‘ >>> re.findall( r’ \b\d+[eE]?\d*\b’ , s ) ['123', '10e3'] 它正確匹配了123和10e3,正是我們期望的。注意前后的’\b’的使用,否則將得到不期望的結(jié)果。 |
|