你的第一個正則表達式
正則表達式為高級的文本模式匹配、抽取、與/或文本形式的搜索和替換功能提供了基礎。簡單的說,正則表達式(簡稱為regex)是一些由字符和特殊符號組成的字符串,它們描述了模式的重復或者表述多個字符,於是正則表達式能按照某種模式匹配一系列有相似特征的字符串。——正則表達式能夠匹配多個字符串,否則他就沒什么意義了!
術語:“匹配”(matching):
“模式匹配”(pattern-matching),在Python術語中,主要有兩種方法完成模式匹配:
1、“搜索”(searcing),即在字符串任意部分中搜索匹配的模式;
2、而“匹配”(matching)是指判斷一個字符串能否從起始處全部或者部分地匹配某個模式。
常見的特殊符號和字符:
1、符號
re1|re2 匹配正則表達式re1 或者 re2
. 匹配任何字符(除了\n之外)
^ 匹配字符串其實部分
$ 匹配字符串終止部分
* 匹配0次或者多次前面出現的正則表達式
+ 匹配次或者多次前面出現的正則表達式
? 匹配0次或者1次前面出現的正則表達式
{N} 匹配N次前面出現的正則表達式
{M,N} 匹配M~N次前面出現的正則表達式
[...] 匹配來自字符集的任意單一字符
[..x-y..] 匹配x~y范圍中的任意單一字符
[^...] 不匹配此字符集中出現的任何一個字符,包括某一范圍的字符(如果再次字符集中出現)
(*|+|?|{})? 用於匹配上面頻繁出現/重復出現符號的非貪婪版本(*、+、?、{})
(...) 匹配封閉的正則表達式,然后另存為子組
2、特殊字符
\d 匹配任何十進制數字,與[0-9]一致(\D與\d相反,表示不匹配任何非數值型的數字)
\w 匹配任何字母數字字符,與[A-Za-z0-9]相同 (與 \W 相反)
\s 匹配任何空格字符,與[\n\t\r\v\f]相同(\S與之相反)
\b 匹配任何單詞邊界(\B與之相反)
\N 匹配一保存的子組N
\c 逐字匹配任何特殊字符c(即,僅按照字面意義匹配,不匹配特殊含義)
\A(\Z) 匹配字符串的起始(結束)
3、擴展表示法
...
正則表達式和Python語言
re模塊在python 1.5版本中被引入,用於替換regex模塊和regsub模塊,在python 2.5版本中被移除
re模塊支持更強大而且更通用的Perl風格的正則表達式,該模塊允許多個線程共享一個已編譯的正則表達式對象,也支持命名子組
匹配對象以及group()和groups()方法
當處理正則表達式時,除了正則表達式對象之外,還有另一個對象類型:匹配對象
這些是成功調用 match() 或者 search()返回的對象。匹配對象有兩個主要的方法:group()和groups()
使用match()方法匹配字符串
匹配成功——返回一個匹配對象
匹配失敗——返回None
如果使用group()方法則能夠顯示那個成功的匹配。
In [2]: import re #導入re模塊 In [3]: m = re.match('foo','foo') #使用match()方法匹配字符串 In [4]: if m is not None: #判斷是否匹配成功 ...: m.group() ...: In [5]: m Out[5]: <_sre.SRE_Match object; span=(0, 3), match='foo'> #確認返回的匹配對象 In [6]: m.group() #使用group()方法輸出匹配成功的值 Out[6]: 'foo'
在實際操作中,最好不要省去if語句塊,這樣可以避免AttributeError異常(None是返回的錯誤值,該值並沒有group()屬性[方法])
In [8]: m = re.match('foo','food on the table') #可以匹配成功 In [9]: m.group() Out[9]: 'foo'
匹配是從頭開始,以上實例可以匹配成功
In [10]: m = re.match('foo','My food on the table') In [11]: m.group() --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-11-c6ebaf1e4dd8> in <module>() ----> 1 m.group() AttributeError: 'NoneType' object has no attribute 'group'
這里有2點需要注意:1、如果無法從頭開始匹配,那么就會匹配失敗; 2、如果匹配失敗,直接使用group()方法就會報錯,前面已經提到
In [16]: m = re.search('foo','My food on the table') In [17]: m.group() Out[17]: 'foo'
這里就可以使用search()函數來代替match()函數,但是要注意,只是搜索成功,並不表示匹配成功
匹配多個字符串
In [19]: bt = 'bat|bet|bit' In [20]: m = re.match(bt,'bat') #'bat'是一個匹配 In [21]: if m is not None: m.group() In [22]: m.group() Out[22]: 'bat'
In [23]: m = re.match(bt,'blt') In [24]: if m is not None : m.group() In [25]: m.group() #匹配'blt'失敗 --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) <ipython-input-25-c6ebaf1e4dd8> in <module>() ----> 1 m.group() AttributeError: 'NoneType' object has no attribute 'group'
匹配任何單個字符
>>> anyend = '.end' >>> m = re.match(anyend,'bend') #點號 . 匹配的'b'字符,這里要注意 >>> if m is not None: m.group() ... 'bend'
>>> patt314 = '3.14' >>> pi_patt = '3\.14' >>> m = re.match(pi_patt,'3.14') #這里是精確匹配 >>> if m is not None: m.group() ... '3.14' >>> m = re.match(patt314,'3014') #點號匹配的'0' >>> if m is not None: m.group() ... '3014' >>> m = re.match(patt314,'3.14') #點號匹配的'.' >>> if m is not None: m.group() ... '3.14'
2018年3月19日
正則表達式匹配的子組
>>> m = re.match('(\w\w\w)-(\d\d\d)','abc-123') #如果不用括號將\w括起來,那么就不存在子組1,同理\d >>> if m is not None : m.group() #完整匹配 ... 'abc-123' >>> m.group(1) #子組1 'abc' >>> m.group(2) #子組2 '123' >>> m.groups() #全部子組 ('abc', '123')
group()與groups()的意義要理解清楚,下面是一個子組的特殊例子
>>> m = re.match('(a(b))','ab') >>> if m is not None : m.group() ... 'ab' >>> m.group(1) 'ab' >>> m.group(2) 'b' >>> m.groups() ('ab', 'b')
通常情況下,在正則表達式中使用原始字符串是個好主意
>>> m = re.search('^The','The end') >>> if m is not None:m.group() ... 'The' >>> m = re.search('^THe','end. The') >>> >>> if m is not None:m.group() ... >>> m = re.search(r'\bthe','bite the dog') >>> if m is not None : m.group() ... 'the' >>> m = re.search(r'\bthe','bitethe dog') >>> if m is not None: m.group() ... >>> m = re.search(r'\Bthe','bitethe dog') >>> if m is not None:m.group() ... 'the' >>>
其他4個re模塊函數和正則表達式對象方法:findall()、sub()、subn()和split()
findall()查詢字符串某個正則表達式模式全部的非重復出現的情況。
與match()和search()的不同之處在於,findall()總是返回一個列表,
>>> re.findall('car','car') ['car'] >>> re.findall('car','scary') ['car'] >>> re.findall('car','carry the barcardi to the car') #這里匹配了‘car’出現了3次,3次都被記錄在了列表中 ['car', 'car', 'car'] >>>
一個特殊一點的例子:
>>> re.findall('@163\.com','this is my 163.com email: warlock921@163.com') ['@163.com'] >>> re.findall('@163\.com','this is my 163.com email: warlock921@163.com,your email is : xxx@163.com') ['@163.com', '@163.com']
findall()總結:對於一個成功的匹配,每個子組匹配是由 findall() 返回的結果列表中的單一元素;對於多個成功的匹配,每個子組匹配是返回一個元組中的單一元素,而且每個元組(每個元組都對應一個成功的匹配)是結果列表中的元素。
使用sub()和subn()搜索與替換
>>> re.sub('X','Mr.Smith','attn:X\n\nDear X, \n') 'attn:Mr.Smith\n\nDear Mr.Smith, \n' >>> re.subn('X','Mr.Smith','attn: X\n\nDear X,\n') ('attn: Mr.Smith\n\nDear Mr.Smith,\n', 2) >>> print(re.sub('X','Mr.Smith','attn:X\n\nDear X, \n')) attn:Mr.Smith Dear Mr.Smith, >>> re.sub('[ae]','X','abcdef') 'XbcdXf' >>> re.subn('[ae]','X','abcdef') ('XbcdXf', 2)
sub()和subn()兩者幾乎一樣,都是將某字符串中所有匹配正則表達式的部分進行某種形式的替換。用來替換的部分通常是一個字符串,但它也可能是一個函數,該函數返回一個用來替換的字符串。
subn()和sub()一樣,但subn()還返回一個表示替換的總數,替換后的字符串和表示替換總數的數字一起作為擁有兩個元的元組返回。
在限定模式上使用split()分隔字符串
一個簡單的示例:
re.split(':','str1:str2:str3') ['str1', 'str2', 'str3']
一個復雜的示例:
>>> DATA=( ... 'Mountain View, CA 94040', ... 'Sunnyvale, CA', ... 'Los Altos, 94023', ... 'Cupertino, 95014', ... 'Palo Alto CA', ... ) >>> for datum in DATA: ... print(re.split(', |(?= (?:\d{5}|[A-Z]{2})) ',datum)) ... ['Mountain View', 'CA', '94040'] ['Sunnyvale', 'CA'] ['Los Altos', '94023'] ['Cupertino', '95014'] ['Palo Alto', 'CA'] >>>
