Python 正则表达式详解与 re 模块的使用

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 正则表达式详解与 re 模块的使用相关的知识,希望对你有一定的参考价值。

强烈推荐正则表达式在线测试网站: https://regex101.com/

1. 标准库模块 re

更多详情参考官方文档:

Python3中使用re模块支持正则表达式(Regular Expression),需要定义一个用于匹配的模式<br/>(pattern)字符串,以及一个要匹配的字符串(string)。简单的匹配:

In [1]: import re

In [2]: m = re.match(‘My‘, ‘My name is wangy‘)

In [3]: m
Out[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 re

In [2]: p = re.compile(‘[a-z]+‘)  # [a-z]+ 是正则模式,表示1个或多个小写字母

In [3]: p
Out[3]: re.compile(r‘[a-z]+‘, re.UNICODE)

In [4]: if p.match(‘hello123‘):   # p是预编译后的正则模式,它也有match等方法,只是参数不同,不需要再传入正则模式。判断字符串‘hello123‘是否以1个或多个小写字母开头
   ...:     print(‘yes‘)
   ...: else:
   ...:     print(‘no‘)
   ...:     
yes

In [5]: if p.match(‘123hi‘):      # 重用预编译过的正则模式
   ...:     print(‘yes‘)
   ...: else:
   ...:     print(‘no‘)
   ...:     
no

模块级别的函数会将最近编译过的模式缓存起来,因此并不会消耗太多的性能, 但是如果使用预编译模式的话,你将会减少查找和一些额外的处理损耗。

1.1 使用match()从字符串开头开始匹配

可以使用模块级别的re.match()或预编译模式的p.match(),如果字符串是以正则表达式开头,则表明匹配成功,返回匹配到的对象,比如&lt;_sre.SRE_Match object; span=(0, 2), match=‘My‘&gt;,如果匹配失败,返回None

In [1]: import re

In [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)  # 返回None
Out[6]: NoneType

In [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 re

In [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 re

In [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]: iters
Out[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 re

In [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 re

In [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 re

In [2]: from calendar import month_abbr

In [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]: newtext
Out[8]: ‘Today is 2012-11-27. PyCon starts 2013-3-13.‘

In [9]: n
Out[9]: 2

2. 正则表达式语法

2.1 基本模式

语法 说明 模式示例 匹配
普通字符 普通的文本值代表自身,用于匹配非特殊字符 ab ab
. 匹配除换行符 以外的任意一个字符。如果要匹配多行文本,可以指定re.DOTALL标志位,或者(?:.| )*表示匹配. ,且作为一个非捕获组,再指定*表示0个或多个前面的非捕获组 ab. 匹配abc或abC,不匹配ab,因为b后面一定要有一个字符
转义字符,比如要匹配点号.本身,需要转义它.,如果不转义,.将有上一行所示的特殊含义 ab. 匹配ab.com,不匹配abc
[] 匹配中括号内的一个字符<br />1. 中括号内的字符可以全部列出,如[abc]表示匹配字符abc<br />2. 也可以使用-表示范围,如[a-z]表示匹配所以小写字母中的任意一个字符<br />3. 本文后续要介绍的如*+等特殊字符在中括号内将失去特殊含义,如[*+()]表示匹配字符*+()<br />4. 本文后续要介绍的特殊字符集如dw等也可以放入此中括号内,继续保持特殊含义<br />5. 如果中括号内的字符序列前面有一个^,表示不匹配中括号内的任何一个字符,如[^0-9]表示不匹配数字,a[^0-9]c不匹配a1c,但会匹配abc<br />6. 要匹配字符],可以转义它,或者把它放在中括号内的首位,如a[()[]{}]ca[]()[{}]c都可以匹配到a]c a[0-9]b a1b或a2b

2.2 特殊字符集

语法 说明 模式示例 匹配
d 匹配任意一个数字字符,等价于[0-9] adb a1b
D 匹配任意一个非数字字符,等价于[^0-9] aDb aAb
s 匹配任意一个空白字符,等价于[ fv] asb a b
S 匹配任意一个非空白字符,等价于[^ fv] aSb aAb
w 匹配任意一个 alphanumeric character,等价于[a-zA-Z0-9_] awb azb或aZb或a1b或a_b
W 匹配任意一个 non-alphanumeric character,等价于[^a-zA-Z0-9_] aWb a-b或a b

这些字符集也可以放入[]中,比如a[ds]b表示匹配字符a和字符b,且中间有一个数字字符或空白字符,所以它会匹配a1ba b

Python的string模块中预先定义了一些可供我们测试用的字符串常量。我们将使用其中
printable字符串,它包含 100 个可打印的 ASCII 字符,包括大小写字母、数字、空格
符以及标点符号:

In [1]: import string

In [2]: printable = string.printable

In [3]: len(printable)
Out[3]: 100

In [4]: printable[:50]
Out[4]: ‘0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMN‘

In [5]: printable[50:]
Out[5]: ‘OPQRSTUVWXYZ!"#$%&‘()*+,-./:;<=>[email protected][\]^_`{|}~ 	

x0bx0c‘

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]: [‘ ‘, ‘	‘, ‘
‘, ‘
‘, ‘x0b‘, ‘x0c‘]

dw不仅会匹配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)(.| )(这是分组的功能,见1.5)

语法 说明 模式示例 匹配
prev * 匹配0个或多个 prev尽可能多地匹配,贪婪模式,等价于{0,} ab* a或ab或abb或abbb,注意是匹配字符a后面跟0个或多个字符b
prev *? 匹配0个或多个 prev尽可能少地匹配,非贪婪模式 ab*? a,非贪婪模式下匹配0个字符b
prev + 匹配1个或多个 prev尽可能多地匹配,贪婪模式,等价于{1,} ab+ ab或abb或abbb
prev +? 匹配1个或多个 prev尽可能少地匹配,非贪婪模式 ab+? ab,非贪婪模式下匹配1个字符b
prev ? 匹配0个或1个 prev尽可能多地匹配,贪婪模式,等价于{0,1} ab? a或ab
prev ?? 匹配0个或1个 prev尽可能少地匹配,非贪婪模式 ab?? a,非贪婪模式下匹配0个字符b
prev {m} 匹配m个连续的 prev a{3} aaa
prev {m,n} 匹配m到n个连续的 prev尽可能多地匹配,贪婪模式。n可选,如果不指定,则表示m到无穷多个连续的 prev a{3,5} aaa或aaaa或aaaaa
prev {m,n}? 匹配m到n个连续的 prev尽可能少地匹配,非贪婪模式 a{3,5}? aaa

可以在*+?的后面再添加一个?,此时表示非贪婪模式匹配,Python中的正则表达式默认是贪婪模式匹配,它会在满足整个表达式要求的前提下,尽可能多地去匹配字符,具体效果见后面的示例

2.4 边界

语法 说明 模式示例 匹配
^ prev 匹配以 prev 开头的字符串(脱字符)。多行文本中,默认^只会匹配第一行的开头位置,如果设置了re.MULTILINE标志位,则^也会匹配换行符之后的开头位置 ^ab abcd
prev $ 匹配以 prev 结尾的字符串。多行文本中,默认$只会匹配最后一行的结尾位置,如果设置了re.MULTILINE标志位,则$也会匹配换行符之前的结尾位置 ab$ 只匹配ab。如果是.*ab$则会匹配123ab,否则使用ab
 单词边界。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,  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. 注意: 在Python中默认会被转义为x08表示退格,需要将整个正则表达式指定为原始字符串(在前面加个r),即r‘foo‘ r‘foo‘<br />请使用re.findall()测试 匹配foofoo.(foo)bar foo baz,但不匹配foobarfoo3
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 . pyB 匹配pythonpy3py2,但不匹配pypy.py!
A Matches only at the start of the string. Aab abcde
 Matches only at the end of the string. ab 123ab

2.5 分组

2.6 扩展语法

3. 实例

3.1 字符串忽略大小写的搜索替换

3.2 最短匹配模式: 非贪婪

3.3 多行匹配

详情请移步我的个人网站: http://www.madmalls.com/blog/post/regular-expression-in-python3/

以上是关于Python 正则表达式详解与 re 模块的使用的主要内容,如果未能解决你的问题,请参考以下文章

python与正则表达式:re模块详解

python与正则表达式:re模块详解

学不会的python之正则表达式详解(re模块)

学不会的python之正则表达式详解(re模块)

Python高级详解 正则表达式(re模块)

Python高级详解 正则表达式(re模块)