Python 标准库模块 - re

一. 正则表达式基础

正则表达式引擎对正则表达式文本进行编译生成正则表达式对象,再由正则表达式对象对目标文本进行匹配,后返回匹配结果。

正则表达式中一共包含 2 中字符:普通字符元字符

  • 对于普通字符,匹配字符本身;
  • 元字符在正则表达式中具有特殊的含义,按照功能划分为:匹配单个字符匹配重复性匹配位置分组转义元字符

匹配单个字符的元字符及其含义:

元字符 释意
. 匹配单个字符,re.DOTALL 模式中可以匹配 \n
\d 匹配数字,相当于 [0-9]
\D 匹配非数字,相当于 [^\d]
\s 匹配任意空字符,相当于 [\t\n\r\f\v]
\S 匹配任意非空字符。
\w 匹配任意一个普通字符:[_0-9a-zA-Z]
\W 匹配任意的非普通字符:[^_0-9a-zA-Z],即匹配特殊字符。
[] 匹配括号内的任意一个字符。

匹配重复性的元字符及其含义:

元字符 释意
* 匹配前一个字符 0 次或多次,比如:正则表达式 ab* 匹配的是 a ab abb abbbbbbbbbb ....
+ 匹配前一个字符 1 次或多次。
? 匹配一个字符 0 次或 1 次,非贪婪匹配。
{n} 匹配前一个字符 n 次。
{m,n} 匹配前一个字符 m~n 次。

匹配位置的元字符及其含义:

元字符 释意
^ \A 匹配字符串开头,在多行模式中匹配每一行的开头。
[^] 匹配字符集以外的任意一个字符,如 [^a-z] 匹配除了 a-z 以外的任意一个字符。
$ \Z 匹配字符串末尾,在多行模式中匹配每一行的末尾。
\b 匹配单词边界。例如:'er\b' 可以匹配 "never" 中的 'er',但不能匹配 "verb" 中的 'er'
\B 匹配非单词边界。

分组匹配的元字符及其含义:

元字符 释意
(?P) 为分组指定一个别名 name
\number 引用编号为 number 的分组匹配到的内容,如:r'(\d)abc\1' 可以匹配类似 1abc1 5abc5 ... 的字符串。
引用别名为 name 的分组匹配到的内容,如:r'(?P\d)abc(?P=id)' 也可以匹配类似 1abc1 5abc5 ...的字符串。

表示或运算的元字符:

元字符 释意
| 或。匹配 | 符号左右表达式任意一个,如果 | 没有包括在 () 中,则它的范围是整个正则表达式。

转义元字符:

元字符 释意
\ 正则表达式在匹配特殊符号时需要转义,如匹配 . 则需要使用 \.

二. re 模块

在 Python 中使用正则表达式,我们需要借助 re 模块提供的强大 API,下面我们就来学习几个 re 模块常用的接口吧~

findall

参数说明:findall("正则表达式", "要匹配的字符串", flags=标志1|标志2|...)
返回值:以列表形式返回匹配到的字符串。

其中参数 flags 常用的标志如下

  • re.S / re.DOTALL:使 . 匹配包括换行在内的所有字符;
  • re.I / re.IGNORECASE:使匹配对大小写不敏感;
  • re.M / re.MULTILINE:多行匹配影响 ^$
  • re.U / re.UNICODE:根据 Unicode 字符集解析字符,这个标志影响 \w,\W,\b,\B

下面,我们用 findall 返回 Python 之禅中首尾用到的反义词:

import re

zen_of_python = """
The Zen of Python, by Tim Peters

1 Beautiful is better than ugly.
2 Explicit is better than implicit.
3 Simple is better than complex.
4 Complex is better than complicated.
5 Flat is better than nested.
6 Sparse is better than dense.
"""

regex = "\d\s*(\w+)\s*is better than\s*(\w+)\."
re.findall(regex, zen_of_python)

运行结果:

[('Beautiful', 'ugly'),
 ('Explicit', 'implicit'),
 ('Simple', 'complex'),
 ('Complex', 'complicated'),
 ('Flat', 'nested'),
 ('Sparse', 'dense')]

当正则表达式中含有一个以上分组时,findall 返回的列表由元组构成,元组中包含每个分组匹配到的内容。如果只有一个分组,则返回由该分组匹配到的内容组所构成的列表:

>> regex = "\d\s*(\w+)\s*is better than"
>> re.findall(regex, zen_of_python)
['Beautiful', 'Explicit', 'Simple', 'Complex', 'Flat', 'Sparse']

match

import re

my_str = "Last Checkpoint: 7小时前 11:23:00 *"
my_regex = r"Last\s+\w+:\s+(\d+)\D+\s+(.*)\s+"
result = re.match(my_regex, my_str)

match 函数返回的结果是一个 SRE_Match 对象:

>> result
<_sre.SRE_Match object; span=(0, 31), match='Last Checkpoint: 7小时前 11:23:00 '>

SRE_Match 具有很多的属性,比如 .string 属性可以方便我们我获取在匹配时输入的字符串:

>> result.string
'Last Checkpoint: 7小时前 11:23:00 *'

属性 .re 可以获取匹配时使用的编译后的正则表达式模式:

>> result.re
re.compile(r'Last\s+\w+:\s+(\d+)\D+\s+(.*)\s+', re.UNICODE)

由于默认使用了 re.UNICODE ,所以我们这里的 \w 可以匹配中文字符。

属性 .regs 则以列表的形式返回正则表达式匹配到的内容以及各个分组陪陪到的内容,不过请注意,返回的都是索引的形式:

>> result.regs
((0, 31), (17, 18), (22, 30))

可以使用序列切片来看一下我们的正则表达式及其中的两个分组所匹配到的内容:

>> my_str[slice(*result.regs[0])]
'Last Checkpoint: 7小时前 11:23:00 '
>> my_str[slice(*result.regs[1])]
'7'
>> my_str[slice(*result.regs[2])]
'11:23:00'

SRE_Match 对象也提供了非常多好用的方法,比如 groups 可以获取各个分组匹配到的内容:

>> result.groups()
('7', '11:23:00')

group 则可以灵活地获取正则表达式或对应分组匹配到的内容:

>> result.group(0)
'Last Checkpoint: 7小时前 11:23:00 '
>> result.group(1)
'7'
>> result.group(2)
'11:23:00'

如果使用的正则表达式定义了分组的名称,group 还可以通过名称获取相应分组匹配的内容:

>> my_regex = r"Last\s+\w+:\s+(?P\d+)\D+\s+(?P.*)\s+"
>> result = re.match(my_regex, my_str)
>> result.group('hours')
'7'
>> result.group('times')
'11:23:00'

在定义了分组的名称之后,还可以方便地使用 groupdict 以字典的形式返回所有分组匹配的结果:

>> result.groupdict()
{'hours': '7', 'times': '11:23:00'}

最后需要注意的是,match 从字符串的开头开始匹配,如果开头不符合要求,则直接返回 None

>> result = re.match(my_regex, "PYTHON"+my_str)
>> print(result)
None

search

match 匹配开头不同,search 匹配第一个符合规则的字符串,未成功则返回 None。参数:re.search(pattern, string, flags=0);返回值同 match

在介绍 match 的使用时,最后一个例子,由于我们在字符串前面添加了 'PYTHON' 导致 match 使用原来的正则表达式无法匹配,返回 None。此时,使用 search 就可以迎刃而解啦:

import re

my_str = "PYTHON Last Checkpoint: 7小时前 11:23:00 *"
my_regex = r"Last\s+\w+:\s+(?P\d+)\D+\s+(?P.*)\s+"
result = re.search(my_regex, my_str)

运行结果:

>> result

>> result.groupdict()
{'hours': '7', 'times': '11:23:00'}
>> result.group(0)
'Last Checkpoint: 7小时前 11:23:00 '
>> result.group('hours')
'7'
>> result.group('times')
'11:23:00'

小结:

split

参数:re.split(pattern, string, maxsplit=0, flags=0)split 功能非常强大,以正则表达式匹配到的标志来分隔字符串,比如下面这样一个混乱的字符串,我们要提取其中所有的数字:

>> my_str = "1*2+3-4^5"
>> my_regex = "[\*\+\-\^]"
>> re.split(my_regex, my_str)
['1', '2', '3', '4', '5']

下面,我们就来详细介绍一下 split 的用法。

首先,是以单字符切割:

>> line = 'a b  c;d;;e,f,,g'
>> re.split(';',line)
['a b  c', 'd', '', 'e,f,,g']

以分号切割时,共产生了 4 个子字符串,放在列表中返回。
下面,还是以单字符切割,但可以使用正则表达式中的 [] 来指定多种字符:

>> re.split('[;\s,]', line)
['a', 'b', '', 'c', 'd', '', 'e', 'f', '', 'g']

由于字符串 line 中有连续的 2 个分号,逗号或者空格,因此可以使用 [;\s,]+ 来切割:

>> re.split('[;\s,]+', line)
['a', 'b', 'c', 'd', 'e', 'f', 'g']

最后,上面的字符串在切割时,分隔符都没有被保留下来,使用括号捕获分组,即可保留分隔符:

>> re.split('([;\s,]+)', line)
['a', ' ', 'b', '  ', 'c', ';', 'd', ';;', 'e', ',', 'f', ',,', 'g']

sub / subn

  • re.sub(pattern, repl, string, count=0, flags=0) 将匹配到的内容替换为字符串 repl,返回替换后的字符串。
  • re.subn(pattern, repl, string, count=0, flags=0) 将匹配到的内容替换为字符串 repl,并返回替换的次数。

re.sub 提供比字符串的 replace 方法更加强大的功能:对于输入的字符串 string ,利用正则表达式 pattern 强大的字符串处理功能,实现复杂的字符串替换处理为 repl ,返回被替换后的字符串。

下面的例子中,我们将句子中多余的空格和数字去掉:

>> inputStr = "hello 123 python 456"
>> re.sub('\s\d+', '', inputStr)
'hello python'

如果想要知道替换过程中,共发生了多少次替换,可以使用 subn

>> re.subn('\s\d+', '', inputStr)
('hello python', 2)

小结:

compile

上述的案例中,我们每次都需要传入正则表达式,相应的函数每次在调用时,都需要编译一次正则表达式。如果上述过程需要多次重复,那么每次都去耗费时间编译正则表达式是很不划算的。

re 模块为我们提供了 compile 函数,用来编译正则表达式模式,返回编译好模式。因此,可以把那些常用的正则表达式编译成正则表达式对象,以提高效率。

格式:re.compile(pattern, flags=0),其中 pattern 为编译时用的表达式字符串,flags 为编译标志位,用于修改正则表达式的匹配方式,如:是否区分大小写,多行匹配等。常用的 flags 有:

注:使用按位或 | 连接多个 flags

我们上述介绍的 re 模块的匹配、分割、替换函数,compile 函数的返回值类提供了相应的方法,使用方式类似,只是不需要传入正则表达式字符串而已。

>> pattern1 = re.compile('\s\d+')
>> inputStr = "hello 123 python 456"
>> pattern1.sub('', inputStr)
'hello python'
>> pattern1.subn('', inputStr)
('hello python', 2)

由于用法几乎一致,这里就不一一举例啦~

三. 贪婪匹配和非贪婪匹配

贪婪模式* + ? {m,n},正则表达式的重复默认总是尽可能多得向后匹配内容。

>> re.findall(r'ab*',"abbbbbde")
['abbbbb']
>> re.findall(r'ab+',"abbbbbde")
['abbbbb']
>> re.findall(r'ab?',"abbbbbde")
['ab']
>> re.findall(r'ab{3,5}',"abbbbbde")
['abbbbb']

非贪婪模式*? +? ?? {m,n}?,尽可能少的匹配内容。

>> re.findall(r'ab*?',"abbbbbde")
['a']
>> re.findall(r'ab+?',"abbbbbde")
['ab']
>> re.findall(r'ab??',"abbbbbde")
['a']
>> re.findall(r'ab{3,5}?',"abbbbbde")
['abbb']

你可能感兴趣的:(Python 标准库模块 - re)