Python内置的正则库 re
正则表达式(regular expression,regex)是一种用于匹配和操作文本的强大工具,它是由一系列字符和特殊字符组成的模式,用于描述要匹配的文本模式。
正则表达式可以在文本中查找、替换、提取和验证特定的模式。
正则表达式模式(pattern)
字符
普通字符和元字符
大多数字母和符号都会简单地匹配自身。例如,正则表达式 test
将会精确地匹配到 test
。(你可以启用不区分大小写模式,让这个正则也匹配 Test
或 TEST
,稍后会详细介绍。)
但该规则有例外。有些字符是特殊的 元字符(metacharacters),并不匹配自身。它们表示匹配一些非常规的内容,或者通过重复它们或改变它们的含义来影响正则的其他部分。
元字符包括:. ^ $ * + ? { } [ ] \ | ( )
,它们的作用将在下面具体的类别中介绍。
匹配一种字符
只能匹配字符串中出现的某个确定的字符
- 普通字符
- 字母、数字、汉字、下划线、以及没有特殊定义的标点符号,都是 " 普通字符 "。表达式中的普通字符,在匹配一个字符串的时候,匹配与之相同的一个字符。
- 例如,表达式 “c”,在匹配字符串 “abcde” 时,匹配结果是:成功;匹配到的内容是:“c”;匹配到的位置是:开始于 2,结束于 3。(包含开始位置,不包含结束位置)
- 转义字符
- 一些不可打印字符,采用在前面加
\
的方法来表示它要匹配的内容,例如制表符(\t
)、换行符\n
等; - 一些有特殊用处的元字符,在前面加
\
的方法来代表该符号本身。例如{,}, [, ], /, \, +, *, ., $, ^, |, ?
等。
- 一些不可打印字符,采用在前面加
匹配多种字符
能够和多种字符都匹配的上,但是只匹配符合条件的第一个,不是全匹配完
.
(点)- 在默认模式,匹配除了换行的任意字符。如果指定了标签 DOTALL ,它将匹配包括换行符的任意字符
- 例如,表达式 “c.”,在匹配字符串 “abcde” 时,匹配结果是:成功;匹配到的内容是:“cd”;匹配到的位置是:开始于 2,结束于 4。
[]
字符集合- 字符可以单独列出,比如
[amk]
匹配'a'
,'m'
, 或者'k'
。 - 可以表示字符范围,通过用
'-'
将两个字符连起来。比如[a-z]
将匹配任何小写 ASCII 字符,[0-5][0-9]
将匹配从00
到59
的两位数字,[0-9A-Fa-f]
将匹配任何十六进制数位。 如果-
进行了转义 (比如[a\-z]
)或者它的位置在首位或者末尾(如[-a]
或[a-]
),它就只表示普通字符'-'
。 - 特殊字符在集合中,失去它的特殊含义。比如
[(+*)]
只会匹配这几个文法字符'('
,'+'
,'*'
, or')'
。 - 字符类如
\w
或者\S
(如下定义) 在集合内可以接受,它们可以匹配的字符由ASCII
或者LOCALE
模式决定。 - 不在集合范围内的字符可以通过 取反 来进行匹配。如果集合首字符是
'^'
,所有 不 在集合内的字符将会被匹配,比如[^5]
将匹配所有字符,除了'5'
,[^^]
将匹配所有字符,除了'^'
.^
如果不在集合首位,就没有特殊含义。 - 在集合内要匹配一个字符
']'
,有两种方法,要么就在它之前加上反斜杠,要么就把它放到集合首位。比如,[()[\]{}]
和[]()[{}]
都可以匹配括号。
- 字符可以单独列出,比如
- 转义方式表示的特殊字符集
\s
:匹配 ASCII 中的空白字符,就是[ \t\n\r\f\v]
。\S
:相当于\s
取反,匹配 ASCII 中的非空白字符,就是[^ \t\n\r\f\v]
\d
:匹配任何十进制数的字符,就是[0-9]
\D
:相当于\d
取反,匹配任何非十进制数的字符,就是[^0-9]
\w
:匹配 ASCII 字符中的数字和字母和下划线,就是[a-zA-Z0-9_]
。如果设置了LOCALE
标记,就匹配当前语言区域的数字和字母和下划线。\W
:相当于\w
取反,匹配非单词字符的字符,就是[^a-zA-Z0-9_]
。如果使用了LOCALE
旗标,则会匹配当前区域中既非字母数字也非下划线的字符。
重复
指定正则的某部分必须重复一定的次数。
?
表示匹配一次或零次,即把某项内容变成了可选的- 例如,
home-?brew
可以匹配'homebrew'
或'home-brew'
。
- 例如,
*
表示匹配 零次 或更多次+
表示匹配一次或更多次- 例如,
ca+t
可以匹配'cat'
( 1 个'a'
)或'caaat'
( 3 个'a'
),但不能匹配'ct'
- 例如,
{m,n}
表示必须至少重复 m 次,至多重复 n 次。- m 和 n 都是十进制整数,它们不是必填的,缺失的情况下会设定为默认值。缺失 m 会解释为最少重复 0 次 ,缺失 n 则解释为最多重复无限次。
- 例如,
a/{1,3}b
将匹配'a/b'
,'a//b'
和'a///b'
。 它不能匹配'ab'
,因为其中没有斜杠,也不能匹配'a////b'
,因为其中有四个斜杠。
位置和断言
零宽度断言:它们不会使解析引擎在字符串中前进一个字符;相反,它们根本不占用任何字符,只是成功或失败。例如,\b
是一个断言,指明当前位置位于字边界;这个位置根本不会被 \b
改变。这意味着永远不应重复零宽度断言,因为如果它们在给定位置匹配一次,它们显然可以无限次匹配。
简单来说,零宽断言就是匹配一个位置,这个位置满足某个正则,但是不纳入匹配结果的,所以叫“零宽”,而且这个位置的前面或后面需要满足某种正则。
除了匹配位置的正则外,后面要说的管道匹配符
|
也属于零宽断言,因为它只表示两个正则的“逻辑或”,自身不匹配任何字符。
基本位置
\b
匹配一个单词边界,也就是指单词和空格间的位置。- 例如,
er\b
可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。
- 例如,
\B
与\b
相反,匹配非单词边界。er\B
能匹配“verb”中的“er”,但不能匹配“never”中的“er”。^
匹配字符串的开头- 例如,
^abc
可以匹配“abcx”,但是不能匹配“xabc”,因为后者开头是 “x”,而^abc
要求是以 “a” 开头。
- 例如,
$
匹配字符串的结尾- 例如,
abc$
可以匹配“xabc”,但是不能匹配“abcx”,因为后者结尾是 “x”,而abc$
要求是以 “c” 结尾。
- 例如,
\A
:仅匹配字符串的开头。 当不在MULTILINE
模式时,\A
和^
实际上是相同的。 在MULTILINE
模式中,它们是不同的:\A
仍然只在字符串的开头匹配,但^
可以匹配在换行符之后的字符串内的任何位置。\Z
:和\A
相反,只匹配字符串尾。
特殊位置
有时候在使用正则表达式做匹配的时候,我们希望匹配一个字符串,这个字符串的前面或后面需要是特定的内容(这个内容比较复杂,不是简单的字符开始或结尾,而是需要用另一个正则来表示),这时候我们就需要用到前视断言和后视断言。
肯定型前视断言:(?=exp)
匹配一个位置(但结果不包含此位置)之前的文本内容,这个位置满足正则 exp,举例:匹配出字符串 string 中以 s 结尾的单词的前半部分
string = "Things are good! \nApples are fruits!"
re.findall(r'\b\w+(?=s\b)',string) # ['Thing', 'Apple', 'fruit']
否定型前视断言:(?!exp)
匹配一个位置(但结果不包含此位置)之前的文本,此位置不能满足正则 exp,举例:匹配出字符串 string 中不以 ing 结尾的单词的前半部分.负向断言不支持匹配不定长的表达式
string = 'done do doing'
re.findall(r'\b\w{3}(?!ing\b)',string) # ['don', 'doi']
string = 'done do doing'
re.findall(r'\b\w{2}(?!ing\b)',string) # ['do', 'do']
string = 'done do doing'
re.findall(r'\w{2}',string) # ['do', 'ne', 'do', 'do', 'in']
肯定型后视断言:(?<=exp)
匹配一个位置(但结果不包含此位置)之后的文本,这个位置满足正则 exp,举例:匹配出字符串 string 中以 h 开头的单词的后半部分.
string = 'hello, my honey! I think you are hungry!'
re.findall(r'(?<=\bh)\w+\b',string) # ['ello', 'oney', 'ungry']
否定型后视断言:(?<!exp)
匹配一个位置(但结果不包含此位置)之后的文本,这个位置不能满足正则 exp,举例:匹配字符串 s 中不以 do 开头的单词.
string = 'done do going'
re.findall(r'(?<!\bd)\w{5}\b',string) # ['going']
管道 |
(逻辑或)
A|B
, A 和 B 可以是任意正则表达式,创建一个正则表达式,匹配 A 或者 B. 任意个正则表达式可以用 '|'
连接。它也可以在组合内使用。
扫描目标字符串时, '|'
分隔开的正则样式从左到右进行匹配。当一个样式完全匹配时,这个分支就被接受。意思就是,一旦 A 匹配成功, B 就不再进行匹配,即便它能产生一个更好的匹配。或者说,'|'
操作符绝不贪婪。
如果要匹配 '|'
字符,使用 \|
, 或者把它包含在字符集里,比如 [|]
.
例子:表达式 "Tom|Jack" 在匹配字符串 "I'mTom, he is Jack" 时,匹配结果是:成功;匹配到的内容是:"Tom";匹配到的位置是:开始于 4,结束于 7。如果要继续匹配下一个时,匹配结果是:成功;匹配到的内容是:"Jack";匹配到的位置时:开始于 15,结束于 19。
组合
普通组 (…)
匹配括号内的任意正则表达式,并标识出组合的开始和结尾。在被匹配次数修饰的时候,括号中的表达式可以作为整体被修饰;匹配完成后,组合的内容还可以被获取,并可以在之后用 \number
转义序列进行再次匹配
例如,表达式 (go\s*)+
在匹配 “Let's go go go!” 时,匹配到内容是:“go go go”
命名组 (?P<name>…)
name
是该组的名称。命名组的行为与普通组完全相同,并且还将名称与组关联。这样后面在引用它们时,不仅可以像普通组中那样,通过数字去引用它们,还可以通过给的名称引用它们。
p = re.compile(r'(?P<word>\b\w+\b)')
m = p.search( '(((( Lots of punctuation )))' )
m.group('word')
m.group(1)
非捕获组 (?:…)
普通组和命名组都属于捕获组,即它们会“捕获”并“记下”匹配到的内容。但是有时你只想使用“组”来把正则表达式的某个部分括起来(显得清晰整洁),但是对检索组的内容不感兴趣。 这种情况下,可以通过使用非捕获组来达到相应目的。
非捕获组所匹配的子字符串 * 不能 * 在执行匹配后被获取或是之后在 pattern 中被引用。
贪婪模式与非贪婪模式
默认条件下,正则匹配操作是遵循贪婪模式的 。
例如,当通过 *
、+
等正则 pattern 进行重复正则时,匹配引擎将尝试重复尽可能多的次数。 如果表达式的后续部分不匹配,则匹配引擎将回退并以较少的重复次数再次尝试。
通过一个逐步示例更容易理解这一点。让我们分析一下表达式 a[bcd]*b
。 该表达式首先匹配一个字母 'a'
,接着匹配字符类 [bcd]
中的零个或更多个字母,最后以一个 'b'
结尾。 现在想象一下用这个正则来匹配字符串 'abcbd'
。
步骤 | 匹配 | 说明 |
---|---|---|
1 | a |
正则中的 a 匹配成功。 |
2 | abcbd |
引擎尽可能多地匹配 [bcd]* ,直至字符串末尾。 |
3 | 失败 | 引擎尝试匹配 b ,但是当前位置位于字符串末尾,所以匹配失败。 |
4 | abcb |
回退,让 [bcd]* 少匹配一个字符。 |
5 | 失败 | 再次尝试匹配 b , 但是当前位置上的字符是最后一个字符 'd' 。 |
6 | abc |
再次回退,让 [bcd]* 只匹配 bc 。 |
6 | abcb |
再次尝试匹配 b 。 这一次当前位置的字符是 'b' ,所以它成功了。 |
此时正则表达式已经到达了尽头,并且匹配到了 'abcb'
。 这个例子演示了匹配引擎一开始会尽其所能地进行匹配,如果没有找到匹配,它将逐步回退并重试正则的剩余部分,如此往复,直至 [bcd]*
只匹配零次。如果随后的匹配还是失败了,那么引擎会宣告整个正则表达式与字符串匹配失败。
如果要变成非贪婪模式,可以在 "*","?","+","{m,n}" 后面加上 "?"
例如,a.*b
匹配 'aabcbcd' 时,在默认的贪婪模式下,匹配到的内容为 'aabcb';而如果改用非贪婪模式的 a.*?b
匹配 'aabcbcd' 时,匹配到的内容为 'aab'
其他
扩展标记法/内联标记
(?…)
是个扩展标记法。 '?'
后面的第一个字符决定了这个构建采用什么样的语法。这种扩展通常并不创建新的组合((?P<name>…)
是唯一的例外),只是表示一些特殊的匹配规则,可以代替后文 re.match()
、 re.search()
等正则匹配方法和正则编译方法 re.compile()
中的 flags
参数的功能(相当于把这个参数直接写到正则 pattern 里面去)。 以下是目前支持的扩展:
(?aiLmsux:…)
- (
'a'
,'i'
,'L'
,'m'
,'s'
,'u'
,'x'
中的一个或多个) 这个组合匹配一个空字符串;这些字符对正则表达式设置以下标记re.A
(只匹配 ASCII 字符),re.I
(忽略大小写),re.L
(语言依赖),re.M
(多行模式),re.S
(点 dot 匹配全部字符),re.U
(Unicode 匹配), andre.X
(冗长模式),详见 文档 中的描述。 如果你想将这些标记包含在正则表达式中,这个方法就很有用,免去了在re.compile()
中传递 flag 参数。标记应该在表达式字符串首位表示。 - 例如
re.compile(r'(?i:ab)c')
意思是“ab”部分忽略大小写,所以可以匹配“Abc”、“ABc”等,但是不能匹配“abC”,因为“c”不在括号范围内。
- (
(?aiLmsux-imsx:…)
- (
'a'
,'i'
,'L'
,'m'
,'s'
,'u'
,'x'
中的 0 或者多个, 之后可选跟随'-'
在后面跟随'i'
,'m'
,'s'
,'x'
中的一到多个 .) 这些字符为表达式的其中一部分 设置 或者 去除 相应标记re.A
(只匹配 ASCII),re.I
(忽略大小写),re.L
(语言依赖),re.M
(多行),re.S
(点匹配所有字符),re.U
(Unicode 匹配), andre.X
(冗长模式),详见 文档 中的描述。 'a'
,'L'
and'u'
作为内联标记是相互排斥的, 所以它们不能结合在一起,或者跟随'-'
。 当他们中的某个出现在内联组中,它就覆盖了括号组内的匹配 pattern。在 Unicode 样式中,(?a:…)
切换为 只匹配 ASCII,(?u:…)
切换为 Unicode 匹配 (默认). 在 byte 样式中(?L:…)
切换为语言依赖模式,(?a:…)
切换为 只匹配 ASCII (默认)。这种方式只覆盖组合内匹配,括号外的匹配 pattern 不受影响。
- (
注意事项
- 反斜杠灾难
- 正则表达式里使用
\
作为转义字符,假如你需要匹配文本中的字符\
,那么使用编程语言表示的正则表达式里将需要 4 个反斜杠\\\\
:前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。 - 可以使用原生字符串解决这个问题,Python 中字符串前⾯加上 r 表示原⽣字符串,原生字符串里面的
\
不需要转义,就表示它自身。这样就只需要考虑正则 pattern 里面\
的表达方式,即用 2 个反斜杠\\
就可以了。
- 正则表达式里使用
正则表达式方法
re
模块定义了几个函数,属性,和一个异常。绝大部分重要的应用,总是会先将正则表达式编译,之后在进行操作。
基本匹配
从字符串开头开始匹配 re.match(pattern, string, flags=0)
如果 string 开始的 0 或者多个字符匹配到了正则表达式样式,就返回一个相应的匹配对象。 如果没有匹配,就返回 None
;注意它跟零长度匹配是不同的。
注意即便是 MULTILINE
多行模式, re.match()
也只匹配字符串的开始位置,而不匹配每行开始。
如果你想定位 string 的任何位置,使用 search()
来替代(也可参考 search() vs. match() )
从字符串任意位置开始匹配 re.search(pattern, string, flags=0)
扫描整个 字符串 找到匹配样式的第一个位置,并返回一个相应的 匹配对象。如果没有匹配,就返回一个 None
; 注意这和找到一个零长度匹配是不同的。
search() vs. match()
Python 提供了两种不同的操作:基于 re.match() 检查字符串开头,或者 re.search() 检查字符串的任意位置,例如
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
注意 MULTILINE
多行模式中函数 match()
只匹配字符串的开始,但使用 search()
和以 '^'
开始的正则表达式会匹配每行的开始
re.match('X', 'A\nB\nX', re.MULTILINE) # No match
re.search('^X', 'A\nB\nX', re.MULTILINE) # Match
匹配整个字符串 re.fullmatch(pattern, string, flags=0)
如果整个 string 完全匹配到正则表达式样式,就返回一个相应的匹配对象。 否则就返回一个 None
;注意这跟零长度匹配是不同的。
找到所有匹配项 re.findall(pattern, string, flags=0)
对 string 返回一个不重复的 pattern 的匹配列表, string 从左到右进行扫描,匹配按找到的顺序返回。如果样式里存在一到多个组,就返回一个组合列表;就是一个元组的列表(如果样式里有超过一个组合的话)。空匹配也会包含在结果里。
以迭代器的形式返回所有匹配项 re.finditer(pattern, string, flags=0)
pattern 在 string 里所有的非重复匹配,返回为一个迭代器 iterator,内容是匹配对象。 string 从左到右扫描,匹配按顺序排列。空匹配也包含在结果里。
匹配后编辑
匹配后分割字符串 re.split(pattern, string, maxsplit=0, flags=0)
>>> 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.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 作为一整个组进行引用。
re.subn(pattern, repl, string, count=0, flags=0)
,行为与sub()
相同,但是返回一个元组(字符串, 替换次数)
.
正则编译
正则 pattern 编译 re.compile(pattern, flags=0)
将正则表达式的 pattern 编译为一个 正则表达式对象 (正则对象),可以通过这个对象的方法,如 match()
, search()
等,对字符串进行匹配。
如果需要多次使用这个正则表达式的话,使用 re.compile()
编译保存这个正则对象以便复用,可以让程序更加高效。
通过
re.compile()
编译后的样式,和模块级的函数会被缓存, 所以少数的正则表达式使用无需考虑编译的问题。
prog = re.compile(pattern)
result = prog.match(string)
# 等价于
result = re.match(pattern, string)
这个表达式的行为可以通过指定 flag
的值来改变(也可以直接在正则 pattern 中通过指定特定内联标记来改变)。值可以是以下任意变量,可以通过“位或”操作来结合( |
操作符)多个变量。
re.A
/re.ASCII
/内联标记(?a:…)
让 \w
, \W
, \b
, \B
, \d
, \D
, \s
和 \S
只匹配 ASCII,而不是 Unicode。这只对 Unicode 样式有效,会被 byte 样式忽略。相当于前面语法中的 。
注意,为了保持向后兼容, re.U
标记依然存在(还有他的同义 re.UNICODE
和嵌入形式 (?u)
) , 但是这些在 Python 3 是冗余的,因为默认字符串已经是 Unicode 了(并且 Unicode 匹配不允许 byte 出现)。
-
re.DEBUG
: 显示编译时的 debug 信息,没有内联标记。 -
re.I
/re.IGNORECASE
/内联标记(?i:…)
进行忽略大小写匹配;表达式如 [A-Z]
也会匹配小写字符。Unicode 匹配(比如 Ü
匹配 ü
)同样有用,除非设置了 re.ASCII
标记来禁用非 ASCII 匹配。当前语言区域不会改变这个标记,除非设置了 re.LOCALE
标记。
注意,当设置了 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
/内联标记(?L:…)
由当前语言区域决定 \w
, \W
, \b
, \B
和大小写敏感匹配。这个标记只能对 byte 样式有效。这个标记不推荐使用,因为语言区域机制很不可靠,它一次只能处理一个 " 习惯”,而且只对 8 位字节有效。Unicode 匹配在 Python 3 里默认启用,并可以处理不同语言。
re.M
/re.MULTILINE
/内联标记(?m:…)
设置以后,样式字符 '^'
匹配字符串的开始,和每一行的开始(换行符后面紧跟的符号);样式字符 '$'
匹配字符串尾,和每一行的结尾(换行符前面那个符号)。默认情况下,’^’
匹配字符串头,'$'
匹配字符串尾 。
re.S
/re.DOTALL
/内联标记(?s:…)
让 '.'
特殊字符匹配任何字符,包括换行符;如果没有这个标记,'.'
就匹配 除了 换行符的其他任意字符。
re.X
/re.VERBOSE
/ 内联标记(?x:…)
这个标记允许你编写更具可读性更友好的正则表达式。通过分段和添加注释。空白符号会被忽略,除非在一个字符集合当中或者由反斜杠转义,或者在 *?
, (?:
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*")
正则表达式对象支持的方法和属性
编译得到的正则表达式对象支持的方法和属性,和基本正则匹配中的相应函数类似,但是参数略有差异
Pattern.match(string[, pos[, endpos]])
- 如果 string 的 开始位置 能够找到这个正则样式的任意个匹配,就返回一个相应的 匹配对象。如果不匹配,就返回
None
;注意它与零长度匹配是不同的。 - 可选参数 pos 和 endpos 与
search()
含义相同。
- 如果 string 的 开始位置 能够找到这个正则样式的任意个匹配,就返回一个相应的 匹配对象。如果不匹配,就返回
>>> 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'>
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.fullmatch(string[, pos[, endpos]])
- 如果整个 string 匹配这个正则表达式,就返回一个相应的匹配对象。 否则就返回
None
; 注意跟零长度匹配是不同的。 - 可选参数 pos 和 endpos 与
search()
含义相同。
- 如果整个 string 匹配这个正则表达式,就返回一个相应的匹配对象。 否则就返回
>>> 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 ,限制搜索范围Pattern.finditer(string[, pos[, endpos]])
类似函数finiter()
, 使用了编译后样式,但也可以接收可选参数 pos 和 endpos ,限制搜索范围Pattern.sub(repl, string, count=0)
等价于sub()
函数,使用了编译后的样式。Pattern.subn(repl, string, count=0)
等价于subn()
函数,使用了编译后的样式。Pattern.flags
正则匹配标记。这是可以传递给compile()
的参数,任何(?…)
内联标记,隐性标记比如UNICODE
的结合。Pattern.groups
正则 pattern 中捕获组的数量。Pattern.groupindex
命名组的名字和组号构成的字典,如{'gname1':1, 'gname2':2}
。如果没有命名组,那字典就是空的。Pattern.pattern
编译对象的原始样式字符串。
匹配对象
如果正则表达式成功匹配到了字符串,就会返回一个“匹配对象”(对应的布尔值为 True),反之会返回 None(对应的布尔值是 False)
匹配对象支持以下方法和属性:
方法
Match.expand(template)
- 对 template 进行反斜杠转义替换并且返回,就像
sub()
方法中一样。转义如同\n
被转换成合适的字符,数字引用 (\1
,\2
) 和命名组合 (\g<1>
,\g<name>
) 替换为相应组合的内容。
- 对 template 进行反斜杠转义替换并且返回,就像
Match.group([group1, …])
- 根据输入的组号,返回一个或者多个匹配的子组。
- 组号意义
- 组号为 0,表示整个正则 pattern 匹配到的内容
- 组号为
[1..99
] 的正整数,表示正则 pattern 里面相应的组匹配到的内容 - 对于命名组, 组号也可能是命名组的名字。
- 如果一个组被匹配多次,就返回最后一个匹配结果
- 组号为负数,或者大于样式中定义的组数,或者是未定义的命名组名称,会抛出一个
IndexError
- 参数可能情况
- 如果没有参数,等价于参数为 0,结果是一个字符串,表示整个正则 pattern 匹配的内容
- 如果只有一个参数(组号),结果是一个字符串,表示该组匹配的内容
- 如果有多个参数(组号),结果是一个元组(每个参数对应一个项),表示对应组匹配的内容。
>>> 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')
一个相对复杂的例子
>>> 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)
- 返回一个元组,包含在正则 pattern 中出现的从 1 到任意多的组合。 default 参数用于指定未参与匹配的组的返回值,默认为
None
。
- 返回一个元组,包含在正则 pattern 中出现的从 1 到任意多的组合。 default 参数用于指定未参与匹配的组的返回值,默认为
>>> m = re.match(r"(\d+)\.(\d+)", "24.1632")
>>> m.groups()
('24', '1632')
>>> m = re.match(r"(\d+)\.?(\d+)?", "24") # second group is not matched
>>> 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
。 例如
- 返回一个字典,包含了所有的 命名 子组。key 就是组名。 default 参数用于不参与匹配的组合;默认为
>>> 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 匹配到的字串的开始和结束 index。group 默认为 0(意思是整个匹配的子串)。
- 如果 group 存在,但未产生匹配,就返回
-1
。 - 如果 group 存在,并产生匹配 ,则它匹配的内容 (等价于
m.group(group)
) 是m.string[m.start(g):m.end(g)]
- 如果 group 匹配到一个空字符串的话,
m.start(group)
将会等于m.end(group)
一个应用的例子
>>> 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,就是整个匹配。
- 对于一个匹配 m , 返回一个二元组
属性
Match.pos
/Match.endpos
属性Match.lastindex
属性- 最后一个匹配到的捕获组的索引值,如果没有匹配产生的话,返回
None
。比如,对于字符串'ab'
,表达式(a)b
,((a)(b))
, 和((ab))
将得到lastindex == 1
, 而(a)(b)
会得到lastindex == 2
。
- 最后一个匹配到的捕获组的索引值,如果没有匹配产生的话,返回
Match.lastgroup
- 最后一个匹配到的捕获组的名字,如果它没有名字(不是命名组)或者完全没有产生匹配,返回
None
- 最后一个匹配到的捕获组的名字,如果它没有名字(不是命名组)或者完全没有产生匹配,返回
Match.re
/Match.string
其他
转移正则 pattern 中的元字符 re.escape(pattern)
如果你想对任意可能包含正则表达式元字符的文本字符串进行匹配,它就是有用的。比如
>>> print(re.escape('http://www.python.org'))
http://www\.python\.org
>>> legal_chars = string.ascii_lowercase + string.digits + "!#$%&'*+-.^_`|~:"
>>> print('[%s]+' % re.escape(legal_chars))
[abcdefghijklmnopqrstuvwxyz0123456789!\#\$%\&'\*\+\-\.\^_`\|\~:]+
>>> 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()