以下示例所使用 python 版本為: 3.7
python 提供 re 模塊,來滿足正則表達式的使用。在開始介紹 re 模塊之前,首先說明一下兩個小內(nèi)容:
- 轉(zhuǎn)義字符 \
轉(zhuǎn)義字符作用是使得字符失去原本的意思,去表示另外一個作用。例如字符 d 表示一個普通的字符 d, 加 \ 轉(zhuǎn)義后 \d 表示數(shù)字,字符 s 經(jīng)轉(zhuǎn)義后,\s 表示空白字符。
如果要匹配轉(zhuǎn)義符號 \ 本身,則需要表達為 \\ 。而在編程語言中要表達兩個轉(zhuǎn)義符號 \\,則需要對每個轉(zhuǎn)義符號進行轉(zhuǎn)義,即形式為 \\\\,需要四個轉(zhuǎn)義符號才能完成用于匹配一個轉(zhuǎn)義符號 \ 的正則表達式。
為了減弱轉(zhuǎn)義字符使用上的麻煩,能夠?qū)⑹褂谜叩淖⒁饬性谡齽t表達式的編寫上,這里推薦所有的正則表達式開頭使用 r 字符開頭,表示正則內(nèi)容作為原始字符串輸入到 re 模塊的使用中。即編程環(huán)境中 r'\\' 直接作為正則表達式使用,來完成對字符 \ 的匹配。
示例:
import re
reg = r'\\'
str = '\\' #轉(zhuǎn)義符號,str實際值為\
if re.match(reg,str):
print('match {}'.format(str))
------------------------
運行結(jié)果:
match \
- Pattern、Match對象
在re模塊中使用正則表達式完成字符串處理有兩種方式:
- 在 python 中可以直接使用 re 模塊生成 Match 對象,完成字符串處理。
示例:
import re
reg = r'abc'
str = 'abc'
mattern = re.match(reg,str)
print(mattern .group())
------------------------
運行結(jié)果:
abc
- 使用 re 模塊生成 Pattern 對象,然后使用 Pattern 對象生成 Match 對象,完成字符串處理。
示例:
import re
reg = r'abc'
str = 'abc'
pattern = re.compile(reg)
match = pattern.match(str)
print(match.group())
------------------------
運行結(jié)果:
abc
這兩種方式執(zhí)行效果基本一樣,觀察 re 模塊源代碼:
模塊: \Python37\Lib\re.py
# --------------------------------------------------------------------
# public interface
def match(pattern, string, flags=0): #第一種方式
"""Try to apply the pattern at the start of the string, returning
a Match object, or None if no match was found."""
return _compile(pattern, flags).match(string)
def compile(pattern, flags=0): #第二種方式
"Compile a regular expression pattern, returning a Pattern object."
return _compile(pattern, flags)
# --------------------------------------------------------------------
# internals
_cache = {} # ordered!
_MAXCACHE = 512
def _compile(pattern, flags):
# internal: compile pattern
if isinstance(flags, RegexFlag):
flags = flags.value
try:
return _cache[type(pattern), pattern, flags] #從緩存字典中查詢
except KeyError:
pass
if isinstance(pattern, Pattern):
if flags:
raise ValueError(
"cannot process flags argument with a compiled pattern")
return pattern
if not sre_compile.isstring(pattern):
raise TypeError("first argument must be string or compiled pattern")
p = sre_compile.compile(pattern, flags)
if not (flags & DEBUG):
if len(_cache) >= _MAXCACHE:
# Drop the oldest item
try:
del _cache[next(iter(_cache))]
except (StopIteration, RuntimeError, KeyError):
pass
_cache[type(pattern), pattern, flags] = p #添加到緩存字典中
return p
觀察源碼可知, re 模塊提供的 compile 函數(shù)和 match 函數(shù),都需要先通過 _compile(pattern, flags) 函數(shù)生成一個 Pattern 對象,區(qū)別在于 match 函數(shù)直接在生成 Pattern 對象后調(diào)用了 match 函數(shù)。
觀察 _compile(pattern, flags) 函數(shù)體可知,首先會從緩存字典中查詢是否已經(jīng)存在 Pattern 對象,存在則返回,不存在則生成該 Pattern 對象,添加到緩存字典后返回。
由此可知,直接使用 re 模塊的 match 函數(shù)來生成 Match 對象,和使用 re 模塊的 compile 函數(shù)生成 Pattern 對象,然后再利用 match 函數(shù)生成 Match 對象完成字符串處理,這兩種方式都是首先調(diào)用 _compile(pattern, flags) 函數(shù)生成一個Pattern對象,然后再使用該 Pattern 對象的 match 函數(shù)生成 Match 對象。
所以兩種方式基本相同,區(qū)別只在于當(dāng) Pattern 對象多次使用時,第一種方式會存在多次查詢緩存操作,且緩存字典容量是有限制的,超出后會刪除最先添加的 Pattern 對象,所以推薦所有的正則中都使用構(gòu)造的 Pattern 對象來完成字符串處理。
下面列出 Pattern 對象中使用到的函數(shù):
| 函數(shù)名 | 作用 |
|---|---|
| match(string, pos=0, endpos=-1) | 在指定范圍內(nèi),從指定的起始位置開始匹配,得到匹配對象則返回 |
| search(string, pos=0, endpos=-1) | 在指定范圍內(nèi),從任意位置開始匹配,得到匹配對象則返回 |
| findall(string, pos=0, endpos=-1) | 在指定范圍內(nèi),返回所有匹配結(jié)果構(gòu)成的列表 |
| finditer(string, pos=0, endpos=-1) | 在指定范圍內(nèi),返回所有匹配對象構(gòu)成的迭代器 |
| split(string, maxsplit=0) | 按照指定的分割次數(shù),返回分割得到的結(jié)果列表 |
| sub(repl, string, count=0) | 按照指定的替換規(guī)則和替換次數(shù),返回替換后的結(jié)果 |
| subn(repl, string, count=0) | 按照指定的替換規(guī)則和替換次數(shù),返回替換后的結(jié)果和替換次數(shù)構(gòu)成的元組 |
- match 函數(shù)
match 函數(shù)返回指定范圍內(nèi),從指定起始位置開始匹配到的對象。使用過程中只有三點需要注意一下:
1. 可以指定匹配的范圍,默認范圍是 0 到 len(str)
2. 匹配是從指定的起始位置開始的,也就是固定了開始位置
3. 匹配成功則返回,也就是只返回一個匹配對象
示例:
import re
reg = r'\d'
pattern = re.compile(reg)
str1 = 'a1b2c3'
match1 = pattern.match(str1)
match2 = pattern.match(str1,1)
print('match1 = {}'.format(match1))
if match1:
print('match1 matched = {}'.format(match1.group()))
print('match2 = {}'.format(match2))
if match2:
print('match2 matched = {}'.format(match2.group()))
運行結(jié)果:
match1 = None
match2 = <re.Match object; span=(1, 2), match='1'>
match2 matched = 1
- search 函數(shù)
search 函數(shù)返回指定范圍內(nèi),從任意起始位置開始匹配到的對象。使用過程中只有三點需要注意一下:
1. 可以指定匹配的范圍,默認范圍是 0 到 len(str)
2. 匹配是從范圍內(nèi)的任意起始位置開始的,也就是不固定開始位置
3. 匹配成功則返回,也就是只返回一個匹配對象
示例:
import re
reg = r'\d'
pattern = re.compile(reg)
str1 = 'a1b2c3'
match1 = pattern.search(str1)
print('match1 = {}'.format(match1))
if match1:
print('match1 matched = {}'.format(match1.group()))
運行結(jié)果:
match1 = <re.Match object; span=(1, 2), match='1'>
match1 matched = 1
比較 match 和 search 函數(shù)的使用:兩個函數(shù)都是一次匹配,得到結(jié)果則返回 (None 或 Match 對象),區(qū)別在于 match 函數(shù)從指定的起始位置開始匹配,search 函數(shù)從指定范圍內(nèi)的任意位置開始。
- findall 函數(shù)
findall 函數(shù)返回指定范圍內(nèi),所有匹配結(jié)果構(gòu)成的列表。使用過程中只有兩點需要注意一下:
1. 可以指定匹配的范圍,默認范圍是 0 到 len(str)
2. 返回的是一個列表,元素是 str 對象,而非 Match 對象
示例:
import re
reg = r'\d'
pattern = re.compile(reg)
str1 = 'a1b2c3'
match1 = pattern.findall(str1)
print('match1 = {}'.format(match1))
for element in match1:
print('element = {}'.format(element))
運行結(jié)果:
match1 = ['1', '2', '3']
element = 1
element = 2
element = 3
- finditer 函數(shù)
finditer 函數(shù)返回指定范圍內(nèi),所有匹配對象構(gòu)成的迭代器。使用過程中只有兩點需要注意一下:
1. 可以指定匹配的范圍,默認范圍是 0 到 len(str)
2. 返回的是一個迭代器,元素是 Match 對象
示例:
import re
reg = r'\d'
pattern = re.compile(reg)
str1 = 'a1b2c3'
match1 = pattern.finditer(str1)
print('match1 = {}'.format(match1))
for element in match1:
print('element = {}'.format(element.group()))
運行結(jié)果:
match1 = <callable_iterator object at 0x0000000002CA0550>
element = 1
element = 2
element = 3
比較 findall 和 finditer 函數(shù)的使用:兩個函數(shù)都是獲取所有匹配內(nèi)容,區(qū)別在于 findall 函數(shù)返回的是一個列表,元素類型是 str,finditer 函數(shù)返回的是一個迭代器,元素類型是 Match 。
- split 函數(shù)
split 函數(shù)返回根據(jù)指定分割次數(shù),分割后得到的結(jié)果列表。使用過程中只有兩點需要注意一下:
1. 可以指定分割次數(shù),默認值 0 表示全分割
2. 返回的是一個列表,元素是 str 對象
示例:
import re
reg = r'\d'
pattern = re.compile(reg)
str1 = 'a1b2c3'
match1 = pattern.split(str1)
print('match1 = {}'.format(match1))
for element in match1:
print('element = {}'.format(element))
運行結(jié)果:
match1 = ['a', 'b', 'c', '']
element = a
element = b
element = c
element =
- sub 函數(shù)
sub 函數(shù)根據(jù)指定替換規(guī)則和替換次數(shù),返回替換后內(nèi)容。使用過程中只有三點需要注意一下:
1. 可以指定替換次數(shù),默認值 0 表示全替換
2. 返回的是一個 str 對象
3. 指定的替換規(guī)則可以是 str 對象,可以是一個函數(shù),也可以是分組的引用
示例:
import re
def fun(match):
return match.group()[::-1]
reg = r'(\d)(\d)'
pattern = re.compile(reg)
str1 = 'a12b34c56'
match1 = pattern.sub('__',str1)
match2 = pattern.sub(fun,str1)
match3 = pattern.sub(r'\2\1',str1)
print('match1 = {}'.format(match1))
print('match2 = {}'.format(match2))
print('match3 = {}'.format(match3))
運行結(jié)果:
match1 = a__b__c__
match2 = a21b43c65
match3 = a21b43c65
- subn 函數(shù)
subn 函數(shù)根據(jù)指定替換規(guī)則和替換次數(shù),返回替換后內(nèi)容和替換次數(shù)構(gòu)成的元組。使用過程中只有三點需要注意一下:
1. 可以指定替換次數(shù),默認值 0 表示全替換
2. 返回的是一個 str 對象和 int 對象構(gòu)成的元組
3. 指定的替換規(guī)則可以是 str 對象,可以是一個函數(shù),也可以是分組的引用
示例:
import re
def fun(match):
return match.group()[::-1]
reg = r'(\d)(\d)'
pattern = re.compile(reg)
str1 = 'a12b34c56'
match1 = pattern.subn('__',str1,1)
match2 = pattern.subn('__',str1,2)
print('match1 = {}'.format(match1))
print('match2 = {}'.format(match2))
運行結(jié)果:
match1 = ('a__b34c56', 1)
match2 = ('a__b__c56', 2)
比較 sub 和 subn 函數(shù)的使用:兩個函數(shù)都是替換匹配內(nèi)容,區(qū)別在于 sub 函數(shù)返回的是一個 str 對象,subn 函數(shù)返回的是替換后 str 對象和替換次數(shù) int 對象構(gòu)成的元組。
下面列出 Match 對象中常到的函數(shù):
| 函數(shù)名 | 作用 |
|---|---|
| group(*args) | 返回指定的分組匹配的結(jié)果 |
| groups(default=None) | 返回所有分組匹配結(jié)果構(gòu)成的元組 |
| span(group=0) | 返回由指定分組匹配結(jié)果區(qū)間的首尾兩個下標(biāo)值構(gòu)成的元組 |
| start(group=0) | 返回指定分組匹配結(jié)果區(qū)間的首下標(biāo)值 |
| end(group=0) | 返回指定分組匹配結(jié)果區(qū)間的尾下標(biāo)值 |
- group、groups、span、start、end 函數(shù)
以上這幾個函數(shù)作為常用函數(shù),使用方式較為簡單:
1. group:返回指定分組匹配的內(nèi)容,參數(shù)為零或者默認值時,返回整個正則表達式匹配結(jié)果;參數(shù)為多個分組時,返回分組匹配結(jié)果構(gòu)成的元組
2. groups:返回分組匹配結(jié)果構(gòu)成的元組
3. span:返回指定分組匹配結(jié)果區(qū)間的首尾兩個下標(biāo)值(左閉右開)構(gòu)成的元組,默認情況下匹配整個正則表達式匹配結(jié)果的首尾兩個下標(biāo)值
4. start:返回指定分組匹配結(jié)果區(qū)間的首下標(biāo)
5. end:返回指定分組匹配結(jié)果區(qū)間的尾下標(biāo)
示例:
import re
reg = r'(\d)(\d)'
pattern = re.compile(reg)
str1 = 'a12b'
match1 = pattern.search(str1)
print('group 1 = {}'.format(match1.group(1)))
print('span 1 = {}'.format(match1.span(1)))
print('start 1 = {}'.format(match1.start(1)))
print('end 1 = {}'.format(match1.end(1)),end='\n\n')
print('group 2 = {}'.format(match1.group(2)))
print('span 2 = {}'.format(match1.span(2)))
print('start 2 = {}'.format(match1.start(2)))
print('end 2 = {}'.format(match1.end(2)),end='\n\n')
print('groups = {}'.format(match1.groups()))
print('span = {}'.format(match1.span()))
print('start = {}'.format(match1.start()))
print('end = {}'.format(match1.end()),end='\n\n')
print('group 1,2 = {}'.format(match1.group(1,2)))
運行結(jié)果:
group 1 = 1
span 1 = (1, 2)
start 1 = 1
end 1 = 2
group 2 = 2
span 2 = (2, 3)
start 2 = 2
end 2 = 3
groups = ('1', '2')
span = (1, 3)
start = 1
end = 3
group 1,2 = ('1', '2')
Pattern 用作模式處理,Match 用作分組結(jié)果提取
比較以上 Pattern 和 Match 兩種對象的使用方式,可以發(fā)現(xiàn) Pattern 對象作用如其名稱所示,是一種體現(xiàn)正則表達式規(guī)則的對象,也就是體現(xiàn)為一種文本模式。其所提供的諸多函數(shù),如: find、search、match 等等,都是拿自身的規(guī)則以不同的方式去處理目標(biāo)文本。而 Match 對象則是對分組結(jié)果的操作,Match 對象提供的函數(shù)則都是對結(jié)果以不同方式的提取,如:groups、span等,獲取匹配分組結(jié)果的各項數(shù)據(jù)。