python正则表达式必知必会的基础

正则表达式

本文主要讲的是正则表达式在python中的简单使用,只介绍关键的几个方法函数,就不赘述正则表达式的基础知识了。在我个人的工作学习生活场景中,正则表达式主要用在清洗数据、路由代码、判断用户输入字段是否合规等场景,很简单,拆开来说,无非就下面这些常见的使用场景,使用的函数也就那几个,其他现学现卖就好了。

  • 从头到尾只匹配一次,即匹配到第一次就返回结果,若匹配不到就返回None

    • 从头开始匹配:re.match(),例如检验用户输入的手机号是否合规、路由匹配
    • 不从头开始匹配:re.search(),多用在寻找目标字段,可通过其groups属性提取
  • 从头到尾找多次

    • re.findall(),返回匹配到的字符串列表,这个后面细说
  • 替换数据

    • re.sub(),很多时候要清洗文本中的换行符等字符,或者是替换日期格式等操作

从我的角度来说,re.search()是使用最多的,结合分组、命名等使用方式写个匹配规则,然后search一下,提取目标字段。然后就是标识符需要好好看看,很多时候感觉表达式对了,但是就是匹配不到,很可能是因为标识符没设置,在你掌握了re.search()flags后,已经能应付很多需求了,其他函数也自然就触类旁通,接下来开始正文。

re.match()和re.search()

SRE_Match object

为什么要把re.match()和re.search()放在一起说呢,因为它们两个返回的是同一种对象,官方描述是SRE_Match object,其打印结果如下所示。

text = '<p>asdasdasaaansdghaasdasdsdfs</p>kkkkkkkkkkkkk'

reg = r'<.*?>(?P<content>.*?)</.*?>'

res01 = re.match(pattern=reg, string=text, flags=re.S + re.M)
res02 = re.search(pattern=reg, string=text, flags=re.S + re.M)
print(res01)
print(res02)

print(type(res01))
print(type(res02))

# 打印结果
<_sre.SRE_Match object; span=(034), match='<p>asdasdasaaansdghaasdasdsdfs</p>'>
<_sre.SRE_Match object; span=(034), match='<p>asdasdasaaansdghaasdasdsdfs</p>'>
<class '_sre.SRE_Match'>
<class '_sre.SRE_Match'>

这个search_match_obj中有几个常用的属性和方法

res = re.search(pattern=reg, string=text, flags=re.S)
res.group()   # 只获取第一组的字符串
res.groups()  # 获取组元组
res.groupdict()  # 获取组的key:value对
res.span()  # 匹配的串的开始:结束位  (0, 34)

re.match()

re.match()尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配成功的话,match()就返回none。

函数语法

re.match(pattern, string, flags=0)

函数参数说明:

参数 描述
pattern 匹配的正则表达式。
string 要匹配的字符串。
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 – 可选标志

匹配成功re.match()返回一个匹配的对象,否则返回None。可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式。

使用场景:例如检验用户输入的手机号是否合规、路由匹配等。

使用示例:

text = 'aa<p>asdasdasaaansdghaasdasdsdfs</p>kkkkkkkkkkkkk'
reg = r'<.*?>(?P<content>.*?)</.*?>'
res = re.match(pattern=reg, string=text, flags=re.S + re.M)
print(res)   # 因为一开头就匹配不上,将返回None


# 如下情况则能匹配到
text = 'aa<p>asdasdasaaansdghaasdasdsdfs</p>kkkkkkkkkkkkk'
reg = r'<.*?>(?P<content>.*?)</.*?>'
res = re.match(pattern=reg, string=text, flags=re.S + re.M)
print(res)    # SRE_Match object
print(res.group())
print(res.groups())
print(res.groupdict())

打印结果如下:
<_sre.SRE_Match object; span=(034), match='<p>asdasdasaaansdghaasdasdsdfs</p>'>

<p>asdasdasaaa
sdghaasdasdsdfs</p>

('asdasdasaaansdghaasdasdsdfs''p')

{'content''asdasdasaaansdghaasdasdsdfs'}

(034)

re.search()(重点)

re.search 扫描整个字符串并返回第一个成功的匹配。

函数语法:

re.search(pattern, string, flags=0)

函数参数说明:

参数 描述
pattern 匹配的正则表达式
string 要匹配的字符串。
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。

匹配成功re.search()返回一个匹配的对象,否则返回None。可以使用group(num) 或 groups() 匹配对象函数来获取匹配表达式。

python正则表达式必知必会的基础
image-20210803211907846

使用场景:例如检验用户输入的手机号是否合规、路由匹配等。

使用示例:多用在寻找目标字段,提取、清洗数据。

import re

text = '<p>asdasdasaaansdghaasdasdsdfs</p>kkkkkkkkkkkkk'

# 一个分组,并命名为content,.*?是非贪婪模式下匹配任意字段
reg = r'<.*?>(?P<content>.*?)</.*?>'
res = re.search(pattern=reg, string=text, flags=re.S + re.M)
print(res)
print(res.group())
print(res.groups())
print(res.groupdict())

# 打印结果如下
<_sre.SRE_Match object; span=(034), match='<p>asdasdasaaansdghaasdasdsdfs</p>'>
<p>asdasdasaaa
sdghaasdasdsdfs</p>
('asdasdasaaansdghaasdasdsdfs',)
{'content''asdasdasaaansdghaasdasdsdfs'}

分组的意义,就是在匹配成功的字符串中,再提取()里面的字符串,re.search中通过groupdict()拿到分组对应的内容。

re.flags

前文中,关于flags的使用如下:

res = re.search(pattern=reg, string=text, flags=re.S + re.M)

re.S + re.M 代表同时使用两种模式,re.S主要是为了让.匹配到n,而re.M则是为了避免因为有n而导致的换行匹配不成功,下面将对各个标识符的模式简要介绍一下,需要了解具体说明的看客还请移步官网等教程。

  • re.I(re.IGNORECASE)

    • 忽略大小写匹配
  • re.A(re.ASCII)

    • w, W, b, B, d, D, sS 只匹配ASCII,而不是Unicode。在默认匹配模式下w+匹配到了所有字符串,而在ASCII模式下,只匹配到了a、b、c(ASCII编码支持的字符)。
    • 只对字符串匹配模式有效,对字节匹配模式无效。
  • re.S(re.DOTALL)

    • DOT表示.,ALL表示所有,连起来就是.匹配所有,包括换行符n
    • 默认模式下.是不能匹配行符n
  • re.M(re.MULTILINE)

  • 多行模式,当某字符串中有换行符n,默认模式下是不支持换行符特性的,比如:行开头 和 行结尾,而多行模式下是支持匹配行开头的。

    • 正则表达式中^表示匹配行的开头,默认模式下它只能匹配字符串的开头;而在多行模式下,它还可以匹配 换行符n后面的字符。
  • 正则语法中^匹配行开头、A匹配字符串开头,单行模式下它两效果一致,多行模式下A不能识别n

  • re.X( re.VERBOSE)

    • 详细模式,可以在正则表达式中加注解
    • 默认模式下并不能识别正则表达式中的注释,而详细模式是可以识别的。
  • re.L(re.LOCALE)

    • 由当前语言区域决定 w, W, b, B 和大小写敏感匹配,这个标记只能对byte样式有效。
    • 官方不推荐使用
  • re.U (re.UNICODE)

    • 匹配unicode编码支持的字符
    • Python 3 默认字符串已经是Unicode,不推荐使用
  • re.DEBUG

    • 显示编译时的debug信息,了解即可
  • re.T(re.TEMPLATE)

    • disable backtracking(禁用回溯),了解即可

小结

标志位总共有9个常量,其中:

  • 常用:IGNORECASE、ASCII、DOTALL、MULTILINE、VERBOSE
  • 几乎不用:LOCALE、UNICODE、TEMPLATE、DEBUG

re.sub()

该函数用于将匹配到字符串替换成目标字符。

语法:

re.sub(pattern, repl, string, count=0, flags=0)

参数:

  • pattern : 正则中的模式字符串。
  • repl : 替换的字符串,也可为一个函数。
  • string : 要被查找替换的原始字符串。
  • count : 模式匹配后替换的最大次数,默认 0 表示替换所有的匹配。

示例:

text = 'aa<p>asdasdasaaansdghaasdasdsdfs</p>kkkkkkkkkkkkk'
reg = r'<.*?>'
res = re.sub(pattern=reg, string=text, flags=re.S + re.M, repl=' ')
print(res)  

# 打印结果
aa asdasdasaaa
sdghaasdasdsdfs kkkkkkkkkkkkk

re.subn(pattern, repl, string, count=0, flags=0) 函数与 re.sub函数 功能一致,只不过返回一个元组 (字符串, 替换次数)。

re.split()

split 方法按照能够匹配的子串将字符串分割后返回列表,它的使用形式如下:

re.split(pattern, string, maxsplit=0, flags=0)

参数:

参数 描述
pattern 匹配的正则表达式
string 要匹配的字符串
maxsplit 分隔次数,maxsplit=1 分隔一次,默认为 0,不限制次数。
flags 标志位,用于控制正则表达式的匹配方式,如:是否区分大小写,多行匹配等等。参见:正则表达式修饰符 – 可选标志

示例

text = 'aa<p>asdasdasaaansdghaasdasdsdfs</p>kkkkkkkkkkkkk'
reg = r'<.*?>'
res = re.split(pattern=reg, string=text, flags=re.S + re.M)
print(res)  # 因为一开头就匹配不上,将返回None

re.findall()

在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。

注意: match 和 search 是匹配一次 findall 匹配所有。

语法格式为:

re.findall(pattern, string, flags=0)

参数:

  • string : 待匹配的字符串。
  • pos : 可选参数,指定字符串的起始位置,默认为 0。
  • endpos : 可选参数,指定字符串的结束位置,默认为字符串的长度。

使用场景:多用于找到所有目标字段。

实例

str = 'aabbabaabbaa'

# 一个"."就是匹配除 n (换行符)以外的任意一个字符
print(re.findall(r'a.b',str)) # ['aab', 'aab']

# *前面的字符出现0次或以上
print(re.findall(r'a*b',str)) # ['aab', 'b', 'ab', 'aab', 'b']

# 贪婪,匹配从.*前面为开始到后面为结束的所有内容
print(re.findall(r'a.*b',str)) # ['aabbabaabb']

# 非贪婪,遇到开始和结束就进行截取,因此截取多次符合的结果,中间没有字符也会被截取
print(re.findall(r'a.*?b',str))  # ['aab', 'ab', 'aab']

# 非贪婪,与上面一样,只是与上面的相比多了一个括号,只保留括号的内容
print(re.findall(r'a(.*?)b',str))    # ['a', '', 'a']
 
str = '''aabbab
         aabbaa
         bb'''
     # 后面多加了2个b

# 没有把最后一个换行的aab算进来
print(re.findall(r'a.*?b',str)) # ['aab', 'ab', 'aab']

# re.S不会对n进行中断
print(re.findall(r'a.*?b',str,re.S)) 
# ['aab', 'ab', 'aab', 'aan         b']

re.finditer(pattern, string, flags=0用法与re.findall()差不多,但是前者返回的是迭代器,需要对其进行遍历,才能获取数据,后者则直接是个列表,为什么这么设计呢?这里我本人暂时也不清楚,貌似是因为使用finditer时,无需手动将整个正则用()括起来group()代表整个正则的匹配,这里的需求场景比较特殊,后面若深挖,我将继续分享。

re.fullmatch()

re.fullmatch()的返回值re.match()和re.search()一样都是一个SRE_Match object,fullmatch是完全匹配(从字符串开头到结尾),而match()只是从头匹配,但从理论上来说,能用fullmatch实现的,用match也都能实现。

即,fullmatch(pattern, string, flags=0) ,是match函数的完全匹配(从字符串开头到结尾)版本。

示例:

qq = '1234567890'
res01 = re.fullmatch(r'[1-9]d{4,11}', qq)
res02 = re.match(r'^[1-9]d{4,11}$', qq)

print(res01)
print(res02)
# <_sre.SRE_Match object; span=(0, 10), match='1234567890'>
# <_sre.SRE_Match object; span=(0, 10), match='1234567890'>

re.compile()

在Python中使用正则表达式时,re模块内部会干两件事情:

  • 编译正则表达式,如果正则表达式的字符串本身不合法,会报错

  • 用编译后的正则表达式去匹配字符串。

那么如果一个正则表达式要重复使用几千次,出于效率的考虑,应该先把这个正则先预编译好,接下来重复使用时就不再需要编译这个步骤了,直接匹配,提高我们的效率。

re.compile(pattern, flags=0)接受pattern和flags两个参数,使用简单示例如下:

re_telephone = re.compile(r'^(d{3})-(d{3,8})$')  # 编译

A = re_telephone.match('010-12345').groups()  # 使用
print(A)  # 结果 ('010', '12345')
B = re_telephone.match('010-8086').groups()  # 使用
print(B)  # 结果 ('010', '8086')

小结

方法分类

查找一个匹配项

  • re.search()

  • re.match()

  • re.fullmatch()

查找多个匹配项

  • re.findall()

  • re.finditer()

替换

  • re.sub()
  • re.subn()

分割

  • re.split()

编译

  • re.compile()

注意细节

  1. 字节串 与 字符串

    模式和被搜索的字符串既可以是 Unicode 字符串 (str) ,也可以是8位字节串 (bytes)。但是,Unicode 字符串与8位字节串不能混用。

  2. 反斜杠和r的使用

    正则表达式使用反斜杠/来表示特殊形式,或者把特殊字符转义成普通字符。反斜杠在普通的 Python 字符串里也有相同的作用,所以就产生了冲突。解决办法是对于正则表达式样式使用 Python 的原始字符串表示法;在带有 ‘r’ 前缀的字符串字面值中,反斜杠不必做任何特殊处理。

  3. 重复使用某个正则

    如果要重复使用某个正则表达式,可使用re.compile()优化

正则表达式网站

  • 练习网站:https://regexone.com/

    由浅入深,旁边还有提示,刷完后正则表达式的掌握程度就立马上来了!

python正则表达式必知必会的基础
image-20210803152806742
  • 测试网站:https://regexr.com/

    把目标文本粘贴进去,然后在上方输入你的表达式,匹配到的都会高亮显示,且提示非常全,下面还会有匹配表达式的规则解析说明。

python正则表达式必知必会的基础
image-20210803151108189

参考资料

https://zhuanlan.zhihu.com/p/127807805

https://www.runoob.com/python/python-reg-expressions.html

https://regexr.com/

https://regexone.com/


原文始发于微信公众号(豆子前端):python正则表达式必知必会的基础

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/57040.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!