import re
re.match #從開始位置開始匹配,如果開頭沒有則無
re.search #搜索整個字串
re.findall #搜索整個字串,回傳一個list
# 嘗試從字串起始位置匹配一個模式;如果不是起始位置匹配成功,則 re.match() 回傳none.匹配成功,re.match() 回傳一個匹配的物件,否則回傳None.
# re.search(pattern, string, flags=0)掃描整個字串并回傳第一個成功的匹配.匹配成功, re.search()回傳一個匹配的物件,否則回傳None.
re.sub(pattern, repl, string, count=0, flags=0)
替換字串中的匹配項.
pattern - 正則中的模式字串
repl - 替換的字串,也可為一個函式
string - 要被查找替換的原始字串
count - 模式匹配后替換的最大次數,默認 0 表示替換所有的匹配
正則運算式可以包含一些可選標志修飾符來控制匹配的模式,修飾符被指定為一個可選的標志,多個標志可以通過按位 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 | 該標志通過給予你更靈活的格式以便你將正則運算式寫得更易于理解, |
例如:
re.search(r'[a-z]+','liuyaN1234ab9').group() #回傳'liuya'
re.search(r'[a-z]+','liuyaN1234ab9', re.I).group() #回傳'liuyaN',對大小寫不敏感
#用空格分割
re.split(r'\s+', 'a b c')
回傳:['a', 'b', 'c', 'd']
#用逗號分隔
re.split(r'[\s\,]+', 'a,b, c d')
回傳:['a', 'b', 'c', 'd']
模式字串使用特殊的語法來表示一個正則運算式:
字母和數字表示他們自身,一個正則運算式模式中的字母和數字匹配同樣的字串,
多數字母和數字前加一個反斜杠時會擁有不同的含義,
標點符號只有被轉義時才匹配自身,否則它們表示特殊的含義,
反斜杠本身需要使用反斜杠轉義,
由于正則運算式通常都包含反斜杠,所以你最好使用原始字串來表示它們,模式元素(如 r'/t',等價于'//t')匹配相應的特殊字符,
下表列出了正則運算式模式語法中的特殊元素,如果你使用模式的同時提供了可選的標志引數,某些模式元素的含義會改變,
| 模式 | 描述 |
|---|---|
| ^ | 匹配字串的開頭 |
| $ | 匹配字串的末尾, |
| . | 匹配任意字符,除了換行符,當re.DOTALL標記被指定時,則可以匹配包括換行符的任意字符, |
| [...] | 用來表示一組字符,單獨列出:[amk] 匹配 'a','m'或'k' |
| [^...] | 不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符, |
| re* | 匹配0個或多個的運算式, |
| re+ | 匹配1個或多個的運算式, |
| re? | 匹配0個或1個由前面的正則運算式定義的片段,非貪婪方式 |
| re{ n} | |
| re{ n,} | 精確匹配n個前面運算式, |
| re{ n, m} | 匹配 n 到 m 次由前面的正則運算式定義的片段,貪婪方式 |
| a | b |
| (re) | G匹配括號內的運算式,也表示一個組 |
| (?imx) | 正則運算式包含三種可選標志:i, m, 或 x ,只影響括號中的區域, |
| (?-imx) | 正則運算式關閉 i, m, 或 x 可選標志,只影響括號中的區域, |
| (?: re) | 類似 (...), 但是不表示一個組 |
| (?imx: re) | 在括號中使用i, m, 或 x 可選標志 |
| (?-imx: re) | 在括號中不使用i, m, 或 x 可選標志 |
| (?#...) | 注釋. |
| (?= re) | 前向肯定界定符,如果所含正則運算式,以 ... 表示,在當前位置成功匹配時成功,否則失敗,但一旦所含運算式已經嘗試,匹配引擎根本沒有提高;模式的剩余部分還要嘗試界定符的右邊, |
| (?! re) | 前向否定界定符,與肯定界定符相反;當所含運算式不能在字串當前位置匹配時成功 |
| (?> re) | 匹配的獨立模式,省去回溯, |
| \w | 匹配字母數字 |
| \W | 匹配非字母數字 |
| \s | 匹配任意空白字符,等價于 [\t\n\r\f]. |
| \S | 匹配任意非空字符 |
| \d | 匹配任意數字,等價于 [0-9]. |
| \D | 匹配任意非數字 |
| \A | 匹配字串開始 |
| \Z | 匹配字串結束,如果是存在換行,只匹配到換行前的結束字串, |
| \z | 匹配字串結束 |
| \G | 匹配最后匹配完成的位置, |
| \b | 匹配一個單詞邊界,也就是指單詞和空格間的位置,例如, 'er\b' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er', |
| \B | 匹配非單詞邊界,'er\B' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er', |
| \n, \t, 等. | 匹配一個換行符,匹配一個制表符,等 |
| \1...\9 | 匹配第n個分組的子運算式, |
| \10 | 匹配第n個分組的子運算式,如果它經匹配,否則指的是八進制字符碼的運算式, |
正則運算式實體
字符類:
| 實體 | 描述 |
|---|---|
| [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_]', |
基本規則
‘[‘ ‘]’ 字符集合設定符
首先說明一下字符集合設定的方法,由一對方括號括起來的字符,表明一個字符集合,能夠匹配包含在其中的任意一個字符,比如 [abc123] ,表明字符 ’a’ ‘b’ ‘c’ ‘1’ ‘2’ ‘3’ 都符合它的要求,可以被匹配,
在 ’[‘ ‘]’ 中還可以通過 ’-‘ 減號來指定一個字符集合的范圍,比如可以用 [a-zA-Z] 來指定所以英文字母的大小寫,因為英文字母是按照從小到大的順序來排的,你不可以把大小的順序顛倒了,比如寫成 [z-a] 就不對了,
如果在 ’[‘ ‘]’ 里面的開頭寫一個 ‘^’ 號,則表示取非,即在括號里的字符都不匹配,如 [^a-zA-Z] 表明不匹配所有英文字母,但是如果 ‘^’ 不在開頭,則它就不再是表示取非,而表示其本身,如 [a-z^A-Z] 表明匹配所有的英文字母和字符 ’^’ ,
‘|’ 或規則
將兩個規則并列起來,以‘ | ’連接,表示只要滿足其中之一就可以匹配,比如
[a-zA-Z]|[0-9] 表示滿足數字或字母就可以匹配,這個規則等價于 [a-zA-Z0-9]
注意 :關于 ’|’ 要注意兩點:
第一, 它在 ’[‘ ‘]’ 之中不再表示或,而表示他本身的字符,如果要在 ’[‘ ‘]’ 外面表示一個 ’|’ 字符,必須用反斜杠引導,即 ’/|’ ;
第二, 它的有效范圍是它兩邊的整條規則,比如‘ dog|cat’ 匹配的是‘ dog’ 和 ’cat’ ,而不是 ’g’ 和 ’c’ ,如果想限定它的有效范圍,必需使用一個無捕獲組 ‘(?: )’ 包起來,比如要匹配 ‘ I have a dog’ 或 ’I have a cat’ ,需要寫成 r’I have a (?:dog|cat)’ ,而不能寫成 r’I have a dog|cat’
‘.’ 匹配所有字符
匹配除換行符 ’/n’ 外的所有字符,如果使用了 ’S’ 選項,匹配包括 ’/n’ 的所有字符,
例:
>>> s=’123 /n456 /n789’
>>> findall(r‘.+’,s)
['123', '456', '789']
>>> re.findall(r‘.+’ , s , re.S)
['123/n456/n789']
‘^’ 和 ’$’ 匹配字串開頭和結尾
注意 ’^’ 不能在‘ [ ] ’中,否則含意就發生變化,具體請看上面的 ’[‘ ‘]’ 說明, 在多行模式下,它們可以匹配每一行的行首和行尾,具體請看后面 compile 函式說明的 ’M’ 選項部分
‘/d’ 匹配數字
這是一個以 ’/’ 開頭的轉義字符, ’/d’ 表示匹配一個數字,即等價于 [0-9]
‘/D’ 匹配非數字
這個是上面的反集,即匹配一個非數字的字符,等價于 [^0-9] ,注意它們的大小寫,下面我們還將看到 Python 的正則規則中很多轉義字符的大小寫形式,代表互補的關系,這樣很好記,
‘/w’ 匹配字母和數字
匹配所有的英文字母和數字,即等價于 [a-zA-Z0-9] ,
‘/W’ 匹配非英文字母和數字
即 ’/w’ 的補集,等價于 [^a-zA-Z0-9] ,
‘/s’ 匹配間隔符
即匹配空格符、制表符、回車符等表示分隔意義的字符,它等價于 [ /t/r/n/f/v] ,(注意最前面有個空格 )
‘/S’ 匹配非間隔符
即間隔符的補集,等價于 [^ /t/r/n/f/v]
‘/A’ 匹配字串開頭
匹配字串的開頭,它和 ’^’ 的區別是, ’/A’ 只匹配整個字串的開頭,即使在 ’M’ 模式下,它也不會匹配其它行的很首,
‘/Z’ 匹配字串結尾
匹配字串的結尾,它和 ’$’ 的區別是, ’/Z’ 只匹配整個字串的結尾,即使在 ’M’ 模式下,它也不會匹配其它各行的行尾,
例:
>>> s= '12 34/n56 78/n90'
>> re.findall( r'^/d+' , s , re.M ) # 匹配位于行首的數字
['12', '56', '90']
>>> re.findall( r’/A/d+’, s , re.M ) # 匹配位于字串開頭的數字
['12']
>>> re.findall( r'/d+$' , s , re.M ) # 匹配位于行尾的數字
['34', '78', '90']
>>> re.findall( r’/d+/Z’ , s , re.M ) # 匹配位于字串尾的數字
['90']
‘/b’ 匹配單詞邊界
它匹配一個單詞的邊界,比如空格等,不過它是一個‘ 0 ’長度字符,它匹配完的字串不會包括那個分界的字符,而如果用 ’/s’ 來匹配的話,則匹配出的字串中會包含那個分界符,
例:
>>> s = 'abc abcde bc bcd'
>>> re.findall( r’/bbc/b’ , s ) # 匹配一個單獨的單詞 ‘bc’ ,而當它是其它單詞的一部分的時候不匹配
['bc'] #只找到了那個單獨的 ’bc’
>>> re.findall( r’/sbc/s’ , s ) #匹配一個單獨的單詞 ‘bc’
[' bc '] # 只找到那個單獨的 ’bc’ ,不過注意前后有兩個空格,可能有點看不清楚
‘/B’ 匹配非邊界
和 ’/b’ 相反,它只匹配非邊界的字符,它同樣是個 0 長度字符,
接上例:
>>> re.findall( r’/Bbc/w+’ , s ) # 匹配包含 ’bc’ 但不以 ’bc’ 為開頭的單詞
['bcde'] # 成功匹配了 ’abcde’ 中的 ’bcde’ ,而沒有匹配 ’bcd’
**‘(?:)’ 無捕獲組**
當你要將一部分規則作為一個整體對它進行某些操作,比如指定其重復次數時,你需要將這部分規則用 ’(?:’ ‘)’ 把它包圍起來,而不能僅僅只用一對括號,那樣將得到絕對出人意料的結果,
例:匹配字串中重復的 ’ab’
>>> s=’ababab abbabb aabaab’
>>> re.findall( r’/b(?:ab)+/b’ , s )
['ababab']
如果僅使用一對括號,看看會是什么結果:
>>> re.findall( r’/b(ab)+/b’ , s )
['ab']
這是因為如果只使用一對括號,那么這就成為了一個組 (group) ,組的使用比較復雜,將在后面詳細講解,
**‘(?# )’ 注釋**
Python 允許你在正則運算式中寫入注釋,在 ’(?#’ ‘)’ 之間的內容將被忽略,
(?iLmsux) 編譯選項指定
Python 的正則式可以指定一些選項,這個選項可以寫在 findall 或 compile 的引數中,也可以寫在正則式里,成為正則式的一部分,這在某些情況下會便利一些,具體的選項含義請看后面的 compile 函式的說明,
此處編譯選項 ’i’ 等價于 IGNORECASE ,L 等價于 LOCAL ,m 等價于 MULTILINE , s 等價于 DOTALL , u 等價于 UNICODE , x 等價于 VERBOSE ,
請注意它們的大小寫,在使用時可以只指定一部分,比如只指定忽略大小寫,可寫為 ‘(?i)’ ,要同時忽略大小寫并使用多行模式,可以寫為 ‘(?im)’ ,
另外要注意選項的有效范圍是整條規則,即寫在規則的任何地方,選項都會對全部整條正則式有效,
重復
正則式需要匹配不定長的字串,那就一定需要表示重復的指示符, Python 的正則式表示重復的功能很豐富靈活,重復規則的一般的形式是在一條字符規則后面緊跟一個表示重復次數的規則,已表明需要重復前面的規則一定的次數,重復規則有:
‘*’ 0 或多次匹配
表示匹配前面的規則 0 次或多次,
‘+’ 1 次或多次匹配
表示匹配前面的規則至少 1 次,可以多次匹配
例:匹配以下字串中的前一部分是字母,后一部分是數字或沒有的變數名字
>>> s = ‘ aaa bbb111 cc22cc 33dd ‘
>>> re.findall( r’/b[a-z]+/d*/b’ , s ) # 必須至少 1 個字母開頭,以連續數字結尾或沒有數字
['aaa', 'bbb111']
注意上例中規則前后加了表示單詞邊界的 ’/b’ 指示符,如果不加的話結果就會變成:
>>> re.findall( r’[a-z]+/d*’ , s )
['aaa', 'bbb111', 'cc22', 'cc', 'dd'] # 把單詞給拆開了
大多數情況下這不是我們期望的結果,
‘?’ 0 或 1 次匹配
只匹配前面的規則 0 次或 1 次,
例,匹配一個數字,這個數字可以是一個整數,也可以是一個科學計數法記錄的數字,比如 123 和 10e3 都是正確的數字,
>>> s = ‘ 123 10e3 20e4e4 30ee5 ‘
>>> re.findall( r’ /b/d+[eE]?/d*/b’ , s )
['123', '10e3']
它正確匹配了 123 和 10e3, 正是我們期望的,注意前后的 ’/b’ 的使用,否則將得到不期望的結果,
前向界定與后向界定
有時候需要匹配一個跟在特定內容后面的或者在特定內容前面的字串, 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 就要多試,跌跌撞撞的走過來,雖然曲折,卻也很有樂趣,
組的基本知識
上面我們已經看過了 Python 的正則式的很多基本用法,不過如果僅僅是上面那些規則的話,還是有很多情況下會非常麻煩,比如上面在講前向界定和后向界定時,取夾在字母中間的數字的例子,用前面講過的規則都很難達到目的,但是用了組以后就很簡單了,
‘(‘’)’ 無命名組
最基本的組是由一對圓括號括起來的正則式,比如上面匹配包夾在字母中間的數字的例子中使用的 (/d+) ,我們再回顧一下這個例子:
>>> s = ‘aaa111aaa , bbb222 , 333ccc ‘
>>> re.findall (r'[a-z]+(/d+)[a-z]+' , s )
['111']
可以看到 findall 函式只回傳了包含在 ’()’ 中的內容,而雖然前面和后面的內容都匹配成功了,卻并不包含在結果中,
除了最基本的形式外,我們還可以給組起個名字,它的形式是
‘(?P<name>…)’ 命名組
‘(?P’ 代表這是一個 Python 的語法擴展 ’<…>’ 里面是你給這個組起的名字,比如你可以給一個全部由數字組成的組叫做 ’num’ ,它的形式就是 ’(?P<num>/d+)’ ,起了名字之后,我們就可以在后面的正則式中通過名字呼叫這個組,它的形式是
‘(?P=name)’ 呼叫已匹配的命名組
要注意,再次呼叫的這個組是已被匹配的組,也就是說它里面的內容是和前面命名組里的內容是一樣的,
我們可以看更多的例子:請注意下面這個字串各子串的特點,
>>> s='aaa111aaa,bbb222,333ccc,444ddd444,555eee666,fff777ggg'
我們看看下面的正則式會回傳什么樣的結果:
>>> re.findall( r'([a-z]+)/d+([a-z]+)' , s ) # 找出中間夾有數字的字母
[('aaa', 'aaa'), ('fff', 'ggg')]
>>> re.findall( r '(?P<g1>[a-z]+)/d+(?P=g1)' , s ) # 找出被中間夾有數字的前后同樣的字母
['aaa']
>>> re.findall( r'[a-z]+(/d+)([a-z]+)' , s ) # 找出前面有字母引導,中間是數字,后面是字母的字串中的中間的數字和后面的字母
[('111', 'aaa'), ('777', 'ggg')]
我們可以通過命名組的名字在后面呼叫已匹配的命名組,不過名字也不是必需的,
‘/number’ 通過序號呼叫已匹配的組
正則式中的每個組都有一個序號,序號是按組從左到右,從 1 開始的數字,你可以通過下面的形式來呼叫已匹配的組
比如上面找出被中間夾有數字的前后同樣的字母的例子,也可以寫成:
>>> re.findall( r’([a-z]+)/d+/1’ , s )
['aaa']
結果是一樣的,
我們再看一個例子
>>> s='111aaa222aaa111 , 333bbb444bb33'
>>> re.findall( r'(/d+)([a-z]+)(/d+)(/2)(/1)' , s ) # 找出完全對稱的 數字-字母-數字-字母-數字 中的數字和字母
[('111', 'aaa', '222', 'aaa', '111')]
Python2.4 以后的 re 模塊,還加入了一個新的條件匹配功能
‘(?( id/name )yes-pattern|no-pattern)’ 判斷指定組是否已匹配,執行相應的規則
這個規則的含義是,如果 id/name 指定的組在前面匹配成功了,則執行 yes-pattern 的正則式,否則執行 no-pattern 的正則式,
舉個例子,比如要匹配一些形如 usr@mail 的郵箱地址,不過有的寫成 < usr@mail > 即用一對 <> 括起來,有點則沒有,要匹配這兩種情況,可以這樣寫
>>> s='<usr1@mail1> usr2@maill2'
>>> re.findall( r'(<)?/s*(/w+@/w+)/s*(?(1)>)' , s )
[('<', 'usr1@mail1'), ('', 'usr2@maill2')]
不過如果目標字串如下
>>> s='<usr1@mail1> usr2@maill2 <usr3@mail3 usr4@mail4> < usr5@mail5 '
而你想得到要么由一對 <> 包圍起來的一個郵件地址,要么得到一個沒有被 <> 包圍起來的地址,但不想得到一對 <> 中間包圍的多個地址或不完整的 <> 中的地址,那么使用這個式子并不能得到你想要的結果
>>> re.findall( r'(<)?/s*(/w+@/w+)/s*(?(1)>)' , s )
[('<', 'usr1@mail1'), ('', 'usr2@maill2'), ('', 'usr3@mail3'), ('', 'usr4@mail4'), ('', 'usr5@mail5')]
它仍然找到了所有的郵件地址,
想要實作這個功能,單純的使用 findall 有點吃力,需要使用其它的一些函式,比如 match 或 search 函式,再配合一些控制功能,這部分的內容將在下面詳細講解,
小結:以上基本上講述了 Python 正則式的語法規則,雖然大部分語法規則看上去都很簡單,可是稍不注意,仍然會得到與期望大相徑庭的結果,所以要寫好正則式,需要仔細的體會正則式規則的含義后不同規則之間細微的差別,
詳細的了解了規則后,再配合后面就要介紹的功能函式,就能最大的發揮正則式的威力了,
re 模塊的基本函式
在上面的說明中,我們已經對 re 模塊的基本函式 ‘findall’ 很熟悉了,當然如果光有 findall 的話,很多功能是不能實作的,下面開始介紹一下 re 模塊其它的常用基本函式,靈活搭配使用這些函式,才能充分發揮 Python 正則式的強大功能,
首先還是說下老熟人 findall 函式吧
findall(rule , target [,flag] )
在目標字串中查找符合規則的字串,
第一個引數是規則,第二個引數是目標字串,后面還可以跟一個規則選項(選項功能將在 compile 函式的說明中詳細說明),
回傳結果結果是一個串列, 中間存放的是符合規則的字串,如果沒有符合規則的字串被找到,就回傳一個空 串列,
2.1 使用 compile 加速
compile( rule [,flag] )
將正則規則編譯成一個 Pattern 物件,以供接下來使用,
第一個引數是規則式,第二個引數是規則選項,
回傳一個 Pattern 物件
直接使用 findall ( rule , target ) 的方式來匹配字串,一次兩次沒什么,如果是多次使用的話,由于正則引擎每次都要把規則解釋一遍,而規則的解釋又是相當費時間的,所以這樣的效率就很低了,如果要多次使用同一規則來進行匹配的話,可以使用 re.compile 函式來將規則預編譯,使用編譯過回傳的 Regular Expression Object 或叫做 Pattern 物件來進行查找,
例
>>> s='111,222,aaa,bbb,ccc333,444ddd'
>>> rule=r’/b/d+/b’
>>> compiled_rule=re.compile(rule)
>>> compiled_rule.findall(s)
['111', '222']
可見使用 compile 過的規則使用和未編譯的使用很相似, compile 函式還可以指定一些規則標志,來指定一些特殊選項,多個選項之間用 ’| ’ (位或)連接起來,
I IGNORECASE 忽略大小寫區別,
L LOCAL 字符集本地化,這個功能是為了支持多語言版本的字符集使用環境的,比如在轉義符/w ,在英文環境下,它代表[a-zA-Z0-9] ,即所以英文字符和數字,如果在一個法語環境下使用,預設設定下,不能匹配 "é" 或 "?" , 加上這 L 選項和就可以匹配了,不過這個對于中文環境似乎沒有什么用,它仍然不能匹配中文字符,
M MULTILINE 多行匹配,在這個模式下 ’^’( 代表字串開頭 ) 和 ’$’( 代表字串結尾 ) 將能夠匹配多行的情況,成為行首和行尾標記,比如
>>> s=’123 456/n789 012/n345 678’
>>> rc=re.compile(r’^/d+’) # 匹配一個位于開頭的數字,沒有使用 M 選項
>>> rc.findall(s)
['123'] # 結果只能找到位于第一個行首的 ’123’
>>> rcm=re.compile(r’^/d+’,re.M) # 使用 M 選項
>>> rcm.findall(s)
['123', '789', '345'] # 找到了三個行首的數字
同樣,對于 ’$’ 來說,沒有使用 M 選項,它將匹配最后一個行尾的數字,即 ’678’ ,加上以后,就能匹配三個行尾的數字 456 012 和 678 了 .
>>> rc=re.compile(r’/d+$’)
>>> rcm=re.compile(r’/d+$’,re.M)
>>> rc.findall(s)
['678']
>>> rcm.findall(s)
['456', '012', '678']
S DOTALL ‘.’ 號將匹配所有的字符,預設情況下 ’.’ 匹配除換行符 ’/n’ 外的所有字符,使用這一選項以后, ’.’ 就能匹配包括 ’/n’ 的任何字符了,
U UNICODE /w , /W , /b , /B , /d , /D , /s 和 /S 都將使用Unicode ,
X VERBOSE 這個選項忽略規則運算式中的空白,并允許使用 ’#’ 來引導一個注釋,這樣可以讓你把規則寫得更美觀些,比如你可以把規則
>>> rc
= re.compile(
r
"
/d+|[a-zA-Z]+
")
#
匹配一個數字或者單詞
使用 X 選項寫成:
>>> rc
= re.compile(r"""
# start a rule
/d+
#
number
|
[a-zA-Z]+
#
word
""", re.VERBOSE)
在這個模式下,如果你想匹配一個空格,你必須用'/ '
的形式('/'
后面跟一個空格)
2.2 match 與 search
match( rule , targetString [,flag] )
search( rule , targetString [,flag] )
(注: re 的 match 與 search 函式同 compile 過的 Pattern 物件的 match 與 search 函式的引數是不一樣的, Pattern 物件的 match 與 search 函式更為強大,是真正最常用的函式)
按照規則在目標字串中進行匹配,
第一個引數是正則規則,第二個是目標字串,第三個是選項(同 compile 函式的選項)
回傳:若成功回傳一個 Match 物件,失敗無回傳
findall 雖然很直觀,但是在進行更復雜的操作時,就有些力不從心了,此時更多的使用的是 match 和 search 函式,他們的引數和 findall 是一樣的,都是:
match( rule , targetString [,flag] )
search( rule , targetString [,flag] )
不過它們的回傳不是一個簡單的字串串列,而是一個 MatchObject (如果匹配成功的話) . ,通過操作這個 matchObject ,我們可以得到更多的資訊,
需要注意的是,如果匹配不成功,它們則回傳一個 NoneType ,所以在對匹配完的結果進行操作之前,你必需先判斷一下是否匹配成功了,比如:
>>> m=re.match( rule , target )
>>> if m: # 必需先判斷是否成功
doSomethin
這兩個函式唯一的區別是: match 從字串的開頭開始匹配,如果開頭位置沒有匹配成功,就算失敗了;而 search 會跳過開頭,繼續向后尋找是否有匹配的字串,針對不同的需要,可以靈活使用這兩個函式,
關于 match 回傳的 MatchObject 如果使用的問題,是 Python 正則式的精髓所在,它與組的使用密切相關,我將在下一部分詳細講解,這里只舉個最簡單的例子:
例:
>>> s= 'Tom:9527 , Sharry:0003'
>>> m=re.match( r'(?P<name>/w+):(?P<num>/d+)' , s )
>>> m.group()
'Tom:9527'
>>> m.groups()
('Tom', '9527')
>>> m.group(‘name’)
'Tom'
>>> m.group(‘num’)
'9527'
2.3 finditer
finditer( rule , target [,flag] )
引數同 findall
回傳一個迭代器
finditer 函式和 findall 函式的區別是, findall 回傳所有匹配的字串,并存為一個串列,而 finditer 則并不直接回傳這些字串,而是回傳一個迭代器,關于迭代器,解釋起來有點復雜,還是看看例子把:
>>> s=’111 222 333 444’
>>> for i in re.finditer(r’/d+’ , s ):
print i.group(),i.span() # 列印每次得到的字串和起始結束位置
結果是
111 (0, 3)
222 (4, 7)
333 (8, 11)
444 (12, 15)
簡單的說吧,就是 finditer 回傳了一個可呼叫的物件,使用 for i in finditer() 的形式,可以一個一個的得到匹配回傳的 Match 物件,這在對每次回傳的物件進行比較復雜的操作時比較有用,
2.4 字串的替換和修改
re 模塊還提供了對字串的替換和修改函式,他們比字串物件提供的函式功能要強大一些,這幾個函式是
sub ( rule , replace , target [,count] )
subn(rule , replace , target [,count] )
在目標字串中規格規則查找匹配的字串,再把它們替換成指定的字串,你可以指定一個最多替換次數,否則將替換所有的匹配到的字串,
第一個引數是正則規則,第二個引數是指定的用來替換的字串,第三個引數是目標字串,第四個引數是最多替換次數,
這兩個函式的唯一區別是回傳值,
sub 回傳一個被替換的字串
sub 回傳一個元組,第一個元素是被替換的字串,第二個元素是一個數字,表明產生了多少次替換,
例,將下面字串中的 ’dog’ 全部替換成 ’cat’
>>> s=’ I have a dog , you have a dog , he have a dog ‘
>>> re.sub( r’dog’ , ‘cat’ , s )
' I have a cat , you have a cat , he have a cat '
如果我們只想替換前面兩個,則
>>> re.sub( r’dog’ , ‘cat’ , s , 2 )
' I have a cat , you have a cat , he have a dog '
或者我們想知道發生了多少次替換,則可以使用 subn
>>> re.subn( r’dog’ , ‘cat’ , s )
(' I have a cat , you have a cat , he have a cat ', 3)
split( rule , target [,maxsplit] )
切片函式,使用指定的正則規則在目標字串中查找匹配的字串,用它們作為分界,把字串切片,
第一個引數是正則規則,第二個引數是目標字串,第三個引數是最多切片次數
回傳一個被切完的子字串的串列
這個函式和 str 物件提供的 split 函式很相似,舉個例子,我們想把上例中的字串被 ’,’ 分割開,同時要去掉逗號前后的空格
>>> s=’ I have a dog , you have a dog , he have a dog ‘
>>> re.split( ‘/s*,/s*’ , s )
[' I have a dog', 'you have a dog', 'he have a dog ']
結果很好,如果使用 str 物件的 split 函式,則由于我們不知道 ’,’ 兩邊會有多少個空格,而不得不對結果再進行一次處理,
escape( string )
這是個功能比較古怪的函式,它的作用是將字串中的 non-alphanumerics 字符(我已不知道該怎么翻譯比較好了)用反義字符的形式顯示出來,有時候你可能希望在正則式中匹配一個字串,不過里面含有很多 re 使用的符號,你要一個一個的修改寫法實在有點麻煩,你可以使用這個函式 ,
例 在目標字串 s 中匹配 ’(*+?)’ 這個子字串
>>> s= ‘111 222 (*+?) 333’
>>> rule= re.escape( r’(*+?)’ )
>>> print rule
/(/*/+/?/)
>>> re.findall( rule , s )
['(*+?)']
3 更深入的了解 re 的組與物件
前面對 Python 正則式的組進行了一些簡單的介紹,由于還沒有介紹到 match 物件,而組又是和 match 物件密切相關的,所以必須將它們結合起來介紹才能充分地說明它們的用途,
不過再詳細介紹它們之前,我覺得有必要先介紹一下將規則編譯后的生成的 patter 物件
3.1 編譯后的 Pattern 物件
將一個正則式,使用 compile 函式編譯,不僅是為了提高匹配的速度,同時還能使用一些附加的功能,編譯后的結果生成一個 Pattern 物件,這個物件里面有很多函式,他們看起來和 re 模塊的函式非常象,它同樣有 findall , match , search ,finditer , sub , subn , split 這些函式,只不過它們的引數有些小小的不同,一般說來, re 模塊函式的第一個引數,即正則規則不再需要了,應為規則就包含在 Pattern 物件中了,編譯選項也不再需要了,因為已經被編譯過了,因此 re 模塊中函式的這兩個引數的位置,就被后面的引數取代了,
findall , match , search 和 finditer 這幾個函式的引數是一樣的,除了少了規則和選項兩個引數外,它們又加入了另外兩個引數,它們是:查找開始位置和查找結束位置,也就是說,現在你可以指定查找的區間,除去你不感興趣的區間,它們現在的引數形式是:
findall ( targetString [, startPos [,endPos] ] )
finditer ( targetString [, startPos [,endPos] ] )
match ( targetString [, startPos [,endPos] ] )
search ( targetString [, startPos [,endPos] ] )
這些函式的使用和 re 模塊的同名函式使用完全一樣,所以就不多介紹了,
除了和 re 模塊的函式同樣的函式外, Pattern 物件還多了些東西,它們是:
flags 查詢編譯時的選項
pattern 查詢編譯時的規則
groupindex 規則里的組
這幾個不是函式,而是一個值,它們提供你一些規則的資訊,比如下面這個例子
>>> p=re.compile( r'(?P<word>/b[a-z]+/b)|(?P<num>/b/d+/b)|(?P<id>/b[a-z_]+/w*/b)' , re.I )
>>> p.flags
2
>>> p.pattern
'(?P<word>//b[a-z]+//b)|(?P<num>//b//d+//b)|(?P<id>//b[a-z_]+//w*//b)'
>>> p.groupindex
{'num': 2, 'word': 1, 'id': 3}
我們來分析一下這個例子:這個正則式是匹配單詞、或數字、或一個由字母或 ’_’ 開頭,后面接字母或數字的一個 ID ,我們給這三種情況的規則都包入了一個命名組,分別命名為 ’word’ , ‘num’ 和 ‘id’ ,我們規定大小寫不敏感,所以使用了編譯選項 ‘I’ ,
編譯以后回傳的物件為 p ,通過 p.flag 我們可以查看編譯時的選項,不過它顯示的不是 ’I’ ,而是一個數值 2 ,其實 re.I 是一個整數, 2 就是它的值,我們可以查看一下:
>>> re.I
2
>>> re.L
4
>>> re.M
8
…
每個選項都是一個數值,
通過 p.pattern 可以查看被編譯的規則是什么,使用 print 的話會更好看一些
>>> print p.pattern
(?P<word>/b[a-z]+/b)|(?P<num>/b/d+/b)|(?P<id>/b[a-z_]+/w*/b)
看,和我們輸入的一樣,
接下來的 p.groupindex 則是一個字典,它包含了規則中的所有命名組,字典的 key 是名字, values 是組的序號,由于字典是以名字作為 key ,所以一個無命名的組不會出現在這里,
3.2 組與 Match 物件
組與 Match 物件是 Python 正則式的重點,只有掌握了組和 Match 物件的使用,才算是真正學會了 Python 正則式,
3.2.1 組的名字與序號
正則式中的每個組都有一個序號,它是按定義時從左到右的順序從 1 開始編號的,其實, re 的正則式還有一個 0 號組,它就是整個正則式本身,
我們來看個例子
>>> p=re.compile( r’(?P<name>[a-z]+)/s+(?P<age>/d+)/s+(?P<tel>/d+).*’ , re.I )
>>> p.groupindex
{'age': 2, 'tel': 3, 'name': 1}
>>> s=’Tom 24 88888888 <=’
>>> m=p.search(s)
>>> m.groups() # 看看匹配的各組的情況
('Tom', '24', '8888888')
>>> m.group(‘name’) # 使用組名獲取匹配的字串
‘Tom’
>>> m.group( 1 ) # 使用組序號獲取匹配的字串,同使用組名的效果一樣
>>> m.group(0) # 0 組里面是什么呢?
'Tom 24 88888888 <='
原來 0 組就是整個正則式 , 包括沒有被包圍到組里面的內容,當獲取 0 組的時候,你可以不寫這個引數, m.group(0) 和 m.group() 的效果是一樣的:
>>> m.group()
'Tom 24 88888888 <='
接下來看看更多的 Match 物件的方法,看看我們能做些什么,
3.2.2 Match 物件的方法
group([index|id]) 獲取匹配的組,預設回傳組 0, 也就是全部值
groups() 回傳全部的組
groupdict() 回傳以組名為 key ,匹配的內容為 values 的字典
接上例:
>>> m.groupindex()
{'age': '24', 'tel': '88888888', 'name': 'Tom'}
start( [group] ) 獲取匹配的組的開始位置
end( [group] ) 獲取匹配的組的結束位置
span( [group] ) 獲取匹配的組的(開始,結束)位置
expand( template ) 根據一個模版用找到的內容替換模版里的相應位置
這個功能比較有趣,它根據一個模版來用匹配到的內容替換模版中的相應位置,組成一個新的字串回傳,它使用 /g<index|name> 或 /index 來指示一個組,
接上例
>>> m.expand(r'name is /g<1> , age is /g<age> , tel is /3')
'name is Tom , age is 24 , tel is 88888888'
除了以上這些函式外, Match 物件還有些屬性
pos 搜索開始的位置引數
endpos 搜索結束的位置引數
這兩個是使用 findall 或 match 等函式時,傳入的引數,在上面這個例子里,我們沒有指定開始和結束位置,那么預設的開始位置就是 0, 結束位置就是最后,
>>> m.pos
0
>>> m.endpos
19
lastindex 最后匹配的組的序號
>>> m.lastindex
3
lastgroup 最后匹配的組名
>>> m.lastgroup
'tel'
re 產生這個匹配的 Pattern 物件,可以認為是個逆參考
>>> m.re.pattern
'(?P<name>[a-z]+)//s+(?P<age>//d+)//s+(?P<tel>//d+).*'
得到了產生這個匹配的規則
string 匹配的目標字串
>>> m.string
'Tom 24 88888888 <='
校驗數字的運算式
1. 數字:^[0-9]*$
2. n位的數字:^\d{n}$
3. 至少n位的數字:^\d{n,}$
4. m-n位的數字:^\d{m,n}$
5. 零和非零開頭的數字:^(0|[1-9][0-9]*)$
6. 非零開頭的最多帶兩位小數的數字:^([1-9][0-9]*)+(.[0-9]{1,2})?$
7. 帶1-2位小數的正數或負數:^(\-)?\d+(\.\d{1,2})?$
8. 正數、負數、和小數:^(\-|\+)?\d+(\.\d+)?$
9. 有兩位小數的正實數:^[0-9]+(.[0-9]{2})?$
10. 有1~3位小數的正實數:^[0-9]+(.[0-9]{1,3})?$
11. 非零的正整數:^[1-9]\d*$ 或 ^([1-9][0-9]*){1,3}$ 或 ^\+?[1-9][0-9]*$
12. 非零的負整數:^\-[1-9][]0-9"*$ 或 ^-[1-9]\d*$
13. 非負整數:^\d+$ 或 ^[1-9]\d*|0$
14. 非正整數:^-[1-9]\d*|0$ 或 ^((-\d+)|(0+))$
15. 非負浮點數:^\d+(\.\d+)?$ 或 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0$
16. 非正浮點數:^((-\d+(\.\d+)?)|(0+(\.0+)?))$ 或 ^(-([1-9]\d*\.\d*|0\.\d*[1-9]\d*))|0?\.0+|0$
17. 正浮點數:^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$ 或 ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
18. 負浮點數:^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$ 或 ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
19. 浮點數:^(-?\d+)(\.\d+)?$ 或 ^-?([1-9]\d*\.\d*|0\.\d*[1-9]\d*|0?\.0+|0)$
校驗字符的運算式
1. 漢字:^[\u4e00-\u9fa5]{0,}$
2. 英文和數字:^[A-Za-z0-9]+$ 或 ^[A-Za-z0-9]{4,40}$
3. 長度為3-20的所有字符:^.{3,20}$
4. 由26個英文字母組成的字串:^[A-Za-z]+$
5. 由26個大寫英文字母組成的字串:^[A-Z]+$
6. 由26個小寫英文字母組成的字串:^[a-z]+$
7. 由數字和26個英文字母組成的字串:^[A-Za-z0-9]+$
8. 由數字、26個英文字母或者下劃線組成的字串:^\w+$ 或 ^\w{3,20}$
9. 中文、英文、數字包括下劃線:^[\u4E00-\u9FA5A-Za-z0-9_]+$
10. 中文、英文、數字但不包括下劃線等符號:^[\u4E00-\u9FA5A-Za-z0-9]+$ 或 ^[\u4E00-\u9FA5A-Za-z0-9]{2,20}$
11. 可以輸入含有^%&',;=?$\"等字符:[^%&',;=?$\x22]+ 12 禁止輸入含有~的字符:[^~\x22]+
特殊需求運算式
1. Email地址:^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
2. 域名:[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
3. InternetURL:[a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
4. 手機號碼:^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
5. 電話號碼("XXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"和"XXXXXXXX):^(\(\d{3,4}-)|\d{3.4}-)?\d{7,8}$
6. 國內電話號碼(0511-4405222、021-87888822):\d{3}-\d{8}|\d{4}-\d{7}
7. 身份證號(15位、18位數字):^\d{15}|\d{18}$
8. 短身份證號碼(數字、字母x結尾):^([0-9]){7,18}(x|X)?$ 或 ^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$
9. 帳號是否合法(字母開頭,允許5-16位元組,允許字母數字下劃線):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
10. 密碼(以字母開頭,長度在6~18之間,只能包含字母、數字和下劃線):^[a-zA-Z]\w{5,17}$
11. 強密碼(必須包含大小寫字母和數字的組合,不能使用特殊字符,長度在8-10之間):^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$
12. 日期格式:^\d{4}-\d{1,2}-\d{1,2}
13. 一年的12個月(01~09和1~12):^(0?[1-9]|1[0-2])$
14. 一個月的31天(01~09和1~31):^((0?[1-9])|((1|2)[0-9])|30|31)$
15. 錢的輸入格式:
16. 1.有四種錢的表示形式我們可以接受:"10000.00" 和 "10,000.00", 和沒有 "分" 的 "10000" 和 "10,000":^[1-9][0-9]*$
17. 2.這表示任意一個不以0開頭的數字,但是,這也意味著一個字符"0"不通過,所以我們采用下面的形式:^(0|[1-9][0-9]*)$
18. 3.一個0或者一個不以0開頭的數字.我們還可以允許開頭有一個負號:^(0|-?[1-9][0-9]*)$
19. 4.這表示一個0或者一個可能為負的開頭不為0的數字.讓用戶以0開頭好了.把負號的也去掉,因為錢總不能是負的吧.下面我們要加的是說明可能的小數部分:^[0-9]+(.[0-9]+)?$
20. 5.必須說明的是,小數點后面至少應該有1位數,所以"10."是不通過的,但是 "10" 和 "10.2" 是通過的:^[0-9]+(.[0-9]{2})?$
21. 6.這樣我們規定小數點后面必須有兩位,如果你認為太苛刻了,可以這樣:^[0-9]+(.[0-9]{1,2})?$
22. 7.這樣就允許用戶只寫一位小數.下面我們該考慮數字中的逗號了,我們可以這樣:^[0-9]{1,3}(,[0-9]{3})*(.[0-9]{1,2})?$
23 8.1到3個數字,后面跟著任意個 逗號+3個數字,逗號成為可選,而不是必須:^([0-9]+|[0-9]{1,3}(,[0-9]{3})*)(.[0-9]{1,2})?$
24. 備注:這就是最終結果了,別忘了"+"可以用"*"替代如果你覺得空字串也可以接受的話(奇怪,為什么?)最后,別忘了在用函式時去掉去掉那個反斜杠,一般的錯誤都在這里
25. xml檔案:^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
26. 中文字符的正則運算式:[\u4e00-\u9fa5]
27. 雙位元組字符:[^\x00-\xff] (包括漢字在內,可以用來計算字串的長度(一個雙位元組字符長度計2,ASCII字符計1))
28. 空白行的正則運算式:\n\s*\r (可以用來洗掉空白行)
29. HTML標記的正則運算式:<(\S*?)[^>]*>.*?</\1>|<.*? /> (網上流傳的版本太糟糕,上面這個也僅僅能部分,對于復雜的嵌套標記依舊無能為力)
30. 首尾空白字符的正則運算式:^\s*|\s*$或(^\s*)|(\s*$) (可以用來洗掉行首行尾的空白字符(包括空格、制表符、換頁符等等),非常有用的運算式)
31. 騰訊QQ號:[1-9][0-9]{4,} (騰訊QQ號從10000開始)
32. 中國郵政編碼:[1-9]\d{5}(?!\d) (中國郵政編碼為6位數字)
33. IP地址:\d+\.\d+\.\d+\.\d+ (提取IP地址時有用)
34. IP地址:((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/205051.html
標籤:Python
