Python 中使用正則表達(dá)式
1. 正則表達(dá)式
1.1 簡(jiǎn)介
正則表達(dá)式 (regular expression) 描述了一種字符串匹配的模式 (pattern),例如:
- 模式 ab+c
- 可以匹配 abc、abbc、abbbc
-
- 代表前面的字符出現(xiàn) 1 次或者多次
- 模式 ab*c
- 可以匹配 ac、abc、abbc
- ? 代表前面的字符出現(xiàn) 0 次或者多次
- 模式 ab?c
- 可以匹配 ac、abc
- ? 代表前面的字符出現(xiàn) 0 次或者 1 次
它的用途包括:
- 檢查一個(gè)串是否含有某種子串
- 將匹配的子串替換
- 從某個(gè)串中取出符合某個(gè)條件的子串
1.2 普通字符
正則表達(dá)式是由普通字符(例如字符 a 到 z)以及特殊字符(稱(chēng)為"元字符")組成的文字模式。模式描述在搜索文本時(shí)要匹配的一個(gè)或多個(gè)字符串。正則表達(dá)式作為一個(gè)模板,將某個(gè)字符模式與所搜索的字符串進(jìn)行匹配。
普通字符包括沒(méi)有顯式指定為元字符的所有可打印和不可打印字符。這包括所有大寫(xiě)和小寫(xiě)字母、所有數(shù)字、所有標(biāo)點(diǎn)符號(hào)和一些其他符號(hào)。
1.3 特殊字符
特殊字符是一些有特殊含義的字符,例如的 ab*c 中的 *,* 之前的字符是 b,* 表示匹配 0 個(gè)或者多個(gè) 字符 b。下表列出了正則表達(dá)式中的特殊字符:
特殊字符 | 描述 |
---|---|
\t | 制表符 |
\f | 換頁(yè)符 |
\n | 換行符 |
\r | 回車(chē)符 |
\s | 匹配任意空白字符,等價(jià)于 [\t\n\r\f] |
\S | 匹配任意非空字符 |
\d | 匹配任意數(shù)字,等價(jià)于 [0-9] |
\D | 匹配任意非數(shù)字 |
^ | 匹配字符串的開(kāi)頭 |
$ | 匹配字符串的末尾 |
. | 匹配任意字符 |
\b | 匹配一個(gè)單詞邊界,在單詞的開(kāi)頭或者末尾匹配xcd ef’ |
\B | 匹配非單詞邊界 |
[…] | 用來(lái)表示一組字符 |
[^…] | 不在[]中的字符 |
re* | 匹配 0 個(gè)或多個(gè)正則表達(dá)式 |
re+ | 匹配 1 個(gè)或多個(gè)正則表達(dá)式 |
re? | 匹配 0 個(gè)或 1 個(gè)正則表達(dá)式 |
re{n} | 匹配 n 個(gè)正則表達(dá)式 |
re{n,m} | 匹配 n 到 m 個(gè)正則表達(dá)式 |
a | b | 匹配 a或 b |
(re) | 對(duì)正則表達(dá)式分組并記住匹配的文本 |
2. 模塊 re
2.1 簡(jiǎn)介
Python 提供了 re 模塊,提供正則表達(dá)式的模式匹配功能。在 re 模塊中定義了如下常用函數(shù):
函數(shù) | 功能 |
---|---|
re.match(pattern, string, flags) | 從字符串 string 的起始位置,查找符合模式 pattern 的子串 |
re.search(pattern, string, flags) | 從字符串 string 的任意位置,查找符合模式 pattern 的子串 |
re.split(pattern, string) | 根據(jù)分隔符 pattern 將字符串 string 分割 |
re.sub(pattern, replace, string) | 將字符串中匹配模式 patter 的子串替換字符串 replace |
2.2 正則表達(dá)式修飾符
正則表達(dá)式可以包含一些可選修飾符來(lái)控制匹配的模式。修飾符被指定為一個(gè)可選的標(biāo)志,多個(gè)標(biāo)志可以通過(guò)按位 OR(|) 它們來(lái)指定,如 re.I | re.M 被設(shè)置成 I 和 M 標(biāo)志。下表列舉了常用的正則表達(dá)式修飾符:
修飾符 | 描述 |
---|---|
re.I | 使匹配對(duì)大小寫(xiě)不敏感 |
re.M | 多行匹配,影響 ^ 和 $ |
2.3 re.MatchObject
re.MatchObject 表示模式匹配的結(jié)果,該對(duì)象包含 3 個(gè)成員方法:
- start() 返回匹配開(kāi)始的位置
- end() 返回匹配結(jié)束的位置
- span() 返回一個(gè)元組包含匹配 (開(kāi)始,結(jié)束) 的位置
2.4 re.RegexObject
re.RegexObject 表示正則表示對(duì)象,該對(duì)象包含 2 個(gè)成員方法:
- match(string) | 從字符串 string 的起始位置,查找符合模式 pattern 的子串
- serach(string) | 從字符串 string 的任意位置,查找符合模式 pattern 的子串
3. 在字符串查找與模式匹配的字符串
3.1 從字符串的起始位置進(jìn)行匹配
函數(shù) re.match(pattern, string, flags = 0) 用于在字符串查找與模式匹配的字符串:
- 從字符串 string 的起始位置,查找符合模式 pattern 的子串
- 如果匹配成功,則返回一個(gè) re.MatchObject 對(duì)象
- 如果匹配失敗,則返回 None
- 參數(shù) flags,用于控制正則表達(dá)式的匹配方式,如:是否區(qū)分大小寫(xiě),多行匹配等
函數(shù)的使用示例如下:
>>> import re
>>> matchObject = re.match('w+', 'idcbgp.cn')
>>> matchObject.group()
'www'
>>> matchObject.span()
(0, 3)
- 在第 1 行,導(dǎo)入模塊 re
- 在第 2 行,在字符串 ‘idcbgp.cn’ 中查找模式 ‘w+’
- 該模式匹配連續(xù)的小寫(xiě)字符 W
- 如果找到模式匹配的子字符串,則返回一個(gè)匹配對(duì)象 matchObject
- 在第 3 行,匹配對(duì)象 matchObject.group() 方法返回匹配的字符串
- 在第 5 行,匹配對(duì)象 matchObject.span() 方法返回一個(gè)元組
- 元組的第 0 項(xiàng),匹配的字符串在原始字符串中的起始位置
- 元組的第 1 項(xiàng),匹配的字符串在原始字符串中的結(jié)束位置
>>> import re
>>> matchObject = re.match('W+', 'idcbgp.cn')
>>> matchObject is None
True
- 在第 1 行,導(dǎo)入模塊 re
- 在第 2 行,在字符串 ‘idcbgp.cn’ 中查找模式 ‘W+’
- 該模式匹配連續(xù)的大寫(xiě)字符 W
- 如果找不到模式匹配的子字符串,則返回一個(gè) None
>>> import re
>>> matchObject = re.match('o+', 'idcbgp.cn')
>>> matchObject is None
True
- 在第 1 行,導(dǎo)入模塊 re
- 在第 2 行,在字符串 ‘idcbgp.cn’ 中查找模式 ‘o+’
- 該模式匹配連續(xù)的小寫(xiě)字符 o
- 如果找不到模式匹配的子字符串,則返回一個(gè) None
- 在第 4 行,顯示匹配結(jié)果是 None
- 盡管字符 string 的中間含有字符串 oo
- 函數(shù) re.match 從字符串 string 的開(kāi)始位置進(jìn)行匹配
- 因此找不到匹配
3.2 從字符串的任意位置進(jìn)行匹配
函數(shù) re.search(pattern, string, flags = 0) 用于在字符串查找與模式匹配的字符串:
- 從字符串 string 的任意位置,查找符合模式 pattern 的子串
- 如果匹配成功,則返回一個(gè) re.MatchObject 對(duì)象
- 如果匹配失敗,則返回 None
- 參數(shù) flags,用于控制正則表達(dá)式的匹配方式,如:是否區(qū)分大小寫(xiě),多行匹配等
>>> import re
>>> matchObject = re.search('o+', 'idcbgp.cn')
>>> matchObject.group()
'oo'
>>> matchObject.span()
(6, 8)
- 在第 1 行,導(dǎo)入模塊 re
- 在第 2 行,在字符串 ‘idcbgp.cn’ 中查找模式 ‘o+’
- 該模式匹配連續(xù)的小寫(xiě)字符 o
- 如果找到模式匹配的子字符串,則返回一個(gè)匹配對(duì)象 matchObject
- 在第 3 行,匹配對(duì)象 matchObject.group() 方法返回匹配的字符串
- 在第 5 行,匹配對(duì)象 matchObject.span() 方法返回一個(gè)元組
- 元組的第 0 項(xiàng),匹配的字符串在原始字符串中的起始位置
- 元組的第 1 項(xiàng),匹配的字符串在原始字符串中的結(jié)束位置
3.3 在字符串的首部進(jìn)行匹配
>>> import re
>>> re.search('^a', 'abc')
<_sre.SRE_Match object; span=(0, 1), match='a'>
>>> re.search('^a', 'xabc')
>>>
- 在第 2 行,^a 表示從字符串 ‘a(chǎn)bc’ 的首部進(jìn)行匹配
- 在第 3 行,顯示匹配結(jié)果不為 None
- 在第 4 行,^a 表示從字符串 ‘xabc’ 的首部進(jìn)行匹配
- 在第 5 行,顯示匹配結(jié)果為 None
3.4 在字符串的尾部進(jìn)行匹配
>>> import re
>>> re.search('c$', 'abc')
<_sre.SRE_Match object; span=(2, 3), match='c'>
>>> re.search('c$', 'abcx')
>>>
- 在第 2 行,c$ 表示從字符串 ‘a(chǎn)bc’ 的尾部進(jìn)行匹配
- 在第 3 行,顯示匹配結(jié)果不為 None
- 在第 4 行,c$ 表示從字符串 ‘xabc’ 的尾部進(jìn)行匹配
- 在第 5 行,顯示匹配結(jié)果為 None
3.5 匹配一串?dāng)?shù)字
>>> import re
>>> re.search('\d+', 'abc123xyz')
<_sre.SRE_Match object; span=(3, 6), match='123'>
>>> re.search('\d{3}', 'abc123xyz')
<_sre.SRE_Match object; span=(3, 6), match='123'>
>>> re.search('\d{4}', 'abc123xyz')
>>>
- 在第 2 行,\d+ 表示匹配 1 個(gè)或者多個(gè)數(shù)字
- 在第 3 行,顯示匹配結(jié)果不為 None
- 在第 4 行,\d{3} 表示匹配 3 個(gè)數(shù)字
- 在第 5 行,顯示匹配結(jié)果不為 None
- 在第 6 行,\d+ 表示匹配 4 個(gè)數(shù)字
- 在第 7 行,顯示匹配結(jié)果為 None
3.6 判斷是否是合法的變量名
Python 的變量命名規(guī)則如下:
- 首個(gè)字符必須是字母或者字符 _
- 其余的字符可以是字符、數(shù)字或者字符 _
下面的例子使用正則表達(dá)式判斷字符串是否是一個(gè)合法的變量名稱(chēng):
import re
def isPythonId(id):
pattern = '^[a-zA-Z_][a-zA-Z0-9_]*$'
matchObject = re.search(pattern, id)
if matchObject is None:
print('%s is not Id' % id)
else:
print('%s is Id' % id)
isPythonId('abc')
isPythonId('Abc_123')
isPythonId('123')
- 在第 3 行,定義了函數(shù) isPythonId(id),判斷輸入字符串 id 是否是一個(gè)合法的 Python 變量名
- 在第 4 行,模式 pattern 定義了一個(gè)合法的 Python 變量名的模式,該模式由 4 個(gè)部分構(gòu)成
模式 | 功能 |
---|---|
^ | 匹配字符串頭部,即被匹配的字符串從原始字符串的頭部開(kāi)始 |
[a-zA-Z_] | 匹配小寫(xiě)字符、大寫(xiě)字符和字符 _ |
[a-zA-Z0-9_] | 匹配小寫(xiě)字符、大寫(xiě)字符、數(shù)字和字符 _ |
* | 將 * 之前的字符重復(fù) 0 次或者多次 |
$ | 匹配字符串尾部,即被匹配的字符串以原始字符串的尾部結(jié)尾 |
程序運(yùn)行輸出結(jié)果如下:
abc is Id
Abc_123 is Id
123 is not Id
4. 將字符串分割成多個(gè)部分
函數(shù) re.split(pattern, string) 根據(jù)分隔符 pattern 將字符串 string 分割
- 返回一個(gè)列表,該列表記錄了分割的字符串
- 參數(shù) pattern,描述了分隔符的模式
- 參數(shù) string,是被分割的字符串
>>> import re
>>> re.split('[ :]', 'www imooc:com')
['www', 'imooc', 'com']
>>> re.split(' +', 'www imooc com')
['www', 'imooc', 'com']
5. 在字符串替換與模式匹配的字符串
5.1 替換字符串
函數(shù) re.sub(pattern, replace, string, count=0, flags=0) 用于替換字符串:
- 在字符串 string 中查找與模式 pattern 匹配的子串,將其替換為字符串 replace
- 參數(shù) replace,是被替換的字符串,也可為一個(gè)函數(shù)
- 參數(shù) count,模式匹配后替換的最大次數(shù),默認(rèn) 0 表示替換所有的匹配
- 參數(shù) flags,用于控制正則表達(dá)式的匹配方式,如:是否區(qū)分大小寫(xiě),多行匹配等
import re
line = 'number = 123 # this is comment'
result = re.sub('\d+', 'NUMBER', line)
print(result)
result = re.sub('#.*$', '', line)
print(result)
- 在第 4 行,搜索字符串 line,將與模式 ‘\d+’ 匹配的字符串替換為 ‘NUMBER’
- 模式 ‘\d+’ 匹配多個(gè)連續(xù)的數(shù)字
- 在第 6 行,搜索字符串 line,將與模式 ‘#.*$’ 匹配的字符串替換為 ‘’
- 替換為空字符串,即刪除匹配的字符串
- 模式 ‘#.*$’ 匹配從字符 # 開(kāi)始到到結(jié)尾的字符串,即行的注釋
程序輸出結(jié)果:
number = NUMBER # this is comment
number = 123
- 在第 1 行,將數(shù)字 123 替換為 NUMBER
- 在第 1 行,將以 # 開(kāi)始的注釋刪除
5.2 使用函數(shù)替換字符串
參數(shù) replace 用于替換匹配的字符串,它可以是一個(gè)函數(shù)。下面的例子將匹配的數(shù)字乘以 2:
import re
def replace(matchedObject):
text = matchedObject.group()
number = int(text)
return str(number * 2)
line = 'number = 123'
result = re.sub('\d+', replace, line)
print(result)
- 在第 8 行,定義了原始字符串 line
- 在第 9 行,使用 re.sub 搜索符合模式 ‘\d+’ 的字符串,使用函數(shù) replace 進(jìn)行替換
- re.sub 找到符合模式 ‘\d+’ 的字符串時(shí),將匹配結(jié)果傳遞給 replace
- 函數(shù) replace 根據(jù)匹配結(jié)果,返回一個(gè)字符串
- re.sub 將符合模式的字符串替換為函數(shù) replace 的返回結(jié)果
- 在第 3 行,定義了函數(shù) replace
- 在第 4 行,matchedObject.group() 返回匹配模式的字符串
- 在第 5 行,將匹配的字符串轉(zhuǎn)換為整數(shù)
- 在第 6 行,將整數(shù)乘以 2 后轉(zhuǎn)換為字符串,并返回
程序輸出結(jié)果如下:
number = 246
6. 分組與捕獲
6.1 簡(jiǎn)介
正則表達(dá)式中的分組又稱(chēng)為子表達(dá)式,就是把一個(gè)正則表達(dá)式的全部或部分當(dāng)做一個(gè)整體進(jìn)行處理,分成一個(gè)或多個(gè)組。其中分組是使用 () 表示的。進(jìn)行分組之后 ()里面的內(nèi)容就會(huì)被當(dāng)成一個(gè)整體來(lái)處理。
把正則表達(dá)式中子表達(dá)式匹配的內(nèi)容,保存到內(nèi)存中以數(shù)字編號(hào)或顯式命名的組里,方便后面引用,被稱(chēng)為捕獲。
6.2 分析 URL
import re
def parseUrl(url):
pattern = '(.*)://(.*)/(.*)'
matchObject = re.search(pattern, url)
all = matchObject.group(0)
protocol = matchObject.group(1)
host = matchObject.group(2)
path = matchObject.group(3)
print('group(0) =', all)
print('group(1) =', protocol)
print('group(2) =', host)
print('group(3) =', path)
print()
parseUrl('http://idcbgp.cn/wiki')
parseUrl('http://idcbgp.cn/courses')
- 在第 3 行,函數(shù) parseUrl(url) 分析 URL 的組成部分
- URL 由 3 部分構(gòu)成:協(xié)議、主機(jī)名、路徑名
- 在第 4 行,定義了匹配 URL 的模式 ‘(.)://(.)/(.*)’
- 第 1 個(gè) (.*) 匹配協(xié)議
- 第 2 個(gè) (.*) 匹配主機(jī)名
- 第 3 個(gè) (.*) 匹配路徑名
- 匹配對(duì)象 matchObject 的 group(index) 方法返回指定分組號(hào)的分組
- group(0) 為匹配整個(gè)表達(dá)式的字符串
- group(1) 為匹配的協(xié)議
- group(2) 為匹配的主機(jī)名
- group(3) 為匹配的路徑名
程序運(yùn)行輸出:
group(0) = http://idcbgp.cn/wiki
group(1) = https
group(2) = idcbgp.cn
group(3) = wiki
group(0) = http://idcbgp.cn/courses
group(1) = http
group(2) = idcbgp.cn
group(3) = courses