re 正则表达式¶
有些人面临一个问题时会想:“我知道,我将使用正则表达式来解决这个问题。”这会让他们面临的问题变成了两个。 – Jamie Zawinski
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
模块 re 提供了对正则表达式的支持,在 Python 中使用正则表达式只需两个步骤,如下:
>>> import re
>>> nu = re.search(r'\d{3}-\d{3,4}-\d{4}', 'My number is 400-055-2818.')
>>> nu.group(0)
400-055-2818
常用操作符¶
操作符就是上边定义中指出的“事先定义好的一些特定字符”,它们具有特殊的含义。如果在匹配时需要使用字符原本的含义,需要用 \ 转义字符转义。
操作符 |
含义 |
匹配结果 |
|---|---|---|
. |
任何单个字符 |
|
[] |
单个字符集范围 |
[ac] 匹配 a 或 c;[a-z] 匹配 a 至 z 中的单个字符 |
[^] |
非单字符集范围 |
[^ab] 匹配非 a 或非 b 的单个字符 |
* |
零次或无限次 |
abc* 匹配 ab、abc、abcc、abccc 等 |
+ |
一次或无限次 |
abc+ 匹配 abc、abcc、abccc 等 |
? |
零次或一次 |
abc? 匹配 ab、abc |
| |
左右任意一个 |
abc|def 匹配 abc 或 def |
{m} |
m 次 |
ab{2}c 匹配 abbc |
{m,n} |
m 至 n 次 |
ab{1,4}c 匹配 abc、abbc、abbbc、abbbbc |
^ |
字符串开头 |
^abc 匹配以 abc 开头的字符串 abc… |
$ |
字符串结尾 |
abc$ 匹配以 abc 结尾的字符串 …abc |
() |
分组 |
(abc) 匹配 abc,(abc|def) 匹配 abc 或 def |
\d |
数字 |
等价于 [0-9] |
\D |
非数字 |
等价于 [^0-9] |
\w |
单词字符 |
等价于 [A-Za-z0-9_] |
\W |
非单词字符 |
等价于 [^A-Za-z0-9_] |
\s |
任意空白符 |
等价于 [ \f\n\r\t\v] 包括空格、制表符、换页符等 |
\S |
非任意空白符 |
除空格、制表符和换行符以外的任何字符 |
非贪婪匹配¶
在字符串 ‘HaHaHaHaHa’ 中,要匹配 (Ha){3,5} 会返回什么结果呢?答案是 HaHaHaHaHa。
你可能会问,为什么会是 HaHaHaHaHa,而不是更短的 HaHaHa。毕竟,’HaHaHa’ 和 ‘HaHaHaHa’ 都能够有效地匹配正则表达式 (Ha){3,5}。
Python 的正则表达式默认是“贪婪”的,这表示在有歧义的情况下,它会尽可能长的匹配字符串。那么,如何才能尽可能短的匹配字符串呢?只需要多加一个 ? 问号。
注意在查找相同字符串时,花括号的贪婪形式和非贪婪形式之间的区别:
>>> greedyHaRegex = re.compile(r'(Ha){3,5}')
>>> mo1 = greedyHaRegex.search('HaHaHaHaHa')
>>> mo1.group()
'HaHaHaHaHa'
>>> nongreedyHaRegex = re.compile(r'(Ha){3,5}?')
>>> mo2 = nongreedyHaRegex.search('HaHaHaHaHa')
>>> mo2.group()
'HaHaHa'
请注意,问号在正则表达式中可能有两种含义:声明非贪婪匹配或表示可选的分组。这两种含义是完全无关的。
最小匹配操作符组合实例:
操作符 |
说明 |
|---|---|
*? |
最小匹配 0 次或无限次 |
+? |
最小匹配 1 次或无限次 |
?? |
最小匹配 0 次或 1 次 |
{m,n}? |
最小匹配 m 至 n 次(包含 n 次) |
常用方法¶
方法 |
描述 |
|---|---|
搜索字符串,返回列表类型 |
|
搜索字符串,返回匹配结果的迭代类型,每个迭代元素是 matche 对象 |
|
从字符串的开始位置起匹配正则表达式,返回 matche 对象 |
|
在字符串中搜索匹配正则表达式的第一个位置,返回 matche 对象 |
|
将一个字符串按照正则表达式匹配结果进行分割,返回列表类型 |
|
在一个字符串中替换所有匹配正则表达式的子串,返回替换后的字符串 |
re.findall(pattern, string, flags=0)¶
从左到右扫描字符串搜索匹配样式,将匹配结果按找到的顺序以列表的形式返回。如果样式里存在一到多个组,就返回一个组合列表。空匹配也会包含在结果里。
re.finditer(pattern, string, flags=0)¶
匹配样式在字符串中所有的非重复匹配,返回一个迭代器。字符串从左到右扫描,匹配按顺序排列。空匹配也包含在结果里。
re.match(pattern, string, flags=0)¶
从字符串开始位置匹配样式,就返回一个相应的匹配对象。如果没有匹配,就返回 None ;注意它跟零长度匹配是不同的。
pattern:正则表达式语句
string:待匹配字符串
flags:正则表达式的控制标记
常用标记 |
说明 |
|---|---|
re.A re.ASCII |
让 \w、\W、\b、\B、\d、\D、\s 和 \S 只匹配 ASCII,而不是 Unicode。 |
re.I re.IGNORECASE |
忽略大小写 |
re.S re.DOTALL |
. 操作符匹配任意字符,默认匹配除换行符外的其他任意字符 |
re.search(pattern, string, flags=0)¶
扫描整个字符串搜索匹配样式的第一个位置,并返回一个相应的匹配对象。如果没有匹配,就返回一个 None ;注意这和找到一个零长度匹配是不同的。
Note
search() 和 match()
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'>
re.split(pattern, string, maxsplit=0, flags=0)¶
用匹配样式分割字符串。如果在匹配样式中捕获到括号,那么所有的组里的文字也会包含在列表里。
maxsplit:最大分割数,剩余部分将全部放入列表的最后一个元素
re.sub(pattern, repl, string, count=0, flags=0)¶
返回通过使用 repl 替换后的新字符串。如果样式没有找到,则返回原字符串。
repl:字符串替换中的新字符串。repl 可以是字符串或函数
count:匹配的最大替换次数
repl 为字符串,则其中任何反斜杠转义序列都会被处理。也就是说, \n 会被转换为一个换行符, \r 会被转换为一个回车附,依此类推。其他未知转义序列例如 \& 会保持原样。向后引用像是 \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 为一个函数,那它会对每个非重复的匹配样式的情况调用。这个函数只能有一个匹配对象参数,并返回一个替换后的字符串。比如
>>> 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,所有的匹配都会被替换。
匹配对象¶
匹配对象总是有一个布尔值 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'3.6 新版功能.
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() 的支持。匹配对象被看作是原子性的。
编译正则表达式¶
在 Python 中使用正则表达式有几个步骤,但每一步都相当简单。
用 import re 导入正则表达式模块。
用 re.compile() 函数创建一个 Regex 对象(记得使用原始字符串)。
向 Regex 对象的 search() 方法传入想查找的字符串。它返回一个 Match 对象。
调用 Match 对象的 group() 方法,返回实际匹配文本的字符串。
>>> import re
>>> phoneNumRegex = re.compile(r'\d{3}-\d{3}-\d{4}')
>>> mo = phoneNumRegex.search('My number is 415-555-4242.')
>>> print('Phone number found: ' + mo.group())
Phone number found: 415-555-4242
编译正则表达式¶
正则表达式被编译成模式对象,模式对象具有各种操作的方法,例如搜索模式匹配或执行字符串替换。
re.compile() 也接受一个可选的 flags 参数,用于启用各种特殊功能和语法变体。 我们稍后将介绍可用的设置,但现在只需一个例子 >>>
>>> p = re.compile('ab*', re.IGNORECASE)
正则作为字符串传递给 re.compile() 。 正则被处理为字符串,因为正则表达式不是核心Python语言的一部分,并且没有创建用于表达它们的特殊语法。 (有些应用程序根本不需要正则,因此不需要通过包含它们来扩展语言规范。)相反,re 模块只是Python附带的C扩展模块,就类似于 socket 或 zlib 模块。
将正则放在字符串中可以使 Python 语言更简单,但有一个缺点是下一节的主题。
re.compile(pattern, flags=0)
将正则表达式的样式编译为一个正则表达式对象(正则对象),可以用于匹配,通过这个对象的方法 match(),search() 以及其他如下描述。
这个表达式的行为可以通过指定标记的值来改变。值可以是以下任意变量,可以通过位的 OR 操作来结合(| 操作符)。
序列
prog = re.compile(pattern) result = prog.match(string)
等价于
result = re.match(pattern, string)
如果需要多次使用这个正则表达式的话,使用 re.compile() 和保存这个正则对象以便复用,可以让程序更加高效。
Note
通过 re.compile() 编译后的样式,和模块级的函数会被缓存,所以少数的正则表达式使用无需考虑编译的问题。