CrazyWing:Python自動化運維開發實戰 十6、Python正則表達式

導語

wing忠告:搞運維的童鞋若是沒有接觸過shell裏面的正則表達式,建議先跳過本章python

正則表達式是一個特殊的字符序列,用來檢查一個字符串是否與某種模式匹配。
Python提供的是 Perl 風格的正則表達式模式。
re 模塊使 Python擁有所有的正則表達式功能。
compile 函數根據一個模式字符串和可選的標誌參數生成一個正則表達式對象。該對象擁有一系列方法用於正則表達式匹配和替換。
re 模塊也提供與這些方法功能徹底一致的函數,這些函數使用一個模式字符串作爲它們的第一個參數。linux

正則表達式的使用有兩種方式:

1.使用正則以前先編譯正則
2.使用正則以前不編譯正則

1.使用正則以前先編譯正則

正則表達式被編譯成 'RegexObject' 實例,能夠爲不一樣的操做提供方法,如模式匹配搜索或
字符串替換。git

>>> import re
>>> p=re.compile('n+')              #先編譯正則表達式,生成一個表達式對象
>>> p.match('nnnning').group()  #再使用表達式去進行匹配
'nnnn'

group()返回被 RE 匹配的字符串    //'nnnn'
start()返回匹配開始的位置            //0
end()返回匹配結束位置的下一個位置   //4
span()返回一個元組包含匹配 (開始,結束) 的位置 //(0,4) 不包含下邊界

2.使用正則以前不編譯正則

利用match和search幫咱們編譯正則表達式

re.match():
re.match 嘗試從字符串的起始位置匹配一個模式,若是不是在起始位置匹配成功,match()
就返回none。shell

語法:ruby

re.match(pattern, string, flags=0)運維

參數說明:ide

| 參數 | 描述 |
| pattern | 匹配的正則表達式 |
| string | 要匹配的字符串 |
| flags | 標誌位,用於控制正則表達式的匹配方式,如:是否區分大小寫,多行匹配等等 |

匹配成功re.match方法返回一個匹配的對象,不然返回None。
使用group(num) 或 groups() 匹配對象函數來獲取匹配表達式。                  

| 匹配對象方法 | 描述 |
| group(num=0) | 匹配的整個表達式的字符串,group() 能夠一次輸入多個組號,在這種狀況下它將返回一個包含那些組所對應值的元組。 |
| groups() | 返回一個包含全部小組字符串的元組,從 1 到 所含的小組號。 |

例 1:
#!/usr/bin/python3
import re
print(re.match('www', 'www.fklinux.com').span()) # 在起始位置匹配
print(re.match('com', 'www.fklinux.com'))函數

輸出結果:
(0, 3)
None

例 2:
#!/usr/bin/python
import re
line = "Cats are smarter than dogs"
matchObj = re.match( r'(.) are (.?) .*', line, re.M|re.I)
#上面的re.M|re.I是正則表達式修飾符 - 可選標誌,後面立刻會講到ui

if matchObj:
   print "matchObj.group() : ", matchObj.group()
   print "matchObj.group(1) : ", matchObj.group(1)
   print "matchObj.group(2) : ", matchObj.group(2)
else:
   print "No match!!"

執行結果:
matchObj.group() :  Cats are smarter than dogs
matchObj.group(1) :  Cats
matchObj.group(2) :  smarter

re.search():
re.search 掃描整個字符串並返回第一個成功的匹配。

函數語法:

re.search(pattern, string, flags=0)

函數參數說明:

| 參數 | 描述 |
| pattern | 匹配的正則表達式 |
| string | 要匹配的字符串。 |
| flags | 標誌位,用於控制正則表達式的匹配方式,如:是否區分大小寫,多行匹配等等。 |

匹配成功re.search方法返回一個匹配的對象,不然返回None。
使用group(num) 或  groups() 匹配對象函數來獲取匹配表達式。  

| 匹配對象方法 | 描述 |
| group(num=0) | 匹配的整個表達式的字符串,group() 能夠一次輸入多個組號,在這種狀況下它將返回一個包含那些組所對應值的元組。 |
| groups() | 返回一個包含全部小組字符串的元組 |

例 1:

print(re.search('www', 'www.fklinux.com'))
<_sre.SRE_Match object at 0x7fbbe5816c60> #python2只顯示內存地址
<_sre.SRE_Match object; span=(0, 3), match='www'> #python3的顯示結果

print(re.search('com', 'www.fklinux.com'))
<_sre.SRE_Match object at 0x7fbbe5816c60> #python2只顯示內存地址
<_sre.SRE_Match object; span=(12, 15), match='com'> #python3的顯示結果
print(re.search('www', 'www.fklinux.com').span()) #加上span()方法處理一下
(0, 3)
print(re.search('com', 'www.fklinux.com').span())
(12, 15)

例 2:
#!/usr/bin/python
import re
line = "Cats are smarter than dogs";
searchObj = re.search( r'(.) are (.?) .*', line, re.M|re.I) #每一個小括號一個分組

if searchObj:
   print "searchObj.group() : ", searchObj.group()
   print "searchObj.group(1) : ", searchObj.group(1)
   print "searchObj.group(2) : ", searchObj.group(2)
else:
   print "Nothing found!!"

執行結果:
searchObj.group() :  Cats are smarter than dogs   #
searchObj.group(1) :  Cats
searchObj.group(2) :  smarter

例3:

print line
wig wing winng winnng winnnng
re.search('(wig)( +)(wing)',line).groups() #全部匹配的組的內容
('wig', ' ', 'wing')
re.search('(wig)( +)(wing)',line).group() #全部匹配的組的內容
'wig wing'
re.search('(wig)( +)(wing)',line).group(0) #全部匹配的組的內容
'wig wing'
re.search('(wig)( +)(wing)',line).group(1) #第一組匹配的內容
'wig'
re.search('(wig)( +)(wing)',line).group(2) #第二組匹配的內容
' '
re.search('(wig)( +)(wing)',line).group(3) #第三組匹配的內容
'wing'

re.match與re.search的區別
re.match

只匹配字符串的開始,若是字符串開始不符合正則表達式,則匹配失敗,函數返回None。
re.search
匹配整個字符串,直到找到一個匹配。

例:
#!/usr/bin/python
import re
line = "Cats are smarter than dogs"
matchObj = re.match( r'dogs', line, re.M|re.I)
if matchObj:
print "match --> matchObj.group() : ", matchObj.group()
else:
print "No match!!"

matchObj = re.search( r'dogs', line, re.M|re.I)
if matchObj:
   print "search --> matchObj.group() : ", matchObj.group()
else:
   print "No match!!"

運行結果:
No match!!
search --> matchObj.group() :  dogs

正則表達式修飾符 - 可選標誌

正則表達式能夠包含一些可選標誌修飾符來控制匹配的模式
修飾符被指定爲一個可選的標誌。多個標誌能夠經過按位 OR(|) 它們來指定。
如: re.I | re.M 被設置成 I 和 M 標誌

| 修飾符 | 描述 |
| re.I | 使匹配對大小寫不敏感 |
| re.L | 作本地化識別(locale-aware)匹配 |
| re.M | 多行匹配,影響 ^ 和 $ |
| re.S | 使 . 匹配包括換行在內的全部字符 |
| re.U | 根據Unicode字符集解析字符。這個標誌影響 \w, \W, \b, \B. |
| re.X | 詳細模式。這個模式下正則表達式能夠是多行,忽略空白字符,並能夠加入註釋。 |

[root@wing python]# cat a.py
#!/usr/bin/env python
import re
line='''winGa
winng
winnnga'''
r=re.compile(r'g.')        #這裏的點不包括換行,因此匹配到的是ga
print r.search(line).group()

[root@wing python]# python a.py
ga

------------------------------------------------
[root@wing python]# cat a.py
#!/usr/bin/env python
import re
line='''winGa
winng
winnnga'''
r=re.compile(r'g.',re.S)    #這裏的點包括換行,因此匹配到的是g和換行
print r.search(line).group()
[root@wing python]# python a.py
g

[root@wing python]#
-----------------------------------------------
[root@wing python]# cat a.py
#!/usr/bin/env python
#coding=utf-8
import re
line='''winGa
winng
winnnga'''
r=re.compile(r'g. #hello,這是註釋',re.X)    #用了re.X後能夠給正則表達式添加註釋
print r.search(line).group()
[root@wing python]# python a.py
ga

======================================================

findall():

findall()和 search()類似之處:

在於兩者都執行字符串搜索

findall()和 match()與search()不一樣之處:

findall()總返回一個列表(match和search返回的是元組)
若是 findall()沒有找到匹配的部分,會返回空列表
若是成功找到匹配部分,則返回全部匹配部分的列表(按從左到右出現的順序排列)

語法:

findall(rule , target [,flag]  )
    rule      規則
    target  目標字符串
    flag      規則選項(選項功能和compile和search的flag同樣)。返回結果是一個列表, 中間存放的是符合規則的字符串。若是沒有符合規則的字符串被找到,就返回一個空列表。

例子:

re.findall('car', 'car')
['car']
re.findall('car', 'scary')
['car']
re.findall('car', 'carry the barcardi to the car')
['car', 'car', 'car']

======================================================

檢索和替換

Python 的re模塊提供了re.sub和re.subn用於替換字符串中的匹配項。

re.sub():

語法:

re.sub(pattern, repl, string, max=0)
    返回的字符串是在字符串中用 RE 最左邊不重複的匹配來替換。若是模式沒有發現,字符將被沒有改變地返回。
    可選參數 count 是模式匹配後替換的最大次數;count 必須是非負整數。缺省值是 0 表示替換全部的匹配。

例:
#!/usr/bin/python
import re
phone = "2004-959-559 # This is Phone Number"

Delete Python-style comments

num = re.sub(r'#.*$', "", phone)
print "Phone Num : ", num
# Remove anything other than digits
num = re.sub(r'\D', "", phone)
print "Phone Num : ", num

執行結果:
Phone Num :  2004-959-559
Phone Num :  2004959559

re.subn():

subn()和 sub()同樣,但它還返回一個表示替換次數的數字,替換後的字符串和表示替換次數的數字做爲一個元組的元素返回。

re.sub('[ae]', 'X', 'abcdef')
'XbcdXf'
re.subn('[ae]', 'X', 'abcdef')
('XbcdXf', 2)

==================================================

re.split():

re 模塊和正則表達式對象的方法 split()與字符串的 split()方法類似,前者是根據正則表達式模式分隔字符串,後者是根據固定的字符串分割,所以與後者相比,顯著提高了字符分割的能力。若是你不想在每一個模式匹配的地方都分割字符串,你能夠經過設定一個值參數(非零)來指定分割的最大次數。

若是分隔符沒有使用由特殊符號表示的正則表達式來匹配多個模式,那re.split()和string.split()的執行過程是同樣的:

好比:在每個冒號處分隔

re.split(':', 'str1:str2:str3')
['str1', 'str2', 'str3']

使用正則表達式作分割符:

line='who ----> are ----> you'
re.split(r'\s-+>\s',line)
['who', 'are', 'you']

正則表達式模式

模式字符串使用特殊的語法來表示一個正則表達式:

• 字母和數字表示他們自身
• 多數字母和數字前加一個反斜槓時會擁有不一樣的含義
• 標點符號只有被轉義時才匹配自身,不然它們表示特殊的含義
• 反斜槓自己須要使用反斜槓轉義
• 因爲正則表達式一般都包含反斜槓,因此你最好使用原始字符串來表示它們。模式元素(如 r'\t',等價於'\\t')匹配相應的特殊字符。

下表列出了正則表達式模式語法中的特殊元素。若是你使用模式的同時提供了可選的標誌參數,某些模式元素的含義會改變。

| 模式 | 描述 |
| ^ | 匹配字符串的開頭 |
| $ | 匹配字符串的末尾。 |
| . | 匹配任意字符,除了換行符,當re.DOTALL標記被指定時,則能夠匹配包括換行符的任意字符。 |
| [...] | 用來表示一組字符,單獨列出:[amk] 匹配 'a','m'或'k' |
| [^...] | 不在[]中的字符:[^abc] 匹配除了a,b,c以外的字符。 |
| * | 匹配0個或多個的表達式。 |
| + | 匹配1個或多個的表達式。 |
| ? | 匹配0個或1個由前面的正則表達式定義的片斷,非貪婪方式 |
| {n} | 精確匹配n個前面表達式。 |
| {n,} | 匹配最少n個前面表達式。 |
| {n, m} | 匹配 n 到 m 次由前面的正則表達式定義的片斷,貪婪方式 |
| a|b | 匹配a或b |
| (re) | 既匹配括號內的表達式,也表示一個組 |
| (?: re) | 相似 (...), 可是不表示一個組 |
| (?#...) | 註釋. |
| \w | 匹配字母數字及下劃線 |
| \W | 匹配非字母數字及下劃線 |
| \s | 匹配任意空白字符,等價於 [\t\n\r\f]. |
| \S | 匹配任意非空字符 |
| \d | 匹配任意數字,等價於 [0-9]. |
| \D | 匹配任意非數字 |
| \A | 匹配字符串開始,相似於^,但他主要是爲了那些沒有caret(^)字符的鍵盤準備的 |
| \Z | 匹配字符串結束,若是是存在換行,只匹配到換行前的結束字符串。相似於$,但他主要是爲了那些沒有($)字符的鍵盤準備的 |
| \b | 匹配一個單詞邊界,也就是指單詞和空格間的位置。例如, 'er\b' 能夠匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。 |
| \B | 匹配非單詞邊界。'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。 |
| \n, \t, 等. | 匹配一個換行符。匹配一個製表符。等 |
| \1...\9 | 匹配第n個分組的子表達式。 |
| \10 | 匹配第n個分組的子表達式,若是它經匹配。不然指的是八進制字符碼的表達式。 |

注意:
1.上面有些正則若是無論用,就說明必須用原始字符串,好比'er\b'必須寫成r'er\b'纔會生效
緣由是 ASCII 字符和正則表達式特殊字符間所產生的衝突。好比,特殊符號「\b」在ASCII 字符中表明退格鍵,但同時「\b」也是一個正則表達式的特殊符號, 表明「匹配一個單詞邊界」。爲了讓 RE 編譯器把兩個字符「\b」當成你想要表達的字符串,而不是一個退格鍵,你須要用另外一個反斜線對它進行轉義,便可以這樣寫:"\b"或者r"\b"。

2.‘?’ ‘+?’ ‘??’ 最小匹配:
’ ‘+’ ‘?’ 一般都是儘量多的匹配字符。有時候咱們但願它儘量少的匹配。好比一個 c 語言的註釋 ‘/ part 1 / / part 2 /’ ,若是使用最大規則:

問號'?'有兩種含義:
1)單獨使用時表示匹配出現零次或一次的狀況
2)緊跟在表示重複的元字符後面時,表示要求搜索引擎匹配的字符串越短越好。例如:(+?),本來+表示1次或屢次,如今只表示1次
例:

re.sub(r'n+','A','wing winnng is a good student')
'wiAg wiAg is a good studeAt'
re.sub(r'n+?','A','wing winnng is a good student')
'wiAg wiAAAg is a good studeAt'

3.‘(?:)’ 無捕獲組
當你要將一部分規則做爲一個總體對它進行某些操做,好比指定其重複次數時,你須要將這部分規則用 ’(?:’ ‘)’ 把它包圍起來,而不能僅僅只用一對括號,那樣將獲得絕對出人意料的結果。
例:匹配字符串中重複的 ’ab’

s=’ababab abbabb aabaab’
re.findall( r’/b(?:ab)+/b’ , s )
['ababab']
若是僅使用一對括號,看看會是什麼結果:
re.findall( r’/b(ab)+/b’ , s )
['ab']

4.‘(?# )’ 註釋
Python 容許你在正則表達式中寫入註釋,在 ’(?#'')’ 之間的內容將被忽略。

s=’ababab abbabb aabaab’
re.findall('abab(?#註釋)ab',s)
['ababab']

5.(?iLmsux) 編譯選項指定
Python 的正則式能夠指定一些選項,這個選項能夠寫在 findall 或 compile 的參數中,也能夠寫在正則式裏,成爲正則式的一部分。這在某些狀況下會便利一些。具體的選項含義請看後面的 compile 函數的說明。
此處編譯選項 ’i’ 等價於 IGNORECASE ,L 等價於 LOCAL ,m 等價於 MULTILINE , s 等價於 DOTALL , u 等價於 UNICODE , x 等價於 VERBOSE 。
請注意它們的大小寫。在使用時能夠只指定一部分,好比只指定忽略大小寫,可寫爲 ‘(?i)’ ,要同時忽略大小寫並使用多行模式,能夠寫爲 ‘(?im)’ 。
另外要注意選項的有效範圍是整條規則,即寫在規則的任何地方,選項都會對所有整條正則式有效。

s=’ababab abbabb aabaab’
re.findall('abAb(?#註釋)ab(?i)',s)
['ababab']

6.前向界定與後向界定
有時候須要匹配一個跟在特定內容後面的或者在特定內容前面的字符串, Python 提供一個簡便的前向界定和後向界定功能,或者叫前導指定和跟從指定功能。它們是:
‘(?<=…)’ 前向界定,括號中 ’…’ 表明你但願匹配的字符串的前面應該出現的字符串。
‘(?=…)’   後向界定,括號中的 ’…’ 表明你但願匹配的字符串後面應該出現的字符串。

例: 你但願找出 c 語言的註釋中的內容,它們是包含在 ’/’ 和 ’/’ 之間,不過你並不但願匹配的結果把 ’/’ 和 ’/’ 也包括進來,那麼你能夠這樣用:

s=r’/ comment 1 /  code  / comment 2 /’
re.findall( r’(?<=/*).+?(?=*/)’ , s )
[' comment 1 ', ' comment 2 ']
注意:這裏咱們仍然使用了最小匹配,以免把整個字符串給匹配進去了。
要注意的是,前向界定括號中的表達式必須是常值,即你不能夠在前向界定的括號裏寫正則式。好比你若是在下面的字符串中想找到被字母夾在中間的數字,你不能夠用前向界定:
例:
s = ‘aaa111aaa , bbb222 , 333ccc ‘
re.findall( r’(?<=[a-z]+)\d+(?=[a-z]+)' , s )          # 錯誤的用法
它會給出一個錯誤信息:
error: look-behind requires fixed-width pattern
 
不過若是你只要找出後面接着有字母的數字,你能夠在後向界定寫正則式:
re.findall( r’\d+(?=[a-z]+)’, s )
['111', '333']

若是你必定要匹配包夾在字母中間的數字,你可使用組( group )的方式

re.findall (r'[a-z]+(\d+)[a-z]+' , s )
['111']
 
除了前向界定前向界定和後向界定外,還有前向非界定和後向非界定,它的寫法爲:
‘(?<!...)’ 前向非界定,只有當你但願的字符串前面不是’…’ 的內容時才匹配
‘(?!...)’ 後向非界定,只有當你但願的字符串後面不跟着 ’…’ 內容時才匹配
接上例,但願匹配後面不跟着字母的數字
re.findall( r’\d+(?!\w+)’ , s )
['222']

注意這裏咱們使用了 \w 而不是像上面那樣用 [a-z] ,由於若是這樣寫的話,結果會是:
    >>> re.findall( r’\d+(?![a-z]+)’ , s )
    ['11', '222', '33']
這和咱們指望的彷佛有點不同。它的緣由,是由於 ’111’ 和 ’222’ 中的前兩個數字也是知足這個要求的。

=============================================

正則表達式實例

字符匹配:

| 實例 | 描述 |
| python | 匹配 "python".  |

字符類:

| 實例 | 描述 |
| [Pp]ython  | 匹配 "Python" 或 "python" |
| rub[ye] | 匹配 "ruby" 或 "rube" |
| [aeiou] | 匹配中括號內的任意一個字母 |
| [0-9] | 匹配任何數字。相似於 [0123456789] |
| [a-z] | 匹配任何小寫字母 |
| [A-Z] | 匹配任何大寫字母 |
| [a-zA-Z0-9] | 匹配任何字母及數字 |
| [^aeiou] | 除了aeiou字母之外的全部字符  |
| [^0-9] | 匹配除了數字外的字符  |

特殊字符類:

| 實例 | 描述 |
| . | 匹配除 "\n" 以外的任何單個字符。要匹配包括 '\n' 在內的任何字符,請使用象 '[.\n]' 的模式。 |
| \d | 匹配一個數字字符。等價於 [0-9]。 |
| \D  | 匹配一個非數字字符。等價於 [^0-9]。 |
| \s | 匹配任何空白字符,包括空格、製表符、換頁符等等。等價於 [ \f\n\r\t\v]。 |
| \S  | 匹配任何非空白字符。等價於 [^ \f\n\r\t\v]。 |
| \w | 匹配包括下劃線的任何單詞字符。等價於'[A-Za-z0-9_]'。 |
| \W | 匹配任何非單詞字符。等價於 '[^A-Za-z0-9_]'。 |
相關文章
相關標籤/搜索