Python 正则表达式详解与 re 模块的使用
1. 标准库模块 re强烈推荐
正则表达式
在线测试网站: https://regex101.com/
https://docs.python.org/3/howto/regex.html#regex-howtohttps://docs.python.org/3/library/re.html更多详情参考官方文档:
Python3中使用re
模块支持正则表达式(Regular Expression),需要定义一个用于匹配的模式<br/>(pattern)
字符串,以及一个要匹配的字符串(string)
。简单的匹配:
In [1]: import reIn [2]: m = re.match('My', 'My name is wangy')In [3]: mOut[3]: <_sre.SRE_Match object; span=(0, 2), match='My'>In [4]: m.group() # 等价于m.group(0)Out[4]: 'My'In [5]: m.start(), m.end()Out[5]: (0, 2)In [6]: m.span()Out[6]: (0, 2)
其中,My
是正则表达式模式,最简单的,只匹配字符My
本身。而My name is wangy
是想要检查的字符串,re.match()
函数用于查看字符串是不是以正则模式开头
。
如果你仅仅是做一次简单的文本匹配/搜索操作的话,可以直接使用 re
模块级别的函数,比如re.match
。如果你打算做大量的匹配和搜索操作的话,最好先编译正则表达式
,然后再重复使用它:
In [1]: import reIn [2]: p = re.compile('[a-z]+') # [a-z]+ 是正则模式,表示1个或多个小写字母In [3]: pOut[3]: re.compile(r'[a-z]+', re.UNICODE)In [4]: if p.match('hello123'): # p是预编译后的正则模式,它也有match等方法,只是参数不同,不需要再传入正则模式。判断字符串'hello123'是否以1个或多个小写字母开头 ...: print('yes') ...: else: ...: print('no') ...: yesIn [5]: if p.match('123hi'): # 重用预编译过的正则模式 ...: print('yes') ...: else: ...: print('no') ...: no
模块级别的函数会将最近编译过的模式缓存起来,因此并不会消耗太多的性能, 但是如果使用预编译模式的话,你将会减少查找和一些额外的处理损耗。
1.1 使用match()
从字符串开头开始匹配可以使用模块级别的re.match()
或预编译模式的p.match()
,如果字符串是以正则表达式开头,则表明匹配成功,返回匹配到的对象,比如<_sre.SRE_Match object; span=(0, 2), match='My'>
,如果匹配失败,返回None
In [1]: import reIn [2]: m1 = re.match('wangy', 'wangy is a handsome boy.') # 模块级的match方法In [3]: m1 # 匹配成功,返回Match对象Out[3]: <_sre.SRE_Match object; span=(0, 5), match='wangy'>In [4]: m1.group() # Match对象有group()、start()、end()、span()等方法Out[4]: 'wangy'In [5]: m2 = re.match('mayun', 'wangy is a handsome boy.') # 匹配失败In [6]: type(m2) # 返回NoneOut[6]: NoneTypeIn [7]: p = re.compile('wangy') # 预编译正则模式也是可以的In [8]: p.match('wangy is a handsome boy.') # 调用预编译正则模式的match方法Out[8]: <_sre.SRE_Match object; span=(0, 5), match='wangy'>
1.2 使用search()
寻找首次匹配
如果字符串中有多个地方与正则表达式匹配的话,search()
方法返回第一次匹配到的结果:
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.(END)
In [1]: import reIn [2]: s = 'I wish I may, I wish I might have a dish of fish tonight.'In [3]: re.search('wish', s)Out[3]: <_sre.SRE_Match object; span=(2, 6), match='wish'>In [4]: re.search('wish', s).span()Out[4]: (2, 6)
1.3 使用findall()
或finditer()
寻找所有匹配
前面两个函数都是查找到一个匹配后就停止,如果要查找字符串中所有的匹配项,可以使用findall()
In [1]: import reIn [2]: text = 'Today is 11/27/2012. PyCon starts 3/13/2013.'In [3]: p = re.compile('\d+/\d+/\d+')In [4]: p.findall(text)Out[4]: ['11/27/2012', '3/13/2013']
findall()
方法会搜索文本并以列表形式返回所有的匹配。 如果你想以迭代
方式返回匹配,可以使用finditer()
方法来代替,比如:
In [5]: iters = p.finditer(text)In [6]: itersOut[6]: <callable_iterator at 0x7f94c1703f98>In [7]: for m in iters: ...: print(m) ...: <_sre.SRE_Match object; span=(9, 19), match='11/27/2012'><_sre.SRE_Match object; span=(34, 43), match='3/13/2013'>
1.4 使用split()
按匹配切分
字符串的str.split()
方法只适应于非常简单的字符串分割情形, 它并不允许有多个分隔符
或者是分隔符周围不确定的空格
。 当你需要更加灵活的切割字符串的时候,最好使用 re.split() 方法:
In [1]: import reIn [2]: line = 'asdf fjdk; afed, fjek,asdf, foo'In [3]: re.split(r'[;,\s]\s*', line) # 正则模式表示 ;或,或空白字符且它们的后面再跟0个或多个空白字符Out[3]: ['asdf', 'fjdk', 'afed', 'fjek', 'asdf', 'foo']
1.5 使用sub()
替换匹配
对于简单的字面模式,直接使用字符串的str.replace()
方法即可,比如:
In [1]: text = 'yeah, but no, but yeah, but no, but yeah'In [2]: text.replace('yeah', 'yep')Out[2]: 'yep, but no, but yep, but no, but yep'
对于复杂的模式,请使用re
模块中的sub()
,比如你想将形式为 11/27/2012 的日期字符串改成 2012-11-27 。示例如下:
In [1]: import reIn [2]: text = 'Today is 11/27/2012. PyCon starts 3/13/2013.'In [3]: re.sub('(\d+)/(\d+)/(\d+)', r'\3-\1-\2', text)Out[3]: 'Today is 2012-11-27. PyCon starts 2013-3-13.'
sub()
函数中的第一个参数是被匹配的模式,第二个参数是替换模式。反斜杠数字比如\3
指向前面模式的第3个捕获组,此时要加r
指定为原始字符串,否则会被Python自动转义为\x03
对于更加复杂的替换,可以传递一个替换回调函数来代替。一个替换回调函数的参数是一个Match
对象,也就是match()
或者find()
返回的对象。使用group()
方法来提取特定的匹配部分。回调函数最后返回替换字符串。比如:
In [1]: import reIn [2]: from calendar import month_abbrIn [3]: def change_date(m): ...: mon_name = month_abbr[int(m.group(1))] ...: return '{} {} {}'.format(m.group(2), mon_name, m.group(3)) ...: ...: In [4]: text = 'Today is 11/27/2012. PyCon starts 3/13/2013.'In [5]: p = re.compile(r'(\d+)/(\d+)/(\d+)')In [6]: p.sub(change_date, text)Out[6]: 'Today is 27 Nov 2012. PyCon starts 13 Mar 2013.'
如果除了替换后的结果外,你还想知道有多少替换发生了,可以使用re.subn()
来代替。比如:
In [7]: newtext, n = p.subn(r'\3-\1-\2', text)In [8]: newtextOut[8]: 'Today is 2012-11-27. PyCon starts 2013-3-13.'In [9]: nOut[9]: 2
2. 正则表达式语法2.1 基本模式语法 说明 模式示例 匹配 普通字符普通的文本值代表自身,用于匹配非特殊字符ab
ab.
匹配除换行符\n
以外的任意一个字符。如果要匹配多行文本,可以指定re.DOTALL
标志位,或者(?:.|\n)*
表示匹配.
或\n
,且作为一个非捕获组,再指定*
表示0个或多个前面的非捕获组ab.
匹配abc或abC,不匹配ab,因为b后面一定要有一个字符\
转义字符,比如要匹配点号.
本身,需要转义它\.
,如果不转义,.
将有上一行所示的特殊含义ab\.
匹配ab.com,不匹配abc[]
匹配中括号内的一个字符<br />1. 中括号内的字符可以全部列出,如[abc]
表示匹配字符a
或b
或c
<br />2. 也可以使用-
表示范围,如[a-z]
表示匹配所以小写字母中的任意一个字符<br />3. 本文后续要介绍的如*
、+
等特殊字符在中括号内将失去特殊含义,如[*+()]
表示匹配字符*
或+
或(
或)
<br />4. 本文后续要介绍的特殊字符集如\d
、\w
等也可以放入此中括号内,继续保持特殊含义<br />5. 如果中括号内的字符序列前面有一个^
,表示不匹配中括号内的任何一个字符,如[^0-9]
表示不匹配数字,a[^0-9]c
不匹配a1c
,但会匹配abc
<br />6. 要匹配字符]
,可以转义它,或者把它放在中括号内的首位,如a[()[\]{}]c
或a[]()[{}]c
都可以匹配到a]c
a[0-9]b
a1b或a2b2.2 特殊字符集语法 说明 模式示例 匹配 \d
匹配任意一个
数字字符,等价于[0-9]
a\db
a1b\D
匹配任意一个
非数字字符,等价于[^0-9]
a\Db
aAb\s
匹配任意一个
空白字符,等价于[ \t\n\r\f\v]
a\sb
a b\S
匹配任意一个
非空白字符,等价于[^ \t\n\r\f\v]
a\Sb
aAb\w
匹配任意一个
alphanumeric character,等价于[a-zA-Z0-9_]
a\wb
azb或aZb或a1b或a_b\W
匹配任意一个
non-alphanumeric character,等价于[^a-zA-Z0-9_]
a\Wb
a-b或a b
这些字符集也可以放入[]
中,比如a[\d\s]b
表示匹配字符a和字符b,且中间有一个数字字符或空白字符,所以它会匹配a1b
或a b
等
Python的string
模块中预先定义了一些可供我们测试用的字符串常量。我们将使用其中
的printable
字符串,它包含 100 个可打印的 ASCII 字符,包括大小写字母、数字、空格
符以及标点符号:
In [1]: import stringIn [2]: printable = string.printableIn [3]: len(printable)Out[3]: 100In [4]: printable[:50]Out[4]: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN'In [5]: printable[50:]Out[5]: 'OPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \t\n\r\x0b\x0c'
printable 中哪些字符是数字?
In [7]: re.findall('\d', printable)Out[7]: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
哪些字符是数字、字符或下划线?
In [8]: print(re.findall('\w', printable), end='')['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '_']
哪些属于空格符?
In [10]: re.findall('\s', printable)Out[10]: [' ', '\t', '\n', '\r', '\x0b', '\x0c']
\d
和\w
不仅会匹配ASCII字符,还可以匹配Unicode字符:
In [11]: s = 'abc' + '-/*' + '\u00ea' + '\u0115'In [12]: re.findall('\w', s)Out[12]: ['a', 'b', 'c', 'ê', 'ĕ']
2.3 数量
说明: 表格中 斜体 的 prev 表示 1. 单个字符如
a
2. 或者复杂的表达式如(abc)
或(.|\n)
(这是分组的功能,见1.5)
*
匹配0个或多个
prev,尽可能多
地匹配,贪婪模式,等价于{0,}
ab*
a或ab或abb或abbb,注意是匹配字符a后面跟0个或多个字符b
prev *?匹配0个或多个
prev,尽可能少
地匹配,非贪婪模式ab*?
a,非贪婪模式下匹配0个字符bprev +
匹配1个或多个
prev,尽可能多
地匹配,贪婪模式,等价于{1,}
ab+
ab或abb或abbbprev +?匹配1个或多个
prev,尽可能少
地匹配,非贪婪模式ab+?
ab,非贪婪模式下匹配1个字符bprev ?
匹配0个或1个
prev,尽可能多
地匹配,贪婪模式,等价于{0,1}
ab?
a或abprev ??匹配0个或1个
prev,尽可能少
地匹配,非贪婪模式ab??
a,非贪婪模式下匹配0个字符bprev {m}
匹配m个连续的
preva{3}
aaaprev {m,n}
匹配m到n个连续的
prev ,尽可能多
地匹配,贪婪模式。n
可选,如果不指定,则表示m到无穷多个连续的
preva{3,5}
aaa或aaaa或aaaaaprev {m,n}?匹配m到n个连续的
prev ,尽可能少
地匹配,非贪婪模式a{3,5}?
aaa可以在*
或+
或?
的后面再添加一个?
,此时表示非贪婪
模式匹配,Python中的正则表达式默认是贪婪模式匹配,它会在满足整个表达式要求的前提下,尽可能多地去匹配字符,具体效果见后面的示例
^
prev匹配以 prev 开头的字符串(脱字符)。多行文本中,默认^
只会匹配第一行的开头位置,如果设置了re.MULTILINE
标志位,则^
也会匹配换行符之后
的开头位置^ab
abcdprev $
匹配以 prev 结尾的字符串。多行文本中,默认$
只会匹配最后一行的结尾位置,如果设置了re.MULTILINE
标志位,则$
也会匹配换行符之前的结尾位置ab$
只匹配ab。如果是.*ab$
则会匹配123ab,否则使用ab\Z
\b
单词边界。Matches the empty string, but only at the beginning or end of a word. A word is defined as a sequence of word characters. Note that formally, \b
is defined as the boundary between a \w
and a \W
character (or vice versa), or between \w
and the beginning/end of the string. 注意: \b
在Python中默认会被转义为\x08
表示退格
,需要将整个正则表达式指定为原始字符串(在前面加个r),即r'\bfoo\b'
r'\bfoo\b'
<br />请使用re.findall()
测试匹配foo
或foo.
或(foo)
或bar foo baz
,但不匹配foobar
或foo3
\B
非单词边界。Matches the empty string, but only when it is not
at the beginning or end of a word. \B
is just the opposite of \b
.py\B
匹配python
或py3
或py2
,但不匹配py
或py.
或py!
\A
Matches only at the start of the string.\Aab
abcde\Z
Matches only at the end of the string.ab\Z
123ab2.5 分组2.6 扩展语法3. 实例3.1 字符串忽略大小写的搜索替换3.2 最短匹配模式: 非贪婪3.3 多行匹配详情请移步我的个人网站: http://www.madmalls.com/blog/post/regular-expression-in-python3/
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。