- 简介
这个模块提供了与 Perl 语言类似的正则表达式匹配操作。
模式和被搜索的字符串既可以是 Unicode 字符串 (str) ,也可以是8位字节串 (bytes)。 但是,Unicode 字符串与8位字节串不能混用:也就是说,你不能用一个字节串模式去匹配 Unicode 字符串,反之亦然;类似地,当进行替换操作时,替换字符串的类型也必须与所用的模式和搜索字符串的类型一致。
正则表达式使用反斜杠字符 ('') 来表示特殊形式或是允许在使用特殊字符时不引发它们的特殊含义。 这会与 Python 的字符串字面值中对相同字符出于相同目的的用法产生冲突;例如,要匹配一个反斜杠字面值,用户可能必须写成 '\\' 来作为模式字符串,因为正则表达式必须为 \,而每个反斜杠在普通 Python 字符串字面值中又必须表示为 \。 而且还要注意,在 Python 的字符串字面值中使用的反斜杠如果有任何无效的转义序列,现在将会产生 DeprecationWarning 并将在未来改为 SyntaxError。 此行为即使对于正则表达式来说有效的转义字符同样会发生。
解决办法是对于正则表达式样式使用 Python 的原始字符串表示法;在带有 'r' 前缀的字符串字面值中,反斜杠不必做任何特殊处理。 因此 r"\n" 表示包含 '' 和 'n' 两个字符的字符串,而 "\n" 则表示只包含一个换行符的字符串。 样式在 Python 代码中通常都会使用这种原始字符串表示法来表示。
绝大部分正则表达式操作都提供为模块函数和方法,在 编译正则表达式. 这些函数是一个捷径,不需要先编译一个正则对象,但是损失了一些优化参数。
参见 第三方模块 regex , 提供了与标准库 re 模块兼容的API接口, 同时还提供了额外的功能和更全面的Unicode支持。
- 模块内容
模块定义了几个函数,常量,和一个例外。有些函数是编译后的正则表达式方法的简化版本(少了一些特性)。绝大部分重要的应用,总是会先将正则表达式编译,之后在进行操作。
re.compile(pattern, flags=0)
将正则表达式的样式编译为一个 正则表达式对象 (正则对象),可以用于匹配,通过这个对象的方法 match(), search() 以及其他如下描述。
这个表达式的行为可以通过指定 标记 的值来改变。值可以是以下任意变量,可以通过位的OR操作来结合( | 操作符)。
序列
prog = re.compile(pattern)
result = prog.match(string)
等价于
result = re.match(pattern, string)
如果需要多次使用这个正则表达式的话,使用 re.compile() 和保存这个正则对象以便复用,可以让程序更加高效。
注解
通过 re.compile() 编译后的样式,和模块级的函数会被缓存, 所以少数的正则表达式使用无需考虑编译的问题。
re.A
re.ASCII
让 \w, \W, \b, \B, \d, \D, \s 和 \S 只匹配ASCII,而不是Unicode。这只对Unicode样式有效,会被byte样式忽略。相当于前面语法中的内联标志 (?a) 。
注意,为了保持向后兼容, re.U 标记依然存在(还有他的同义 re.UNICODE 和嵌入形式 (?u) ) , 但是这些在 Python 3 是冗余的,因为默认字符串已经是Unicode了(并且Unicode匹配不允许byte出现)。
re.DEBUG
显示编译时的debug信息,没有内联标记。
re.I
re.IGNORECASE
进行忽略大小写匹配;表达式如 [A-Z] 也会匹配小写字符。Unicode匹配(比如 Ü 匹配 ü)同样有用,除非设置了 re.ASCII 标记来禁用非ASCII匹配。当前语言区域不会改变这个标记,除非设置了 re.LOCALE 标记。这个相当于内联标记 (?i) 。
注意,当设置了 IGNORECASE 标记,搜索Unicode样式 [a-z] 或 [A-Z] 的结合时,它将会匹配52个ASCII字符和4个额外的非ASCII字符: 'İ' (U+0130, 拉丁大写的 I 带个点在上面), 'ı' (U+0131, 拉丁小写没有点的 I ), 'ſ' (U+017F, 拉丁小写长 s) and 'K' (U+212A, 开尔文符号).如果使用 ASCII 标记,就只匹配 'a' 到 'z' 和 'A' 到 'Z' 。
re.L
re.LOCALE
由当前语言区域决定 \w, \W, \b, \B 和大小写敏感匹配。这个标记只能对byte样式有效。这个标记不推荐使用,因为语言区域机制很不可靠,它一次只能处理一个 "习惯”,而且只对8位字节有效。Unicode匹配在Python 3 里默认启用,并可以处理不同语言。 这个对应内联标记 (?L) 。
在 3.6 版更改: re.LOCALE 只能用于byte样式,而且不能和 re.ASCII 一起用。
在 3.7 版更改: 设置了 re.LOCALE 标记的编译正则对象不再在编译时依赖语言区域设置。语言区域设置只在匹配的时候影响其结果。
re.M
re.MULTILINE
设置以后,样式字符 '^' 匹配字符串的开始,和每一行的开始(换行符后面紧跟的符号);样式字符 '' 匹配字符串尾。对应内联标记 (?m) 。
re.S
re.DOTALL
让 '.' 特殊字符匹配任何字符,包括换行符;如果没有这个标记,'.' 就匹配 除了 换行符的其他任意字符。对应内联标记 (?s) 。
re.X
re.VERBOSE
这个标记允许你编写更具可读性更友好的正则表达式。通过分段和添加注释。空白符号会被忽略,除非在一个字符集合当中或者由反斜杠转义,或者在 *?, (?: or (?P<…> 分组之内。当一个行内有 # 不在字符集和转义序列,那么它之后的所有字符都是注释。
意思就是下面两个正则表达式等价地匹配一个十进制数字:
a = re.compile(r"""\d + # the integral part
. # the decimal point
\d * # some fractional digits""", re.X)
b = re.compile(r"\d+.\d*")
对应内联标记 (?x) 。
re.search(pattern, string, flags=0)
扫描整个 字符串 找到匹配样式的第一个位置,并返回一个相应的 匹配对象。如果没有匹配,就返回一个 None ; 注意这和找到一个零长度匹配是不同的。
re.match(pattern, string, flags=0)
如果 string 开始的0或者多个字符匹配到了正则表达式样式,就返回一个相应的 匹配对象 。 如果没有匹配,就返回 None ;注意它跟零长度匹配是不同的。
注意即便是 MULTILINE 多行模式, re.match() 也只匹配字符串的开始位置,而不匹配每行开始。
如果你想定位 string 的任何位置,使用 search() 来替代(也可参考 search() vs. match() )
re.fullmatch(pattern, string, flags=0)
如果整个 string 匹配到正则表达式样式,就返回一个相应的 匹配对象 。 否则就返回一个 None ;注意这跟零长度匹配是不同的。
re.split(pattern, string, maxsplit=0, flags=0)
用 pattern 分开 string 。 如果在 pattern 中捕获到括号,那么所有的组里的文字也会包含在列表里。如果 maxsplit 非零, 最多进行 maxsplit 次分隔, 剩下的字符全部返回到列表的最后一个元素。
re.split(r'\W+', 'Words, words, words.')
['Words', 'words', 'words', '']
re.split(r'(\W+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
re.split(r'\W+', 'Words, words, words.', 1)
['Words', 'words, words.']
re.split('[a-f]+', '0a3B9', flags=re.IGNORECASE)
['0', '3', '9']
如果分隔符里有捕获组合,并且匹配到字符串的开始,那么结果将会以一个空字符串开始。对于结尾也是一样
re.split(r'(\W+)', '...words, words...')
['', '...', 'words', ', ', 'words', '...', '']
这样的话,分隔组将会出现在结果列表中同样的位置。
样式的空匹配将分开字符串,但只在不相临的状况生效。
re.split(r'\b', 'Words, words, words.')
['', 'Words', ', ', 'words', ', ', 'words', '.']
re.split(r'\W', '...words...')
['', '', 'w', 'o', 'r', 'd', 's', '', '']
re.split(r'(\W)', '...words...')
['', '...', '', '', 'w', '', 'o', '', 'r', '', 'd', '', 's', '...', '', '', '']
re.findall(pattern, string, flags=0)
对 string 返回一个不重复的 pattern 的匹配列表, string 从左到右进行扫描,匹配按找到的顺序返回。如果样式里存在一到多个组,就返回一个组合列表;就是一个元组的列表(如果样式里有超过一个组合的话)。空匹配也会包含在结果里。
re.finditer(pattern, string, flags=0)
pattern 在 string 里所有的非重复匹配,返回为一个迭代器 iterator 保存了 匹配对象 。 string 从左到右扫描,匹配按顺序排列。空匹配也包含在结果里。
re.sub(pattern, repl, string, count=0, flags=0)
返回通过使用 repl 替换在 string 最左边非重叠出现的 pattern 而获得的字符串。 如果样式没有找到,则不加改变地返回 string。 repl 可以是字符串或函数;如为字符串,则其中任何反斜杠转义序列都会被处理。 也就是说,\n 会被转换为一个换行符,\r 会被转换为一个回车附,依此类推。 未知的 ASCII 字符转义序列保留在未来使用,会被当作错误来处理。 其他未知转义序列例如 & 会保持原样。 向后引用像是 \6 会用样式中第 6 组所匹配到的子字符串来替换。 例如:
re.sub(r'def\s+([a-zA-Z_][a-zA-Z_0-9])\s(\s):',
... r'static PyObject\npy_\1(void)\n{',
... 'def myfunc():')
'static PyObject*\npy_myfunc(void)\n{'
如果 repl 是一个函数,那它会对每个非重复的 pattern 的情况调用。这个函数只能有一个 匹配对象 参数,并返回一个替换后的字符串。比如
def dashrepl(matchobj):
... if matchobj.group(0) == '-': return ' '
... else: return '-'
re.sub('-{1,2}', dashrepl, 'pro----gram-files')
'pro--gram files'
re.sub(r'\sAND\s', ' & ', 'Baked Beans And Spam', flags=re.IGNORECASE)
'Baked Beans & Spam'
样式可以是一个字符串或者一个 样式对象 。
可选参数 count 是要替换的最大次数;count 必须是非负整数。如果忽略这个参数,或者设置为0,所有的匹配都会被替换。空匹配只在不相临连续的情况被更替,所以 sub('x*', '-', 'abxd') 返回 '-a-b--d-' 。
在字符串类型的 repl 参数里,如上所述的转义和向后引用中,\g<name> 会使用命名组合 name,(在 (?P<name>…) 语法中定义) \g<number> 会使用数字组;\g<2> 就是 \2,但它避免了二义性,如 \g<2>0。 \20 就会被解释为组20,而不是组2后面跟随一个字符 '0'。向后引用 \g<0> 把 pattern 作为一整个组进行引用。
在 3.1 版更改: 增加了可选标记参数。
在 3.5 版更改: 不匹配的组合替换为空字符串。
在 3.6 版更改: pattern 中的未知转义(由 '' 和一个 ASCII 字符组成)被视为错误。
在 3.7 版更改: repl 中的未知转义(由 '' 和一个 ASCII 字符组成)被视为错误。
在 3.7 版更改: 样式中的空匹配相邻接时会被替换。
re.subn(pattern, repl, string, count=0, flags=0)
行为与 sub() 相同,但是返回一个元组 (字符串, 替换次数).
在 3.1 版更改: 增加了可选标记参数。
在 3.5 版更改: 不匹配的组合替换为空字符串。
re.escape(pattern)
转义 pattern 中的特殊字符。如果你想对任意可能包含正则表达式元字符的文本字符串进行匹配,它就是有用的。比如
print(re.escape('http://www.python.org'))
http://www.python.org
legal_chars = string.ascii_lowercase + string.digits + "!#%&'*+-.^_`|~:]+
operators = ['+', '-', '', '/', '*']
print('|'.join(map(re.escape, sorted(operators, reverse=True))))
/|-|+|**|*
这个函数不能被用于 sub() 和 subn() 的替换字符串,只有反斜杠应该被转义。 例如:
digits_re = r'\d+'
sample = '/usr/sbin/sendmail - 0 errors, 12 warnings'
print(re.sub(digits_re, digits_re.replace('\', r'\'), sample))
/usr/sbin/sendmail - \d+ errors, \d+ warnings
re.purge()
清除正则表达式缓存。
exception re.error(msg, pattern=None, pos=None)
raise 一个例外。当传递到函数的字符串不是一个有效正则表达式的时候(比如,包含一个不匹配的括号)或者其他错误在编译时或匹配时产生。如果字符串不包含样式匹配,是不会被视为错误的。错误实例有以下附加属性:
msg
未格式化的错误消息。
pattern
正则表达式样式。
pos
编译失败的 pattern 的位置索引(可以是 None )。
lineno
对应 pos (可以是 None) 的行号。
colno
对应 pos (可以是 None) 的列号。
- 正则表达式对象(正则对象)
编译后的正则表达式对象支持以下方法和属性:
Pattern.search(string[, pos[, endpos]])
扫描整个 string 寻找第一个匹配的位置, 并返回一个相应的 匹配对象。如果没有匹配,就返回 None ;注意它和零长度匹配是不同的。
可选的第二个参数 pos 给出了字符串中开始搜索的位置索引;默认为 0,它不完全等价于字符串切片; '^' 样式字符匹配字符串真正的开头,和换行符后面的第一个字符,但不会匹配索引规定开始的位置。
可选参数 endpos 限定了字符串搜索的结束;它假定字符串长度到 endpos , 所以只有从 pos 到 endpos - 1 的字符会被匹配。如果 endpos 小于 pos,就不会有匹配产生;另外,如果 rx 是一个编译后的正则对象, rx.search(string, 0, 50) 等价于 rx.search(string[:50], 0)。
pattern = re.compile("d")
pattern.search("dog") # Match at index 0
<re.Match object; span=(0, 1), match='d'>
pattern.search("dog", 1) # No match; search doesn't include the "d"
Pattern.match(string[, pos[, endpos]])
如果 string 的 开始位置 能够找到这个正则样式的任意个匹配,就返回一个相应的 匹配对象。如果不匹配,就返回 None ;注意它与零长度匹配是不同的。
可选参数 pos 和 endpos 与 search() 含义相同。
pattern = re.compile("o")
pattern.match("dog") # No match as "o" is not at the start of "dog".
pattern.match("dog", 1) # Match as "o" is the 2nd character of "dog".
<re.Match object; span=(1, 2), match='o'>
如果你想定位匹配在 string 中的位置,使用 search() 来替代(另参考 search() vs. match())。
Pattern.fullmatch(string[, pos[, endpos]])¶
如果整个 string 匹配这个正则表达式,就返回一个相应的 匹配对象 。 否则就返回 None ; 注意跟零长度匹配是不同的。
可选参数 pos 和 endpos 与 search() 含义相同。
pattern = re.compile("o[gh]")
pattern.fullmatch("dog") # No match as "o" is not at the start of "dog".
pattern.fullmatch("ogre") # No match as not the full string matches.
pattern.fullmatch("doggie", 1, 3) # Matches within given limits.
<re.Match object; span=(1, 3), match='og'>
Pattern.split(string, maxsplit=0)¶
等价于 split() 函数,使用了编译后的样式。
Pattern.findall(string[, pos[, endpos]])
类似函数 findall() , 使用了编译后样式,但也可以接收可选参数 pos 和 endpos ,限制搜索范围,就像 search()。
Pattern.finditer(string[, pos[, endpos]])
类似函数 finiter() , 使用了编译后样式,但也可以接收可选参数 pos 和 endpos ,限制搜索范围,就像 search()。
Pattern.sub(repl, string, count=0)
等价于 sub() 函数,使用了编译后的样式。
Pattern.subn(repl, string, count=0)
等价于 subn() 函数,使用了编译后的样式。
Pattern.flags
正则匹配标记。这是可以传递给 compile() 的参数,任何 (?…) 内联标记,隐性标记比如 UNICODE 的结合。
Pattern.groups
捕获组合的数量。
Pattern.groupindex
映射由 (?P<id>) 定义的命名符号组合和数字组合的字典。如果没有符号组,那字典就是空的。
Pattern.pattern
编译对象的原始样式字符串。
在 3.7 版更改: 添加 copy.copy() 和 copy.deepcopy() 函数的支持。编译后的正则表达式对象被认为是原子性的。
- 匹配对象
匹配对象总是有一个布尔值 True。如果没有匹配的话 match() 和 search() 返回 None 所以你可以简单的用 if 语句来判断是否匹配
match = re.search(pattern, string)
if match:
process(match)
匹配对象支持以下方法和属性:
Match.expand(template)
对 template 进行反斜杠转义替换并且返回,就像 sub() 方法中一样。转义如同 \n 被转换成合适的字符,数字引用(\1, \2)和命名组合(\g<1>, \g<name>) 替换为相应组合的内容。
在 3.5 版更改: 不匹配的组合替换为空字符串。
Match.group([group1, ...])
返回一个或者多个匹配的子组。如果只有一个参数,结果就是一个字符串,如果有多个参数,结果就是一个元组(每个参数对应一个项),如果没有参数,组1默认到0(整个匹配都被返回)。 如果一个组N 参数值为 0,相应的返回值就是整个匹配字符串;如果它是一个范围 [1..99],结果就是相应的括号组字符串。如果一个组号是负数,或者大于样式中定义的组数,一个 IndexError 索引错误就 raise。如果一个组包含在样式的一部分,并被匹配多次,就返回最后一个匹配。:
m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
m.group(0) # The entire match
'Isaac Newton'
m.group(1) # The first parenthesized subgroup.
'Isaac'
m.group(2) # The second parenthesized subgroup.
'Newton'
m.group(1, 2) # Multiple arguments give us a tuple.
('Isaac', 'Newton')
如果正则表达式使用了 (?P<name>…) 语法, groupN 参数就也可能是命名组合的名字。如果一个字符串参数在样式中未定义为组合名,一个 IndexError 就 raise。
一个相对复杂的例子
m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
m.group('first_name')
'Malcolm'
m.group('last_name')
'Reynolds'
命名组合同样可以通过索引值引用
m.group(1)
'Malcolm'
m.group(2)
'Reynolds'
如果一个组匹配成功多次,就只返回最后一个匹配
m = re.match(r"(..)+", "a1b2c3") # Matches 3 times.
m.group(1) # Returns only the last match.
'c3
Match.getitem(g)
这个等价于 m.group(g)。这允许更方便的引用一个匹配
m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist")
m[0] # The entire match
'Isaac Newton'
m[1] # The first parenthesized subgroup.
'Isaac'
m[2] # The second parenthesized subgroup.
'Newton'
Match.groups(default=None)
返回一个元组,包含所有匹配的子组,在样式中出现的从1到任意多的组合。 default 参数用于不参与匹配的情况,默认为 None。
例如
m = re.match(r"(\d+).(\d+)", "24.1632")
m.groups()
('24', '1632')
如果我们使小数点可选,那么不是所有的组都会参与到匹配当中。这些组合默认会返回一个 None ,除非指定了 default 参数。
m = re.match(r"(\d+).?(\d+)?", "24")
m.groups() # Second group defaults to None.
('24', None)
m.groups('0') # Now, the second group defaults to '0'.
('24', '0')
Match.groupdict(default=None)
返回一个字典,包含了所有的 命名 子组。key就是组名。 default 参数用于不参与匹配的组合;默认为 None。 例如
m = re.match(r"(?P<first_name>\w+) (?P<last_name>\w+)", "Malcolm Reynolds")
m.groupdict()
{'first_name': 'Malcolm', 'last_name': 'Reynolds'}
Match.start([group])
Match.end([group])
返回 group 匹配到的字串的开始和结束标号。group 默认为0(意思是整个匹配的子串)。如果 group 存在,但未产生匹配,就返回 -1 。对于一个匹配对象 m, 和一个未参与匹配的组 g ,组 g (等价于 m.group(g))产生的匹配是
m.string[m.start(g):m.end(g)]
注意 m.start(group) 将会等于 m.end(group) ,如果 group 匹配一个空字符串的话。比如,在 m = re.search('b(c?)', 'cba') 之后,m.start(0) 为 1, m.end(0) 为 2, m.start(1) 和 m.end(1) 都是 2, m.start(2) raise 一个 IndexError 例外。
这个例子会从email地址中移除掉 remove_this
email = "tony@tiremove_thisger.net"
m = re.search("remove_this", email)
email[:m.start()] + email[m.end():]
'tony@tiger.net'
Match.span([group])
对于一个匹配 m , 返回一个二元组 (m.start(group), m.end(group)) 。 注意如果 group 没有在这个匹配中,就返回 (-1, -1) 。group 默认为0,就是整个匹配。
Match.pos
pos 的值,会传递给 search() 或 match() 的方法 a 正则对象 。这个是正则引擎开始在字符串搜索一个匹配的索引位置。
Match.endpos
endpos 的值,会传递给 search() 或 match() 的方法 a 正则对象 。这个是正则引擎停止在字符串搜索一个匹配的索引位置。
Match.lastindex
捕获组的最后一个匹配的整数索引值,或者 None 如果没有匹配产生的话。比如,对于字符串 'ab',表达式 (a)b, ((a)(b)), 和 ((ab)) 将得到 lastindex == 1 , 而 (a)(b) 会得到 lastindex == 2 。
Match.lastgroup
最后一个匹配的命名组名字,或者 None 如果没有产生匹配的话。
Match.re
返回产生这个实例的 正则对象 , 这个实例是由 正则对象的 match() 或 search() 方法产生的。
Match.string
传递到 match() 或 search() 的字符串。
在 3.7 版更改: 添加了对 copy.copy() 和 copy.deepcopy() 的支持。匹配对象被看作是原子性的。
- 正则表达式例子
·检查对子
在这个例子里,我们使用以下辅助函数来更好地显示匹配对象:
def displaymatch(match):
if match is None:
return None
return '<Match: %r, groups=%r>' % (match.group(), match.groups())
假设你在写一个扑克程序,一个玩家的一手牌为五个字符的串,每个字符表示一张牌,"a" 就是 A, "k" K, "q" Q, "j" J, "t" 为 10, "2" 到 "9" 表示2 到 9。
要看给定的字符串是否有效,我们可以按照以下步骤
valid = re.compile(r"^[a2-9tjqk]{5}$")
displaymatch(valid.match("akt5q")) # Valid.
"<Match: 'akt5q', groups=()>"
displaymatch(valid.match("akt5e")) # Invalid.
displaymatch(valid.match("akt")) # Invalid.
displaymatch(valid.match("727ak")) # Valid.
"<Match: '727ak', groups=()>"
最后一手牌,"727ak" ,包含了一个对子,或者两张同样数值的牌。要用正则表达式匹配它,应该使用向后引用如下
pair = re.compile(r".(.).\1")
displaymatch(pair.match("717ak")) # Pair of 7s.
"<Match: '717', groups=('7',)>"
displaymatch(pair.match("718ak")) # No pairs.
displaymatch(pair.match("354aa")) # Pair of aces.
"<Match: '354aa', groups=('a',)>"
要找出对子由什么牌组成,开发者可以按照下面的方式来使用匹配对象的 group() 方法:
pair = re.compile(r".(.).\1")
pair.match("717ak").group(1)
'7'
Error because re.match() returns None, which doesn't have a group() method:
pair.match("718ak").group(1)
Traceback (most recent call last):
File "<pyshell#23>", line 1, in <module>
re.match(r".(.).\1", "718ak").group(1)
AttributeError: 'NoneType' object has no attribute 'group'
pair.match("354aa").group(1)
'a'
- search() VS match()
Python 提供了两种不同的操作:基于 re.match() 检查字符串开头,或者 re.search() 检查字符串的任意位置(默认Perl中的行为)。
例如
re.match("c", "abcdef") # No match
re.search("c", "abcdef") # Match
<re.Match object; span=(2, 3), match='c'>
在 search() 中,可以用 '^' 作为开始来限制匹配到字符串的首位
re.match("c", "abcdef") # No match
re.search("^c", "abcdef") # No match
re.search("^a", "abcdef") # Match
<re.Match object; span=(0, 1), match='a'>
注意 MULTILINE 多行模式中函数 match() 只匹配字符串的开始,但使用 search() 和以 '^' 开始的正则表达式会匹配每行的开始
re.match('X', 'A\nB\nX', re.MULTILINE) # No match
re.search('^X', 'A\nB\nX', re.MULTILINE) # Match
<re.Match object; span=(4, 5), match='X'>
网友评论