python內置模塊[re]
re模塊:
python的re模塊(Regular Expression正則表達式)提供各種正則表達式的匹配操作,在文本解析、復雜字符串分析和信息提取時是一個非常有用的工具。
1、re的簡介
使用python的re模塊,盡管不能滿足所有復雜的匹配情況,但足夠在絕大多數情況下能夠有效地實現對復雜字符串的分析提取相關信息。python會將正則表達式轉化為字節碼,利用C語言的匹配引擎進行深度優先的匹配。
import re print(re.__doc__)
可以查詢re模塊的功能信息。
2、re的正則表達式語法
正則表達式的特殊字符:
特殊字符 | 意義說明 |
'.' | 匹配包括換行符以內的任意一個字符。點號,在普通模式,它匹配除換行符外的任意一個字符。 |
'^' | 匹配一個字符串的開始,在 MULTILINE 模式下,也將匹配任意一個新行的開始,尖尖號。 |
'$' | 匹配一個字符串的結尾或者字符串最后面的換行符,在 MULTILINE 模式下,也匹配任意一行的行尾,美元符號。 |
'*' | 匹配*前面re的重復0次或者任意多次,而且總是試圖盡量多次地匹配,星號。 |
'+' | 匹配+前面re的重復0次或者1次,如果有的話,也盡量匹配1次,加號。 |
'?' | 匹配?前面re的重復0次或者1次,如果有的話,也盡量匹配1次,問號。 |
*?, +?, ?? | 可以看到'*','+'和'?'都是貪婪的,但這也許並不是我們說要的,所以,可以在后面加個問號,將策略改為非貪婪,只匹配盡量少的RE,也就是第一個匹配結果。 |
{m} | m是一個數字,匹配{m}前面的re重復m次。 |
{m,n} | m和n都是數字,匹配{m,n}前面的RE重復m到n次,例如a{3,5}匹配3到5個連續的a。注意,如果省略m,將匹配0到n個前面的RE;如果省略n,將匹配n到無窮多個前面的RE;當然中間的逗號是不能省略的,不然就變成前面那種形式了。 |
{m,n}? | {m,n},也是貪婪的,a{3,5}如果有5個以上連續a的話,會匹配5個,這個也可以通過加問號改變。a{3,5}?如果可能的話,將只匹配3個a。 |
'\' | 反斜桿,轉義'*','?'等特殊字符,或者指定一個特殊序列,強烈建議用raw字符串來表述正則。 |
[] | 方括號,用於指定一個字符的集合。可以單獨列出字符,也可以用'-'連接起止字符以表示一個范圍。特殊字符在中括號里將失效,比如[akm$]就表示字符'a','k','m',或'$',在這里$也變身為普通字符了。[a-z]匹配任意一個小寫字母,[a-zA-Z0-9]匹配任意一個字母或數字。如果你要匹配']'或'-'本身,你需要加反斜桿轉義,或者是將其置於中括號的最前面,比如[]]可以匹配']'。還可以對一個字符集合取反,以匹配任意不在這個字符集合里的字符,取反操作用一個'^'放在集合的最前面表示,放在其他地方的'^'將不會起特殊作用。例如[^5]將匹配任意不是'5'的字符;[^^]將匹配任意不是'^'的字符。注意:在中括號里+、*、(、)這類字符將會失去特殊含義,僅作為普通字符。反向引用也不能在中括號內使用。 |
'|' | 管道符號,A和B是任意的RE,那么A|B就是匹配A或者B的一個新的RE。任意個數的RE都可以像這樣用管道符號間隔連接起來。這種形式可以被用於組中。對於目標字符串,被'|'分割的RE將自左至右一一被測試,一旦有一個測試成功,后面的將不再被測試,即使后面的RE可能可以匹配更長的串,換句話說,'|'操作符是非貪婪的。要匹配字面意義上的'|',可以用反斜桿轉義:\|,或是包含在反括號內:[|]。 |
(...) | 匹配圓括號里的RE匹配的內容,並指定組的開始和結束位置。組里面的內容可以被提取,也可以采用\number這樣的特殊序列,被用於后續的匹配。要匹配字面意義上的'('和')',可以用反斜桿轉義:\(、\),或是包含在反括號內:[(]、[)]。 |
(?...) | 這是一個表達式的擴展符號。'?'后的第一個字母決定了整個表達式的語法和含義,除了(?P...)以外,表達式不會產生一個新的組。(?iLmsux)表示'i'、'L'、'm'、's'、'u'、'x'里的一個或多個字母。表達式不匹配任何字符,但是指定相應的標志:re.I(忽略大小寫)、re.L(依賴locale)、re.M(多行模式)、re.S(.匹配所有字符)、re.U(依賴Unicode)、re.X(詳細模式)。關於各個模式的區別。使用這個語法可以代替在re.compile()的時候或者調用的時候指定flag參數。另外,還要注意(?x)標志如果有的話,要放在最前面。 |
(?:...) | 匹配內部的RE所匹配的內容,但是不建立組。 |
(?P<name>...) | 和普通的圓括號類似,但是子串匹配到的內容將可以用命名的name參數來提取。組的name必須是有效的python標識符,而且在本表達式內不重名。命名了的組和普通組一樣,也用數字來提取,也就是說名字只是個額外的屬性。 |
(?#...) | 注釋,圓括號里的內容會被忽略。 |
(?=...) | 如果 ... 匹配接下來的字符,才算匹配,但是並不會消耗任何被匹配的字符。例如 Isaac (?=Asimov) 只會匹配后面跟着 'Asimov' 的 'Isaac ',這個叫做“前瞻斷言”。 |
(?!...) | 和(?=...)相反,只匹配接下來的字符串不匹配 ... 的串,這叫做“反前瞻斷言”。 |
(?<=...) | 只有當當前位置之前的字符串匹配 ... ,整個匹配才有效,這叫“后顧斷言”。字符串'abcdef'可以匹配正則(?<=abc)def,因為會后向查找3個字符,看是否為abc。所以內置的子RE,需要是固定長度的,比如可以是abc、a|b,但不能是a*、a{3,4}。注意這種RE永遠不會匹配到字符串的開頭。 |
(?<!...) | 這個叫做“反后顧斷言”,子RE需要固定長度的,含義是前面的字符串不匹配 ... 整個才算匹配。 |
(?(id/name)yes-pattern|no-pattern) | 如有由id或者name指定的組存在的話,將會匹配yes-pattern,否則將會匹配no-pattern,通常情況下no-pattern也可以省略。例如:(<)?(\w+@\w+(?:\.\w+)+)(?(1)>)可以匹配 '<user@host.com>' 和 'user@host.com',但是不會匹配 '<user@host.com'。 |
'$'演示:普通模式下,foo.$去搜索'foo1\nfoo2\n'只會找到'foo2′,但是在 MULTILINE 模式,還能找到 ‘foo1′,而且就用一個 $ 去搜索'foo\n'的話,會找到兩個空的匹配:一個是最后的換行符,一個是字符串的結尾。如下演示:
#'$'演示 >>> re.findall('(foo.$)', 'foo1\nfoo2\n') ['foo2'] >>> re.findall('(foo.$)', 'foo1\nfoo2\n') ['foo2'] >>> re.findall('(foo.$)', 'foo1\nfoo2\n', re.MULTILINE) ['foo1', 'foo2'] >>> re.findall('($)', 'foo\n') ['', '']
*?, +?, ??演示:
>>> re.findall('<(.*)>', '<H1>title</H1>') ['H1>title</H1'] >>> re.findall('<(.*?)>', '<H1>title</H1>')
['H1', '/H1']
>>> re.search('<(.*)>', '<H1>title</H1>').group() '<H1>title</H1>' >>> re.search('<(.*?)>', '<H1>title</H1>').group() '<H1>'
(?...)演示:和指定了re.MULTILINE是一樣的效果
>>> re.findall('(?m)(foo.$)', 'foo1\nfoo2\n') ['foo1', 'foo2']
(?P<name>...)演示:
>>> m=re.match('(?P<var>[a-zA-Z_]\w*)', 'abc=123') >>> m.group('var') #通過var參數取值 'abc' >>> m.group(1)#通過數字取值 'abc' >>> re.match('<(?P<tagname>\w*)>.*</(?P=tagname)>', '<h1>xxx</h2>') #這個不匹配 >>> re.match('<(?P<tagname>\w*)>.*</(?P=tagname)>', '<h1>xxx</h1>') #這個匹配 <_sre.SRE_Match object; span=(0, 12), match='<h1>xxx</h1>'>
(?<=...)演示:
>>> m = re.search('(?<=-)\w+', 'spam-egg') >>> m.group(0) 'egg'
正則表達式特殊序列符號:
特殊序列符號 | 意義說明 |
\number | 匹配number所指的組相同的字符串。組的序號從1開始。例如:(.+) \1可以匹配'the the'和'55 55',但不匹配'the end'。這種序列在一個正則表達式里最多可以有99個,如果number以0開頭,或是有3位以上的數字,就會被當做八進制表示的字符了。同時,這個也不能用於方括號內。 |
\A | 只匹配字符串的開始。 |
\b | 匹配單詞邊界(包括開始和結束),這里的“單詞”,是指連續的字母、數字和下划線組成的字符串。注意,\b的定義是\w和\W的交界,所以精確的定義有賴於UNICODE和LOCALE這兩個標志位。 |
\B | 和\b相反,\B匹配非單詞邊界。也依賴於UNICODE和LOCALE這兩個標志位。 |
\d | 相當於[0-9] |
\D | 和\d相反。相當於[^0-9] |
\s | 匹配任何空白字符,等效於[ \t\n\r\f\v] |
\S | 和\s相反,匹配任意非空白字符:[^\t\n\r\r\v] |
\w | 匹配任意數字和字母:[a-zA-Z0-9] |
\W | 和\w相反,匹配任意非數字和字母:[^a-zA-Z0-9] |
\Z | 只匹配字符串的結尾。 |
3、re的主要模塊功能函數:
常用的功能函數包括:compile、search、match、split、findall(finditer)、sub(subn)、escape
compile
re.compile(pattern[, flags])
作用:把正則表達式語法轉化成正則表達式對象
flags定義包括:
re.I:忽略大小寫
re.L:表示特殊字符集 \w, \W, \b, \B, \s, \S 依賴於當前環境
re.M:多行模式
re.S:’ . ’並且包括換行符在內的任意字符(注意:’ . ’不包括換行符)
re.U: 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依賴於 Unicode 字符屬性數據庫
用了re.compile以后,正則對象會得到保留,這樣在需要多次運用這個正則對象的時候,效率會有較大的提升。再用上面用過的例子來演示一下,用相同的正則匹配相同的字符串,執行100萬次,就體現出compile的效率了。
search
re.search(pattern, string[, flags])
search (string[, pos[, endpos]])
作用:在字符串中查找匹配正則表達式模式的位置,返回 MatchObject 的實例,如果沒有找到匹配的位置,則返回 None。
match
re.match(pattern, string[, flags])
match(string[, pos[, endpos]])
作用:match() 函數只在字符串的開始位置嘗試匹配正則表達式,也就是只報告從位置 0 開始的匹配情況,而 search() 函數是掃描整個字符串來查找匹配。如果想要搜索整個字符串來尋找匹配,應當用 search()。
#!/usr/bin/env python import re r1 = re.compile(r'world') if r1.match('helloworld'): print('match succeeds') else: print('match fails') if r1.search('helloworld'): print('search succeeds') else: print('search fails')
說明一下:r是raw(原始)的意思。因為在表示字符串中有一些轉義符,如表示回車'\n'。如果要表示\需要寫為'\\'。但如果我就是需要表示一個'\'+'n',不用r方式要寫為:'\\n'。但使用r方式則為r'\n'這樣清晰多了。
split
re.split(pattern, string[, maxsplit=0, flags=0])
split(string[, maxsplit=0])
作用:可以將字符串匹配正則表達式的部分割開並返回一個列表。
#簡單IP分析 #!/usr/bin/env python import re r1 = re.compile(r'\W+') print(r1.split(r'192.168.1.1')) print(re.split(r'(\W+)','192.168.1.1')) print(re.split(r'(\W+)','192.168.1.1',1)) #輸出結果為: ['192', '168', '1', '1'] ['192', '.', '168', '.', '1', '.', '1'] ['192', '.', '168.1.1']
用匹配pattern的子串來分割string,如果pattern里使用了圓括號,那么被pattern匹配到的串也將作為返回值列表的一部分。如果maxsplit不為0,則最多被分割為maxsplit個子串,剩余部分將整個地被返回。
>>> re.split('\W+', 'Words, words, words.') ['Words', 'words', 'words', ''] >>> re.split('(\W+)', 'Words, words, words.') ['Words', ', ', 'words', ', ', 'words', '.', ''] >>> re.split('\W+', 'Words, words, words.', 1) ['Words', 'words, words.']
如果正則有圓括號,並且可以匹配到字符串的開始位置的時候,返回值的第一項,會多出一個空字符串。匹配到字符結尾也是同樣的道理:
>>> re.split('(\W+)', '...words, words...') ['', '...', 'words', ', ', 'words', '...', '']
注意,split不會被零長度的正則所分割,將會報FureWarning警告,例如:
>>> re.split('x*', 'foo') C:\Users\Administrator\AppData\Local\Programs\Python\Python35-32\lib\re.py:203: FutureWarning: split() requires a non-empty pattern match. return _compile(pattern, flags).split(string, maxsplit) ['foo']
findall
re.findall(pattern, string[, flags])
findall(string[, pos[, endpos]])
作用:在字符串中找到正則表達式所匹配的所有子串,並組成一個列表返回。
#查找[]包括的內容(貪婪和非貪婪查找) #!/usr/bin/env python import re r1 = re.compile(r'([.*])') print(re.findall(r1,"hello[hi]heldfsdsf[iwonder]lo")) r1 = re.compile(r'([.*?])') print(re.findall(r1,"hello[hi]heldfsdsf[iwonder]lo")) print(re.findall(r'[0-9]{2}',"fdskfj1323jfkdj")) print(re.findall('([0-9][a-z])',"fdskfj1323jfkdj")) print(re.findall(r'(?=www)',"afdsfwwwfkdjfsdfsdwww")) print(re.findall(r'(?<=www)',"afdsfwwwfkdjfsdfsdwww")) #輸入結果: [] [] ['13', '23'] ['3j'] ['', ''] ['', '']
#這個返回的就是元組的列表 >>> re.findall('(\d+)\.(\d+)\.(\d+)\.(\d+)', 'My IP is 192.168.0.2, and your is 192.168.0.3.') [('192', '168', '0', '2'), ('192', '168', '0', '3')]
finditer
re.finditer(pattern, string[, flags])
finditer(string[, pos[, endpos]])
說明:和 findall 類似,在字符串中找到正則表達式所匹配的所有子串,並組成一個迭代器返回。
>>> for m in re.finditer('\w+', 'hello, world!'): ... print(m.group()) ... hello world
sub
re.sub(pattern, repl, string[, count, flags])
sub(repl, string[, count=0])
說明:在字符串 string 中找到匹配正則表達式 pattern 的所有子串,用另一個字符串 repl 進行替換。如果沒有找到匹配 pattern 的串,則返回未被修改的 string。Repl 既可以是字符串也可以是一個函數。
#!/usr/bin/env python import re p = re.compile('(one|two|three)') print(p.sub('num','one word two words three words apple',2))
#輸出結果: num word num words three words apple
如果repl是個函數,每次pattern被匹配到的時候,都會被調用一次,傳入一個匹配到的MatchObject對象,需要返回一個字符串,在匹配到的位置,就填入返回的字符串。
>>> def dashrepl(matchobj): ... if matchobj.group(0) == '-': return ' ' ... else:return '-' >>> re.sub('-{1,2}', dashrepl, 'pro----gram-files') 'pro--gram files'
零長度的匹配也會被替換
>>> re.sub('x*', '-', 'abcxxd') '-a-b-c-d-'
特殊地,在替換字符串里,如果有\g這樣的寫法,將匹配正則的命名組(前面介紹過的,(?P...)這樣定義出來的東西)。\g這樣的寫法,也是數字的組,也就是說,\g<2>一般和\2是等效的,但是萬一你要在\2后面緊接着寫上字面意義的0,你就不能寫成\20了(因為這代表第20個組),這時候必須寫成\g<2>0,另外,\g<0>代表匹配到的整個子串。
>>> re.sub('-(\d+)-', '-\g<1>0\g<0>', 'a-11-b-22-c') 'a-110-11-b-220-22-c'
subn
re.subn(pattern, repl, string[, count, flags])
subn(repl, string[, count=0])
說明:該函數的功能和 sub() 相同,只是它返回的是一個元組 (新字符串, 匹配到的次數)。
>>> re.subn('-(\d+)-', '-\g<1>0\g<0>', 'a-11-b-22-c') ('a-110-11-b-220-22-c', 2)
escape
把string中,除了字母和數字以外的字符,都加上反斜桿。
>>> print(re.escape('abc123_@#$')) abc123_\@\#\$
4、編譯標志:
標志 | 含義 |
re.I,re.IGNORECASE | 讓正則表達式忽略大小寫,這樣一來,[A-Z]也可以匹配小寫字母了。此特性和locale無關。 |
re.L,re.LOCALE | 讓\w、\W、\b、\B、\s和\S依賴當前的locale。 |
re.M,re.MULTILINE | 影響'^'和'$'的行為,指定了以后,'^'會增加匹配每行的開始(也就是換行符后的位置);'$'會增加匹配每行的結束(也就是換行符前的位置)。 |
re.S,re.DOTALL | 影響'.'的行為,平時'.'匹配除換行符以外的所有字符,指定了本標志以后,也可以匹配換行符。 |
re.U,re.UNICODE | 讓\w、\W、\b、\B、\d、\D、\s和\S依賴Unicode庫。 |
re.X,re.VERBOSE | 運用這個標志,你可以寫出可讀性更好的正則表達式:除了在方括號內的和被反斜杠轉義的以外的所有空白字符,都將被忽略,而且每行中,一個正常的井號后的所有字符也被忽略,這樣就可以方便地在正則表達式內部寫注釋了。 |
#下面兩個正則表達式是等效的 a = re.compile(r"""\d + # the integral part \. # the decimal point \d * # some fractional digits""", re.X) b = re.compile(r"\d+\.\d*")
5、方法/屬性
groups
RE所含有的組的個數。
groupindex
一個mappingproxy字典,定義了命名組的名字和序號之間的關系。
#這個正則有3個組,如果匹配到,第一個叫區號,最后一個叫分機號,中間的那個未命名
>>> pattern = re.compile("(?P<quhao>\d+)-(\d+)-(?P<fenjihao>\d+)") >>> pattern.groups 3 >>> pattern.groupindex mappingproxy({'fenjihao': 3, 'quhao': 1})
pattern
建立本RE的原始字符串,相當於源代碼了,呵呵。
>>> pattern = re.compile("(?P<quhao>\d+)-(\d+)-(?P<fenjihao>\d+)") >>> print(pattern.pattern) (?P<quhao>\d+)-(\d+)-(?P<fenjihao>\d+)
group([group1, ...])
返回一個或多個子組。如果參數為一個,就返回一個子串;如果參數有多個,就返回多個子串注冊的元組。如果不傳任何參數,效果和傳入一個0一樣,將返回整個匹配。如果某個groupN未匹配到,相應位置會返回None。如果某個groupN是負數或者大於group的總數,則會拋出IndexError異常。
>>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist") >>> m.group(0) #整個匹配 'Isaac Newton' >>> m.group(1)#第一個子串 'Isaac' >>> m.group(2)#第二個子串 'Newton' >>> m.group(1,2) #多個子串組成元組 ('Isaac', 'Newton')
如果有其中有用(?P...)這種語法命名過的子串的話,相應的groupN也可以是名字字符串。
>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds") >>> m.group('first_name') 'Malcolm' >>> m.group('last_name') 'Reynolds'
如果某個組被匹配到多次,那么只有最后一次的數據,可以被提取到
>>> m = re.match(r"(..)+", "a1b2c3") >>> m.group(0) 'a1b2c3' >>> m.group(1) 'c3' >>> m.group(2)
groups([default])
返回一個由所有匹配到的子串組成的元組。default參數,用於給那些沒有匹配到的組做默認值,它的默認值是None
>>> m = re.match(r"(\d+)\.(\d+)", "24.1632") >>> m.groups() ('24', '1632') #default的作用 >>> m = re.match(r"(\d+)\.?(\d+)?", "24") >>> m.groups() #第二個默認是None ('24', None) >>> m.groups('0')#現在默認變成0 ('24', '0')
groupdict([default])
返回一個包含所有命名組的名字和子串的字典,default參數,用於給那些沒有匹配到的組做默認值,它的默認值是None
>>> m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds") >>> m.groupdict() {'first_name': 'Malcolm', 'last_name': 'Reynolds'}
start([group]),end([group])
返回的是:被組group匹配到的子串在原字符串中的位置。如果不指定group或group指定為0,則代表整個匹配。如果group未匹配到,則返回 -1。
對於指定的m和g,m.group(g)和m.string[m.start(g):m.end(g)]等效。
注意:如果group匹配到空字符串,m.start(group)和m.end(group)將相等
>>> m = re.search('b(c?)', 'cba') >>> m.start(0) 1 >>> m.end(0) 2 >>> m.start(1) 2 >>> m.end(1) 2
下面是一個把email地址里的“remove_this”去掉的例子
>>> email = "tony@tiremove_thisger.net" >>> m = re.search("remove_this", email) >>> email[:m.start()] + email[m.end():] 'tony@tiger.net'
span([group])
返回一個元組: (m.start(group), m.end(group))
pos
就是傳給RE對象的search()或match()方法的參數pos,代表RE開始搜索字符串的位置。
endpos
就是傳給RE對象的search()或match()方法的參數endpos,代表RE搜索字符串的結束位置。
lastindex
最后一次匹配到的組的數字序號,如果沒有匹配到,將得到None。
例如:(a)b、((a)(b))和((ab))正則去匹配'ab'的話,得到的lastindex為1。而用(a)(b)去匹配'ab'的話,得到的lastindex為2。
lastgroup
最后一次匹配到的組的名字,如果沒有匹配到或者最后的組沒有名字,將得到None。
re
得到本Match對象的正則表達式對象,也就是執行search()或match()的對象。
string
傳給search()或match()的字符串。