python正则表达式处理文本-re模块
1.概述
正则表达式通常用于含有大量文本处理的应用当中。例如,它们经常用作开发者使用的文本编辑程序的搜索模式,包括 vi,emacs 和现代集成开发环境。它们也是 Unix 命令行工具的组成部分,例如 sed、grep 和 awk。很多编程语言是在语法中支持正则表达式(Perl、Ruby、Awk 和 Tcl),但 C、C++ 和 Python 等其他语言,通过扩展库来支持正则表达式。
正则表达式存在多个开源实现,每个实现都共享一个通用的核心语法,但对高级特性有不同的扩展和修改。Python 的 re 库使用的语法基于 Perl 的正则表达式语法,并且有一些特定于 Python 的增强。
尽管「正则表达式」的正式定义仅限于描述正则语言的表达式,但 re 支持的一些扩展已经超出了正则语言的范畴。在这里,「正则表达式」术语有点更通用的感觉,意味着是任何可以被 Python 的 re 模块所描述的表达式。
2.re模块介绍
python中re模块赋予了正则更强的表达能力:
尽管「正则表达式」的正式定义仅限于描述正则语言的表达式,但 re 模块支持的一些扩展已经超出了正则语言的范畴。
在这里,「正则表达式」术语有点更通用的感觉,意味着是任何可以被 Python 的 re 模块所描述的表达式。
re模块结构介绍
re模块由9个常量、12个函数、1个异常构成,下面通过示例详细介绍他们使用方法,应用到开发中解决实际的问题。
re模块常用函数介绍
re模块提供了许多的函数对文本做不同的处理,下面是函数处理文本的职责。
- match函数
使用一个模式匹配文本,如果匹配成功就返回匹配到的内容,否则返回None - compile 函数
用于编译正则表达式,生成一个正则表达式( Pattern )对象,供 match() 和 search() 这两个函数使用 - search函数
扫描整个字符串并返回第一个成功的匹配,如果没有匹配,就返回一个 None。 - findall函数
在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。注意: match 和 search 是匹配一次 findall 匹配所有。 - finditer函数
和 findall 类似,在字符串中找到正则表达式所匹配的所有子串,并把它们作为一个迭代器返回。 - sub函数
sub是substitute的所写,表示替换,将匹配到的数据进⾏替换。 - subn函数
行为与sub()相同,但是返回一个元组 (字符串, 替换次数)。 - split函数
根据匹配进⾏切割字符串,并返回⼀个列表。
3.re模块常量介绍
3.1.常量介绍
re模块常量是定义在一个继承Int类型的枚举类中,有9个常量,常量的值都是int类型。
class RegexFlag(enum.IntFlag):ASCII = A = sre_compile.SRE_FLAG_ASCII # assume ascii "locale"IGNORECASE = I = sre_compile.SRE_FLAG_IGNORECASE # ignore caseLOCALE = L = sre_compile.SRE_FLAG_LOCALE # assume current 8-bit localeUNICODE = U = sre_compile.SRE_FLAG_UNICODE # assume unicode "locale"MULTILINE = M = sre_compile.SRE_FLAG_MULTILINE # make anchors look for newlineDOTALL = S = sre_compile.SRE_FLAG_DOTALL # make dot match newlineVERBOSE = X = sre_compile.SRE_FLAG_VERBOSE # ignore whitespace and comments# sre extensions (experimental, don't rely on these)TEMPLATE = T = sre_compile.SRE_FLAG_TEMPLATE # disable backtrackingDEBUG = sre_compile.SRE_FLAG_DEBUG # dump pattern after compilation
1.IGNORECASE
语法: re.IGNORECASE 或简写为 re.I
作用: 进行忽略大小写匹配
下面的例子查询match时会将大写和小写都查询出来
import repattern = 'match'
text = 'Does this text match the pattern, Match text'print('默认区分大小写:', re.findall(pattern, text), end='\n')print('IGNORECASE属性忽略大小写', re.findall(pattern, text, re.IGNORECASE))
查找文本时使用re.IGNORECASE
常量忽略了match的大小写,大写和小写的match都会查询到。
默认区分大小写: ['match']
IGNORECASE忽略大小写 ['match', 'Match']
2.ASCII码
语法: re.ASCII 或简写为 re.A
作用: 顾名思义,ASCII表示ASCII码的意思,让 \w, \W, \b, \B, \d, \D, \s 和 \S 只匹配ASCII,而不是Unicode。
import re
pattern = r'\w+'
text = 'a白日依山尽b,黄河入海流c'print('Unicode模式查找结果:', re.findall(pattern, text))print('ASCII模式查找结果:', re.findall(pattern, text, re.ASCII))
在默认匹配模式下\w+匹配到了所有字符串,而在ASCII模式下,只匹配到了a、b、c(ASCII编码支持的字符)。
Unicode模式查找结果: ['a白日依山尽b', '黄河入海流c']
ASCII模式查找结果: ['a', 'b', 'c']
3.DOTALL
语法: re.DOTALL 或简写为 re.S
作用: DOT表示.
,ALL表示所有,连起来就是.
匹配所有,包括换行符\n。默认模式下.
是不能匹配行符\n
的。
import re
pattern = r'.*'
text = '白日依山尽,\n黄河入海流'
print('默认匹配:', re.findall(pattern, text))print('.匹配所有:', re.findall(pattern, text, re.DOTALL))
在默认匹配模式下.
并没有匹配换行符\n,而是将字符串分开匹配;而在re.DOTALL模式下,换行符\n与字符串一起被匹配到。
默认匹配: ['白日依山尽,', '', '黄河入海流', '']
.匹配所有: ['白日依山尽,\n黄河入海流', '']
4. MULTILINE
语法: re.MULTILINE 或简写为 re.M
作用: 多行模式,当某字符串中有换行符\n,默认模式下是不支持匹配下一行行头匹配。而多行模式下是支持匹配行开头的。
正则表达式中^表示匹配行的开头,默认模式下它只能匹配字符串的开头;而在多行模式下,它还可以匹配 换行符\n后面的字符。
注意:正则语法中^匹配行开头、\A匹配字符串开头,单行模式下它两效果一致,多行模式下\A不能识别\n。
import re
pattern = r'^黄'
text = '白日依山尽,\n黄河入海流'
print('默认匹配:', re.findall(pattern, text))
print('多行匹配行头', re.findall(pattern, text, re.MULTILINE))
多行模式匹配到了第二行开头黄
字符
默认匹配: []
多行匹配行头 ['黄']
5. VERBOSE
语法: re.VERBOSE 或简写为 re.X
作用: 详细模式,可以在正则表达式中加注解!
import re
pattern = r'''
白日 # 白天的太阳
'''
text = '白日依山尽,\n黄河入海流'
print('默认匹配:', re.findall(pattern, text))
print('详细模式匹配注释', re.findall(pattern, text, re.VERBOSE))
默认模式下并不能识别正则表达式中的注释,而详细模式是可以识别的。
默认匹配: []
详细模式匹配注释 ['白日']
6.LOCALE
语法: re.LOCALE 或简写为 re.L
作用: 由当前语言区域决定 \w, \W, \b, \B 和大小写敏感匹配,这个标记只能对byte样式有效。这个标记官方已经不推荐使用,因为语言区域机制很不可靠,它一次只能处理一个 "习惯”,而且只对8位字节有效。
7.UNICODE
语法: re.UNICODE 或简写为 re.U
作用: 与 ASCII 模式类似,匹配unicode编码支持的字符,但是 Python 3 默认字符串已经是Unicode,所以有点冗余。
8. DEBUG
语法: re.DEBUG
作用: 显示编译时的debug信息。
import re
pattern = r'白日'
text = '白日依山尽,\n黄河入海流'
print('默认匹配:', re.findall(pattern, text))
print('debug模式', re.findall(pattern, text, re.DEBUG))
运行结果
默认匹配: ['白日']
LITERAL 30333
LITERAL 260850. INFO 10 0b11 2 2 (to 11)prefix_skip 2prefix [0x767d, 0x65e5] ('白日')overlap [0, 0]
11: LITERAL 0x767d ('白')
13. LITERAL 0x65e5 ('日')
15. SUCCESS
debug模式 ['白日']
9.TEMPLATE
语法: re.TEMPLATE 或简写为 re.T
作用:disable backtracking(禁用回溯)
3.2.常量组合使用
常量可叠加使用,因为常量值都是2的幂次方值,所以是可以叠加使用的,叠加时请使用 | 符号,请勿使用+ 符号!
import repattern = '^match'
text = 'Does this text match the pattern, \nMatch text'print('默认区分大小写:', re.findall(pattern, text), end='\n')print('多个模式组合使用', re.findall(pattern, text, re.IGNORECASE | re.MULTILINE))
上面使用了忽略大小写和多行模式组合查询出Match开头的字符。
默认区分大小写: []
IGNORECASE属性忽略大小写 ['Match']
4.re模块函数
re模块有12个函数可以查找匹配文本,根据他们的功能分类如下:
4.1.查找一个匹配项
查找并返回一个匹配项的函数有3个:search、match、fullmatch,他们的区别分别是
- search: 查找任意位置的匹配项
- match: 必须从字符串开头匹配
- fullmatch: 整个字符串与正则完全匹配
1.查找一个匹配项示例
这个例子分别使用search
、match
、fullmatch
三个函数测试查询一个匹配项的区别。
import re
pattern = '白日'
text = 'A白日依山尽,白日依山尽'print('search:', re.search(pattern, text).group())print('match', re.match(pattern, text))print('fullmatch:', re.fullmatch(pattern, text))
search函数在字符串任意位置匹配,符合表达式模式就会返回结果。
match函数需要从头匹配,在文本中开头有个A
字符,因此他没有匹配到结果。
fullmatch函数匹配的内容需要与文本完全相同,因此返回的结果也是空。
search: 白日
match None
fullmatch: None
2.查询结果处理函数
使用search
、match
、fullmatch
三个函数查询字符串返回值是一个Match(Generic[AnyStr])
类型对象,在该对象中提供了一些方法用来处理返回值。
下面介绍下处理匹配结果常用方法
- start:获取匹配结果开始索引地址
- end:获取匹配结果结束索引地址
- group:分组截取字符串
import re
pattern = '白日'
text = '白日依山尽,白日依山尽'search_result = re.search(pattern, text)
print(search_result)
# search 获取查询开始和结尾索引
s = search_result.start()
e = search_result.end()# search_result.re.pattern 获取匹配模式, search_result.string获取匹配的文本
print('Found "{}"\nin "{}"\nfrom {} to {} ("{}")'.format(search_result.re.pattern, search_result.string, s, e, text[s:e]))print('gourp 分组显示查询结果', search_result.group())
print('span 返回查询结果起止下标:', search_result.span())
运行结果
<re.Match object; span=(0, 2), match='白日'>
Found "白日"
in "白日依山尽,白日依山尽"
from 0 to 2 ("白日")
gourp 分组显示查询结果 白日
span 返回查询结果起止下标: (0, 2)
4.2.查找多个匹配项
查找多项函数主要有:findall函数
与 finditer函数
两个方法基本类似,只不过一个是返回列表,一个是返回迭代器。我们知道列表是一次性生成在内存中,而迭代器是需要使用时一点一点生成出来的,节省内存。
- findall: 从字符串任意位置查找,返回一个列表
- finditer:从字符串任意位置查找,返回一个迭代器
import re
pattern = '白日'
text = '白日依山尽,白日依山尽'search_sult = re.search(pattern, text)
print('查找一个匹配项:', search_sult.group())findall_sult = re.findall(pattern, text)
print('查找多个匹配项:', findall_sult)
查找同一个文本,使用search查找结果只有一个。使用findall查找所有符合条件的值。
查找一个匹配项: 白日
查找多个匹配项: ['白日', '白日']
4.3.分割文本
re.split(pattern, string, maxsplit=0, flags=0) 函数作用就是根据分割模式对文本进行分隔,他不是查找字符。
- pattern相当于str.split()中的sep,分隔符的意思,不但可以是字符串,也可以为正则表达式
- maxsplit表示最多进行分割次数,
- flags表示模式,就是上面我们讲解的常量
1.不限制分割次数
import re
pattern = '[a-b]'
text = '1,2,3,4,a,5,6,7,8,b,9,10,11,12'split_sult = re.split(pattern, text, maxsplit=0, flags=re.IGNORECASE)
print('split分割', split_sult)
# 列表索引切片操作获取数据
print(split_sult[0])
print(split_sult[:2])
上面使用[a-b]
模式分割文本,maxsplit设置为0表示为不显示分割次数,split函数将在文本中遇到a或b字符就会将文本分割一次,返回的结果是一个列表,可以通过索引获取每个值。
split分割 ['1,2,3,4,', ',5,6,7,8,', ',9,10,11,12']
1,2,3,4,
['1,2,3,4,', ',5,6,7,8,']
2.限制分割次数
import re
pattern = '[a-b]'
text = '1,2,3,4,a,5,6,7,8,b,9,10,11,12'split_sult = re.split(pattern, text, maxsplit=1, flags=re.IGNORECASE)
print('split分割', split_sult)
设置split分割一次,下面分割后列表中有两个元素。
split分割 ['1,2,3,4,', ',5,6,7,8,b,9,10,11,12']
4.4.替换
替换主要有sub函数 与 subn函数
re.sub(pattern, repl, string, count=0, flags=0) 函数:
- repl替换掉string中被pattern匹配的字符
- count表示最大替换次数
- flags表示正则表达式的常量
re.subn(pattern, repl, string, count=0, flags=0) 函数与 re.sub函数 功能一致,只不过返回一个元组 (字符串, 替换次数)
1.sub替换文本
sub函数中的入参:repl替换内容既可以是字符串,也可以是一个函数哦! 如果repl为函数时,只能有一个入参:Match匹配对象。
import re
pattern = '在'
repl = '尽'
text = '白日依山在,黄河入海流'
sub_sult = re.sub(pattern, repl, text)
print('sub替换文本:',sub_sult)
4.5.编译正则模板
re模块执行过程
re模块匹配文本时需要执行两个步骤,编译正则表达式,然后用表达式匹配文本。
为什么要预编译表达式
如果每次匹配文本都要编译一次正则表达式效率会降低,因此我们可以可通过compile
函数预编译表达式,每次匹配文本时就省去了编译步骤。
编译表达式对象函数
compile函数 与 template函数 将正则表达式的样式编译为一个 正则表达式对象 ,这个对象与re模块有同样的正则函数。
注意
不论你是否使用预编译表达式,python在re模块的源码中默认使用compile预编译模式执行匹配文本。
查看 findall(), search(), match()的源码
def search(pattern, string, flags=0):"""Scan through string looking for a match to the pattern, returninga Match object, or None if no match was found."""# 调用 _compile函数,编译表达式,然后在调用search查询文本return _compile(pattern, flags).search(string)
1.compile函数编译表达式
import re
pattern = '白日'
# 预编译表达式
pattern_obj = re.compile(pattern)
text = '白日依山尽,白日依山尽'compile_sult = pattern_obj.search(text).group()
print('编译正则模板:',compile_sult)
运行结果
编译正则模板: 白日
2.template函数
template函数 与 compile函数 类似,只不过是增加了我们之前说的re.TEMPLATE 模式,我们可以看看源码。
def template(pattern, flags=0):"Compile a template pattern, returning a Pattern object"return _compile(pattern, flags|T)
4.6.转义特殊字符
re.escape(pattern) 可以转义正则表达式中具有特殊含义的字符,比如:.
或者 *
import re
pattern = '白日*'
text = '白日*依山尽,黄河.入海流'
search_sult = re.search(pattern, text)
print('search查询结果:', search_sult.group())# 输出转义字符
escape_sult = re.escape(pattern)
print('escape转义字符输出结果:', escape_sult)
使用普通的search函数输出时没有将*
特殊字符输出,escape函数将*
特殊字符转义后输出,但是它的输出显示转义有问题在*
前面加上了\
因此不建议使用它转义,还是手动转义好。
search查询结果: 白日
escape转义字符输出结果: 白日\*
下面是在表达式中手动对特殊字符转义。
import re
# 手动转义
pattern = '白日\*'
text = '白日*依山尽,黄河.入海流'
search_sult = re.search(pattern, text)
print('search查询结果:', search_sult.group())
运行结果符合我们查找的结果
search查询结果: 白日*
4.7.清除正则表达式缓存
re.purge() 函数作用就是清除 正则表达式缓存,具体有什么缓存呢?我们先看看源码
def purge():"Clear the regular expression caches"_cache.clear()_compile_repl.cache_clear()
5.正则语法
使用好re模块解决工作中遇到的问题,除了掌握re模块提供的函数,还要掌握正则本身的语法。
正则语法是标准的语法在网上也是非常容易找到,这里提供了菜鸟教程的正则语法,方便在工作中查阅正则语法。
菜鸟正则语法:https://www.runoob.com/regexp/regexp-syntax.html