温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

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

发布时间:2020-07-16 23:44:21 来源:网络 阅读:7807 作者:wangy8961 栏目:开发技术

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

1. 标准库模块 re

更多详情参考官方文档:

  • https://docs.python.org/3/howto/regex.html#regex-howto
  • https://docs.python.org/3/library/re.html

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
. 匹配除换行符\n以外的任意一个字符。如果要匹配多行文本,可以指定re.DOTALL标志位,或者(?:.|\n)*表示匹配.\n,且作为一个非捕获组,再指定*表示0个或多个前面的非捕获组 ab. 匹配abc或abC,不匹配ab,因为b后面一定要有一个字符
\ 转义字符,比如要匹配点号.本身,需要转义它\.,如果不转义,.将有上一行所示的特殊含义 ab\. 匹配ab.com,不匹配abc
[] 匹配中括号内的一个字符<br />1. 中括号内的字符可以全部列出,如[abc]表示匹配字符abc<br />2. 也可以使用-表示范围,如[a-z]表示匹配所以小写字母中的任意一个字符<br />3. 本文后续要介绍的如*+等特殊字符在中括号内将失去特殊含义,如[*+()]表示匹配字符*+()<br />4. 本文后续要介绍的特殊字符集如\d\w等也可以放入此中括号内,继续保持特殊含义<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] 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,且中间有一个数字字符或空白字符,所以它会匹配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!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ \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)

语法 说明 模式示例 匹配
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\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()测试 匹配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 \b. py\B 匹配pythonpy3py2,但不匹配pypy.py!
\A Matches only at the start of the string. \Aab abcde
\Z Matches only at the end of the string. ab\Z 123ab

2.5 分组

2.6 扩展语法

3. 实例

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

3.2 最短匹配模式: 非贪婪

3.3 多行匹配

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

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI