compile(), search() & findall()
#!python3 import re regex = re.compile(r'\d{4}-?\d{6}') # r 表示使用 Raw string,即不 escape 反斜線 s = '手機號碼有兩種,第一種是 0912-345678,第二種是 0912345678。' # search 只會找到第一個符合的 mo = regex.search(s) if mo != None: print(mo.group()) # 0912-345678 # findall 會找到全部符合的 mo = regex.findall(s) for mo in mobile_regex.findall(s): print(mo) # 0912-345678 # 0912345678Raw string 請參考 Java 腦袋學 Python 字串,如果不用 Raw string,那所有反斜線都要改用兩個反斜線,這樣變得很麻煩而且讓原本就難以閱讀的 RE 語法變得更加外星語。
快速複習 RE
- \d 表示 0 到 9 的數字,也可以用 [0-9] 或者 (0|1|2|3|4|5|6|7|8|9) 表示
- \D 表示 0 到 9 以外的任何字元,包括空白,也可以用 [^0-9] 表示
- \w 表示字母、數字與底線,可以當作單字,也可以用 [a-zA-Z0-9_] 表示
- \W 表示 \w 以外的任何字元,包括空白,也可以用 [^a-zA-Z0-9_] 表示
- \s 表示空白、\t 與 \n,可以當作空白字元
- \S 表示 \s 以外的任何字元
- 自訂字元用中括號 [] 包起來,也可以使用 - 連字號指定字母與數字的範圍
- 在中括號裡可以直接使用特殊字元,不用 \ Escape
- 在中括號裡第一個字元使用 ^ 表示指定相反的字元集合
- ^ 開頭與 $ 結尾
- . 表示換行字元之外的任何字元,但對應的只有一個字元
- .* 表示換行字元以外的所有字元,且為貪婪模式
- .*? 表示換行字元以外的所有字元,但為非貪婪模式
- 非貪婪模式可以用在 {S,E}?、*? 與 +?
- 分組用 ( 與 )
- 或 |
- 出現零次或一次 ?
- 出現零次或 N 次 *
- 出現一次以上 +
- 出現指定次數 {N}、{S, E}、{S,}、{,E}
要找上述的特殊符號,就用 \ Escape。
指定字元
#!python3 import re s = 'javascript java8 spring4 python3 css' for mo in re.compile(r'\w+\d').findall(s): print(mo) # java8 # spring4 # python3
Group 分組
#!python3 import re # 用括弧分組 regex = re.compile(r'(\d{2})-(\d{8})') s = '先來找簡單的電話號碼 04-22334455' # search 只會找到第一個符合的 mo = regex.search(s) print(mo.group()) # 04-22334455 符合字串 print(mo.group(0)) # 04-22334455 符合字串 print(mo.group(1)) # 04 符合字串的第一個分組 print(mo.group(2)) # 22334455 符合字串的第二個分組 print(mo.groups()) # ('04', '22334455') tuple 組成的分組 area, number = mo.groups() print('{}-{}'.format(area, number)) # 04-22334455
或 |
#!python3 import re regex = re.compile(r'python|java') s = 'java spring python hibernate' for mo in regex.findall(s): print(mo) # java # python或也可以用 []。
#!python3 import re regex = re.compile(r'[Pp]ython') s = 'Python PYthon python' for mo in regex.findall(s): print(mo) # Python # python
指定次數 ? * + { }
#!python3 import re s = 'br bar baar baaar baaaar baaaaar' # 零次或一次 for mo in re.compile(r'ba?r').findall(s): print(mo) # br # bar # 零次或 N 次 for mo in re.compile(r'ba*r').findall(s): print(mo) # br # bar # baar # baaar # baaaar # baaaaar # 一次以上 for mo in re.compile(r'ba+r').findall(s): print(mo) # bar # baar # baaar # baaaar # baaaaar # 指定次數{N} for mo in re.compile(r'ba{3}r').findall(s): print(mo) # baaar # 指定次數 {S,E} for mo in re.compile(r'ba{1,3}r').findall(s): print(mo) # bar # baar # baaar # 指定次數 {S,} for mo in re.compile(r'ba{3,}r').findall(s): print(mo) # baaar # baaaar # baaaaar # 指定次數 {,E} for mo in re.compile(r'ba{,3}r').findall(s): print(mo) # br # bar # baar # baaar
Greedy 貪婪模式
Python 預設是貪婪模式,即在符合條件的情況下,會找出最長的字串,例如 x{3,5} 可以找到五個 x 的話,就不會在 3 個 x 就結束。可以在大括號後面加上 ?,改為非貪婪模式。
#!python3 import re s = 'f fo foo fooo foooo fooooo' for mo in re.compile(r'fo{1,3}').findall(s): print(mo) # fo # foo # fooo # fooo # fooo # 非貪婪模式 for mo in re.compile(r'fo{1,3}?').findall(s): print(mo) # fo # fo # fo # fo # fo
findall() & Group 分組
findall() 會回傳所有符合的結果,但依據 RE 內容的不同而有不同的回傳物件。當內容沒有分組時,回傳的是 list of str,但是有分組時,回傳的是 list of tuple。
#!python3 import re s = '04-22334455 0912-345678 04-23456789' for mo in re.compile(r'\d{2}-\d{8}').findall(s): print(mo) # 04-22334455 # 04-23456789 s = '04-22334455 04-23456789' for mo in re.compile(r'(\d{2})-(\d{8})').findall(s): print(mo) # ('04', '22334455') # ('04', '23456789')
額外的參數
re.DOTALL 讓 . 包括換行字元。#!python3 import re s = 'Ba\na\nar' print(re.compile(r'B.*?r').search(s)) # None print(re.compile(r'B.*?r', re.DOTALL).search(s).group()) # Ba\na\narre.IGNORECASE 或 re.I 不區分大小寫。
#!python3 import re s = 'java Java JAVA' for mo in re.compile(r'java').findall(s): print(mo) # java for mo in re.compile(r'java', re.IGNORECASE).findall(s): print(mo) # java # Java # JAVA # re.I 也行 for mo in re.compile(r'java', re.I).findall(s): print(mo) # java # Java # JAVA同時使用 re.DOTALL 與 re.IGNORECASE。
#!python3 import re s = 'Ba\na\nar' print(re.compile(r'b.*?r', re.DOTALL | re.IGNORECASE).search(s).group()) # Ba\na\nar
取代符合字串
用 sub() 取代 search() 或 findall()。#!python3 import re s = 'Python python' print(re.compile(r'y').sub(r'*', s)) # P*thon p*thon用原字串取代,使用 \N 表示分組 N,注意取代字串也是使用 Raw string 唷。
#!python3 import re s = 'Python python' print(re.compile(r'(python)', re.I).sub(r'[\1]', s)) # [Python] [python]
複雜的分組
當分組裡有分組時,如何判斷分組順序?依照左括號出現的順序。#!python3 import re s = 'Python3 python2' print(re.compile(r'((python)(\d))', re.I).sub(r'\2[\3]', s)) # Python[3] python[2]\1 表示 Python3 或 python2,\2 表示 Python 或 python,而 \3 表示 3 或 2。
分行並註解的語法
可以透過 re.VERBOSE 參數來使用多行模式('''),並可加入註解。#!python3 import re s = '1976-7-6 2017-1-7' print(re.compile(r''' ((19|20)\d\d # 年 - [0-1]?\d # 月 - [0-3]?\d) # 日 ''', re.VERBOSE).findall(s)) # [('1976-7-6', '19'), ('2017-1-7', '20')]---
---
---
沒有留言:
張貼留言