instr = "Hi\nJohn"
print(re.findall(r'\b(.|\n)', instr))
print(re.findall(r'\B.', instr))
['H', '\n', 'J']
['i', 'o', 'h', 'n']
finditer(pattern, string, flags=0)
Return an iterator over all non-overlapping matches in the
string. For each match, the iterator returns a match object.
finditer() 方法与 findall() 唯一不同在于返回的不是列表,而是一个返回 match 对象的迭代器,无匹配,则返回内容为空迭代器。
10
instr = "test1 test2"
print(re.findall(r'(?<=test).', instr))
it = re.finditer(r'(?<=test).', instr)
print(type(it))
for i in it:
print(i.group(), end=' ')
['1', '2']
<class 'callable_iterator'>
5.3. 重复字符
有了元字符,只能够匹配特定的单个字符或者位置,有了重复字符的参与,就可以生成更加复杂的模式,比如我们要匹配 8 个数字,不用写 8个 \d,而直接用 \d{8}。
重复字符又称为数量符,常用的重复字符表如下:
重复字符用在匹配字符的元字符之后,也可以用在分组后,参考 或逻辑和分组 。不可单独使用,功能作用在前一个元字符或者分组上。
以上重复模式默认为贪婪模式,总是选择尽量多匹配的分支,比如 {m, n} 就尽量选择靠近 n 的分支,可以在其后加 ‘?’ 变成非贪婪模式,比如 *?,{m,n}?。
继续借助 findall() 方法来验证以上重复字符的功能:
12
instr = "HHH"
pattern_list = [r'H*', r'H+', r'H?', r'H{2}', r'H{2,3}', r'H{2,}', r'H{,3}']
for i in pattern_list:
print('\'HHH\' -> %06s' % i, re.findall(i, instr))
'HHH' -> H* ['HHH', '']
'HHH' -> H+ ['HHH']
'HHH' -> H? ['H', 'H', 'H', '']
'HHH' -> H{2} ['HH']
'HHH' -> H{2,3} ['HHH']
'HHH' -> H{2,} ['HHH']
'HHH' -> H{,3} ['HHH', '']
这里以 ‘H*’ 简述匹配过程:
指针 p 指向 ps,尝试尽量多的匹配, 匹配到 ‘HHH’,p 指向 pe。
指针指向 pe 匹配到 0 次,也即 ‘’。
所以以上结果中含有 ‘’ 的情况均是因为在 pe 处匹配 0 次出现的。
5.3.1. 非贪婪模式
12
instr = "HHH"
pattern_list = [r'H*', r'H+', r'H?', r'H{2}', r'H{2,3}', r'H{2,}', r'H{,3}']
for i in pattern_list:
print('\'HHH\' -> %07s' % (i + r'?'), re.findall(i + r'?', instr))
'HHH' -> H*? ['', '', '', '']
'HHH' -> H+? ['H', 'H', 'H']
'HHH' -> H?? ['', '', '', '']
'HHH' -> H{2}? ['HH']
'HHH' -> H{2,3}? ['HH']
'HHH' -> H{2,}? ['HH']
'HHH' -> H{,3}? ['', '', '', '']
这里以 ‘H*’ 简述非贪婪模式匹配过程:
指针 p 指向 ps,尝试尽量少的 0 次匹配, 匹配到 ‘’,p 指向 p1。
依次采用尽量少的 0 次匹配,直至指向 pe 再次匹配到 ‘’。
所以 ‘H*’ 最后匹配的 ‘’ 个数是 H 的个数 3 加 1。
5.4. 或逻辑和分组
前文提到电话号码可以有不同的表示形式,比如区号分 3 位和 4 位,手机号总是 13 位。这就用到了或逻辑运算符 |。
它用在多个表达式式中间,表示匹配其中任何一个,比如 A | B | C,它总是先尝试匹配左边的表达式,一旦成功匹配则跳过右边的表达式。
如果 | 没有包含在 () 中,则它的范围是整个表达式。
4
instr = "color colour"
print(re.findall(r'color|colour', instr))
['color', 'colour']
使用 () 括起来的表达式,被称为分组(Group)。重复字符可以加在分组之后。
4
instr = "color colour"
print(re.findall(r'(colo)?', instr))
['colo', '', '', 'colo', '', '', '']
表达式中的每个分组从左至右被自动从 1 编号,可以在表达式中引用编号。也可以为分组指定名字。
10
instr = "0abc1"
print(re.findall(r'(?=abc).', instr))
print(re.findall(r'(?<=abc).', instr))
print(re.findall(r'(?!abc).', instr))
print(re.findall(r'(?<!abc).', instr))
['a']
['1']
['0', 'b', 'c', '1']
['0', 'a', 'b', 'c']
位置匹配可以对匹配字符进行条件选择,例如匹配三个连续的数字,且其后不能再跟数字:
4
instr = "111a1222"
print(re.findall(r'\d{3}(?!\d)', instr))
['111', '222']
re.I (re.IGNORECASE): 匹配时忽略大小写。
re.M (re.MULTILINE): 多行模式,改变’^’和’$’的行为,可以匹配任意一行的行首和行尾。
re.S (re.DOTALL): 点任意匹配模式,此时’.’ 匹配任意字符,包含 \n。
re.L (re.LOCALE): 使预定字符类 w W b B s S 取决于当前区域设定。
re.U (re.UNICODE): 使预定字符类 w W b B s S d D 取决于 unicode 定义的字符属性。
re.X (re.VERBOSE): 详细模式。此模式下正则表达式可以写成多行,忽略空白字符,并可以加入注释。
以下两个表达式是等价的:
2
instr = "Hi\nJohn"
print(re.findall(r'\b(.|\n)', instr))
print(re.findall(r'\b(.)', instr, re.S))
以下两个正则表达式也是等价的:
3
pattern = re.compile(r'''\d + # the integral part
\. # the decimal point
\d * # some fractional digits''', re.X)
pattern = re.compile(r"\d+\.\d*")
5.6. compile
compile(pattern, flags=0)
Compile a regular expression pattern, returning a pattern object.
compile() 方法将字符串形式的表达式编译成匹配模式对象。 第二个参数 flag 指定匹配模式类型,可以按位或运算符 ‘|’ 生效多种模式类型,比如re.I | re.M。另外,也可以在表达式字符串中指定模式,以下两个表达式是等价的:
1
re.compile(r'abc', re.I | re.M)
re.compile('(?im)abc')
将表达式编译成匹配模式对象后,可以重复使用该对象,无需每次都传入表达式。
6
pattern = re.compile(r'(?i)hi')
print(pattern.findall("Hi\nJohn"))
print(pattern.findall("hi\nJohn"))
['Hi']
['hi']
pattern 对象提供了几个可读属性用于查看表达式的相关信息:
pattern: 匹配模式对应的表达式字符串。
flags: 编译时用的匹配模式选项,数字形式。
groups: 表达式中分组的数量。
groupindex: 表达式中有别名的分组的别名为键、以组编号为值的字典,不含无别名的分组。
13
def print_pattern_obj(p):
print("p.pattern\t:", p.pattern)
print("p.flags\t\t:", p.flags)
print("p.groups\t:", p.groups)
print("p.groupindex\t:", p.groupindex)
p = re.compile(r'(key\d{1} *)(: *val\d{1})(?P<comma> *,)', re.I)
print_pattern_obj(p)
p.pattern : (key\d{1} *)(: *val\d{1})(?P<comma> *,)
p.flags : 34
p.groups : 3
p.groupindex : {'comma': 3}
5.7. match 和 search
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.
match() 方法从字符段头部开始判断是否匹配,一旦匹配成功,返回一个 Match 对象,否则返回 None。Match 对象保存了首次匹配的结果。
match() 方法与字符串方法 startswith() 很像,只是它使用正则表达式来判断字符头部是否满足条件。
4
m = re.match(r'\d{3}', 'a123')
print(m)
由于字符串 ‘a123’ 不是以 3 个数字开头的字符串,所以返回 None。再看一个更复杂的例子:
5
pattern = re.compile(r'(key\d{1} *)(: *val\d{1})(?P<comma> *,)')
m = pattern.match('key0 : val0, key1 : val1')
print(type(m))
<class '_sre.SRE_Match'>
search(pattern, string, flags=0)
Scan through string looking for a match to the pattern, returning
a match object, or None if no match was found.
search() 搜索整个字符串,查找匹配的字符,找到后返回一个 match 对象,否则返回 None。
5
pattern = re.compile(r'(key\d{1} *)(: *val\d{1})(?P<comma> *,)')
m = pattern.search('key: val, key0 : val0, key1 : val1')
print(m)
<_sre.SRE_Match object; span=(10, 22), match='key0 : val0,'>
示例尝试匹配 key 和 val 后有一数字的键值对,如果使用 match() 则会返回 None。
5.7.1. match 对象
match 对象保存一次匹配成功的信息,有很多方法会返回该对象,这里对它包含的属性进行介绍。使用上例中的匹配对象,将属性打印如下:
36
def print_match_obj(m):
print("m.re\t\t:", m.re)
print("m.string\t:", m.string)
print("m.pos\t\t:", m.pos)
print("m.endpos\t:", m.endpos)
print("m.lastindex\t:", m.lastindex)
print("m.lastgroup\t:", m.lastgroup)
print("m.group(1,2)\t:", m.group(1, 2))
print("m.groups()\t:", m.groups())
print("m.groupdict()\t:", m.groupdict())
print("m.start(2)\t\t:", m.start(2))
print("m.end(2)\t\t:", m.end(2))
print("m.span(2)\t\t:", m.span(2))
print("m.expand(r'\\1-\\2\\3')\t\t:", m.expand(r'\1-\2\3'))
print("m.expand(r'\\1-\\2g<3>')\t\t:", m.expand(r'\1-\2\g<3>'))
print("m.expand(r'\\1-\\2g<comma>')\t:", m.expand(r'\1-\2\g<comma>'))
print_match_obj(m)
m.re : re.compile('(key\\d{1} *)(: *val\\d{1})(?P<comma> *,)')
m.string : key0 : val0, key1 : val1
m.pos : 0
m.endpos : 24
m.lastindex : 3
m.lastgroup : comma
m.group(1,2) : ('key0 ', ': val0')
m.groups() : ('key0 ', ': val0', ',')
m.groupdict() : {'comma': ','}
m.start(2) : 5
m.end(2) : 11
m.span(2) : (5, 11)
m.expand(r'\1-\2g<comma>') : key0 -: val0,
re:匹配时使用的模式
string:要进行匹配操作的字符串
pos 和 endpos:分别表示开始和结束搜索的位置索引,pos 等于 ps,也即 0 位置;这里的 endpos 为 24,等于 ps,是字符 val1 后的位置,也即 string 的长度。
lastindex:最后一个匹配的分组编号,我们的模式中有 3 个分组,第 3 个分组用于匹配一个 ‘,’。
lastgroup:最后一个匹配的分组的别名,如果没有别名,则为 None。
group():group() 方法使用编号后者别名获取分组,参考 match.group 。
groups():groups() 方法等价于 group(1,2,…last),返回所有分组匹配的子串,是一个元组。
groupdict():groupdict() 方法返回分组中有别名的分组子串,是一个字典,例如 {‘comma’: ‘,’}。
start() 和 end() :分别返回指定分组匹配的字符串的起止字符在 string 上的位置索引值,支持编号和别名。
span(group):等价于 (start(group), end(group)),返回元组类型。
expand(template):将匹配到的分组代入 template 中然后返回,参考 match.expand 。
5.7.2. match.group
group() 方法获取一个或多个分组匹配的字符串:
不提供参数,等同于 group(0),编号 0 代表返回整个匹配的子串。
指定多个编号参数时将返回一个元组。
可以使用编号也可以使用别名;
没有匹配字符串的分组返回 None,匹配了多次的组返回最后一次匹配的子串。
10
pattern = re.compile(r'(key\d{1} *)(: *val\d{1})(?P<comma> *,)')
m = pattern.match('key0 : val0, key1 : val1')
print(m.group())
print(m.group(1, 2))
print(m.group(1, 2, 'comma'))
key0 : val0,
('key0 ', ': val0')
('key0 ', ': val0', ',')
expand(template) 方法将匹配到的分组代入 template 中然后返回。template 中支持两种方式引用分组:
可以使用 id 或 g<id> 引用分组编号,例如 1 和 g<1> 是等价的,编号从 1 开始。
g<name> 通过别名引用分组,例如 g<comma>。
以下三种方式是等价的。
10
pattern = re.compile(r'(key\d{1} *)(: *val\d{1})(?P<comma> *,)')
m = pattern.match('key0 : val0, key1 : val1')
print("m.expand(r'\\1-\\2\\3')\t\t:", m.expand(r'\1-\2\3'))
print("m.expand(r'\\1-\\2g<3>')\t\t:", m.expand(r'\1-\2\g<3>'))
print("m.expand(r'\\1-\\2g<comma>')\t:", m.expand(r'\1-\2\g<comma>'))
m.expand(r'\1-\2\3') : key0 -: val0,
m.expand(r'\1-\2g<3>') : key0 -: val0,
m.expand(r'\1-\2g<comma>') : key0 -: val0,
5.8. split
split(pattern, string, maxsplit=0, flags=0)
Split the source string by the occurrences of the pattern,
returning a list containing the resulting substrings.
split() 方法按照匹配的子串将 string 分割后返回列表。maxsplit 用于指定最大分割次数,不指定将全部分割。
4
p = re.compile(r'[, \-\*]')
print(p.split('1,2 3-4*5'))
['1', '2', '3', '4', '5']
5.9. sub 和 subn
sub(pattern, repl, string, count=0, flags=0)
Return the string obtained by replacing the leftmost
non-overlapping occurrences of the pattern in string by the
replacement repl.
sub() 方法使用 repl 替换 string 中每一个匹配的子串后返回替换后的字符串。repl 接受两种类型的参数:
当 repl 是一个字符串时,可以使用 id 或 g<id>,g<name> 引用分组,id 编号从 1 开始。
当 repl 是一个函数时,它只接受一个match对象作为参数,并返回一个用于替换的字符串(返回的字符串中不可再引用分组)。
count用于指定最多替换次数,不指定时全部替换。
11
p = re.compile(r'(\S+) (\S+)')
instr = '1970-01-01 00:00:00'
print(p.sub(r'\2 \1', instr))
def func(m):
return ' '.join([m.group(2), m.group(1)])
print(p.sub(func, instr))
00:00:00 1970-01-01
00:00:00 1970-01-01
示例用于互换年月日和时分秒位置。
subn(pattern, repl, string, count=0, flags=0)
Return a 2-tuple containing (new_string, number).
subn() 方法参数与 sub() 一致,但是它返回一个元组,元组的格式为 (sub(…), 替换次数)。 例如:
11
p = re.compile(r'(\S+) (\S+)')
instr = '1970-01-01 00:00:00'
print(p.subn(r'\2 \1', instr))
def func(m):
return ' '.join([m.group(2), m.group(1)])
print(p.subn(func, instr))
('00:00:00 1970-01-01', 1)
('00:00:00 1970-01-01', 1)
5.10. escape
escape(pattern)
Escape all the characters in pattern except ASCII letters, numbers and '_'.
escape() 方法对表达式中所有可能被解释为正则运算符的字符进行转义。如果字符串很长且包含很多特殊技字符,而又不想输入一大堆反斜杠,或者字符串来自于用户,且要用作正则表达式的一部分的时候,需要使用这个函数。
16
instr = "* and ?."
map_dict = {'?' : '*', '*' : '?'}
def replace_strs(instr, map_dict, count=0):
import re
re_dict = dict((re.escape(i), j) for i, j in map_dict.items())
print(re_dict)
pattern = re.compile('|'.join(re_dict.keys()))
return pattern.sub(lambda x: re_dict[re.escape(x.group(0))], instr, count)
print(replace_strs(instr, map_dict))
{'\\?': '*', '\\*': '?'}
? and *.
如果我们在编译 pattern 时,直接提供表达式字符串参数,可以在字符串前加 r,如果表达式存储在其他格式的变量中,就需要 escape() 处理。