正則表達式(稱為RE,或正則,或正則表達式模式)本質(zhì)上是嵌入在Python中的一種微小的、高度專業(yè)化的編程語言,可通過 re 模塊獲得。
簡單模式
正則表達式用于對字符串進行操作,因此我們將從最常見的任務開始:匹配字符
匹配字符:匹配不同的字符集合是正則表達式可以做的第一件事
元字符是 [ 和 ]
[abc],[a-c],[0-9],[abc作為元字符在中括號內(nèi)失效,[^5] 匹配非'5'的字符,
[5^] 將匹配 '5' 或 '^'
元字符是反斜杠,\
反斜杠來移除它們的特殊含義:\[ 或 \\,
以 '\' 開頭的特殊序列表示通常有用的預定義字符集
-
\d匹配任何十進制數(shù)字;這等價于類
[0-9]。 -
\D匹配任何非數(shù)字字符;這等價于類
[^0-9]。 -
\s匹配任何空白字符;這等價于類
[ \t\n\r\f\v]。轉(zhuǎn)義符在中括號中不失效嗎 -
\S匹配任何非空白字符;這相當于類
[^ \t\n\r\f\v]。 -
\w匹配任何字母與數(shù)字字符;這相當于類
[a-zA-Z0-9_]。 -
\W匹配任何非字母與數(shù)字字符;這相當于類
[^a-zA-Z0-9_]。
元字符是 .
它匹配除換行符之外的任何內(nèi)容,并且有一個可選模式( re.DOTALL )甚至可以匹配換行符
重復:指定正則的某些部分必須重復一定次數(shù)
元字符是 *
一個逐步的例子將使這更加明顯。 讓我們考慮表達式 a[bcd]*b。 這個正則匹配字母 'a',類 [bcd] 中的零或多個字母,最后以 'b' 結(jié)尾。 現(xiàn)在想象一下這個正則與字符串 'abcbd' 匹配。
| 步驟 | 匹配 | 說明 |
|---|---|---|
| 1 | a |
正則中的 a 匹配。 |
| 2 | abcbd |
引擎盡可能多地匹配 [bcd]* ,直到字符串結(jié)束。 |
| 3 | 失敗 | 引擎嘗試匹配 b ,但是當前位置位于字符串結(jié)束,所以匹配失敗。 |
| 4 | abcb |
回退一次,[bcd]* 少匹配一個字符。 |
| 5 | 失敗 | 再次嘗試匹配 b , 但是當前位置是最后一個字符 'd' 。 |
| 6 | abc |
再次回退,所以 [bcd]* 只匹配 bc 。 |
| 6 | abcb |
再試一次 b 。 這次當前位置的字符是 'b' ,所以它成功了。 |
元字符是 +
ca+t 將匹配 'cat' (1 個 'a'),'caaat' (3 個 'a'),但不會匹配 'ct'
重復限定符。 問號字符 ? 匹配一次或零次
home-?brew 匹配 'homebrew' 或 'home-brew'
重復限定符是 {m,n}
a/{1,3}b 將匹配 'a/b' ,'a//b' 和 'a///b' 。 它不匹配沒有斜線的 'ab',或者有四個的 'a////b', {0,} 與 * 相同, {1,} 相當于 + , {0,1} 和 ? 相同。 最好使用 * , + 或 ? ,只要因為它們更短更容易閱讀。
使用正則表達式
re 模塊提供了正則表達式引擎的接口,允許你將正則編譯為對象,然后用它們進行匹配
編譯正則表達式
正則表達式被編譯成模式對象,模式對象具有各種操作的方法,例如搜索模式匹配或執(zhí)行字符串替換
p = re.compile('ab*')
re.compile() 也接受一個可選的 flags 參數(shù),用于啟用各種特殊功能和語法變體, p = re.compile('ab*', re.IGNORECASE)
正則作為字符串傳遞給 re.compile() ,將正則放在字符串中可以使 Python 語言更簡單,但有一個缺點:反斜杠災難。解決方案 r"\n" 是一個包含 '\' 和 'n' 的雙字符字符串,而 "\n" 是一個包含換行符的單字符字符串。
應用匹配
一旦你有一個表示編譯正則表達式的對象,你用它做什么? 模式對象有幾種方法和屬性。
| 方法 / 屬性 | 目的 |
|---|---|
match() |
確定正則是否從字符串的開頭匹配。 |
search() |
掃描字符串,查找此正則匹配的任何位置。 |
findall() |
找到正則匹配的所有子字符串,并將它們作為列表返回。 |
finditer() |
找到正則匹配的所有子字符串,并將它們返回為一個 iterator。 |
>>> import re
>>> p = re.compile('[a-z]+')
>>> p
re.compile('[a-z]+')
>>> p.match("")
>>> print(p.match(""))
None
>>> m = p.match('tempo')
>>> m
<re.Match object; span=(0, 5), match='tempo'>
現(xiàn)在你可以檢查 匹配對象 以獲取有關(guān)匹配字符串的信息。 匹配對象實例也有幾個方法和屬性;最重要的是:
| 方法 / 屬性 | 目的 |
|---|---|
group() |
返回正則匹配的字符串 |
start() |
返回匹配的開始位置 |
end() |
返回匹配的結(jié)束位置 |
span() |
返回包含匹配 (start, end) 位置的元組 |
嘗試這些方法很快就會清楚它們的含義:
group() 返回正則匹配的子字符串。 start() 和 end() 返回匹配的起始和結(jié)束索引。 span() 在單個元組中返回開始和結(jié)束索引。 由于 match() 方法只檢查正則是否在字符串的開頭匹配,所以 start() 將始終為零。 但是,模式的 search() 方法會掃描字符串,因此在這種情況下匹配可能不會從零開始。:
>>>
>>> print(p.match('::: message'))
None
>>> m = p.search('::: message'); print(m)
<re.Match object; span=(4, 11), match='message'>
>>> m.group()
'message'
>>> m.span()
(4, 11)
在實際程序中,最常見的樣式是在變量中存儲 匹配對象,然后檢查它是否為 None。 這通??雌饋硐?
p = re.compile( ... )
m = p.match( 'string goes here' )
if m:
print('Match found: ', m.group())
else:
print('No match')
兩種模式方法返回模式的所有匹配項。 findall() 返回匹配字符串的列表:
>>>
>>> p = re.compile(r'\d+')
>>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
['12', '11', '10']
在這個例子中需要 r 前綴,使字面為原始字符串字面,因為普通的“加工”字符串字面中的轉(zhuǎn)義序列不能被 Python 識別為正則表達式,導致 DeprecationWarning 并最終產(chǎn)生 SyntaxError。 請參閱 反斜杠災難。
findall() 必須先創(chuàng)建整個列表才能返回結(jié)果。 finditer() 方法將一個 匹配對象 的序列返回為一個 iterator
>>>
>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...')
>>> iterator
<callable_iterator object at 0x...>
>>> for match in iterator:
... print(match.span())
...
(0, 2)
(22, 24)
(29, 31)
模塊級別函
>>> print(re.match(r'From\s+', 'Fromage amk'))
None
>>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998')
<re.Match object; span=(0, 5), match='From '>
編譯標志
這是一個可用標志表,以及每個標志的更詳細說明。
| Flag | 含義 |
|---|---|
ASCII, A
|
使幾個轉(zhuǎn)義如 \w、\b、\s 和 \d 匹配僅與具有相應特征屬性的 ASCII 字符匹配。 |
DOTALL, S
|
使 . 匹配任何字符,包括換行符。 |
IGNORECASE, I
|
進行大小寫不敏感匹配。 |
LOCALE, L
|
進行區(qū)域設置感知匹配。 |
MULTILINE, M
|
多行匹配,影響 ^ 和 $。 |
VERBOSE, X (為 '擴展') |
啟用詳細的正則,可以更清晰,更容易理解。 |
re.I | re.M 設置 I 和 M 標志
這里的正則使用 re.VERBOSE;看看閱讀有多容易?: charref = re.compile(r""" &[#] # Start of a numeric entity reference ( 0[0-7]+ # Octal form | [0-9]+ # Decimal form | x[0-9a-fA-F]+ # Hexadecimal form ) ; # Trailing semicolon """, re.VERBOSE) 如果沒有詳細設置,正則將如下所示: charref = re.compile("&#(0[0-7]+" "|[0-9]+" "|x[0-9a-fA-F]+);")
更多元字符
Crow|Servo 將匹配 'Crow' 或 'Servo',而不是 'Cro'、'w' 或 'S' 和 'ervo'
print(re.search('^From', 'From Here to Eternity')) <re.Match object; span=(0, 4), match='From'> print(re.search('^From', 'Reciting From Memory')) None `
print(re.search('}$', '{block}')) <re.Match object; span=(6, 7), match='}'> >>> print(re.search('}$', '{block} ')) None >>> print(re.search('}$', '{block}\n')) <re.Match object; span=(6, 7), match='}'> `