python 正则使用详解

news/2024/12/30 1:30:06/

python 正则使用详解

  • 什么是正则
  • 在 python 中使用正则
    • 一些正则的定义
    • python 正则的方法
      • match 从字符串开头匹配
        • 正则返回的结果分析(重要)
      • fullmatch 严格匹配整个字符串
      • search 任意位置开始匹配
      • sub 替换匹配内容
      • subn 以元组方式返回替换结果
      • split 正则切割
      • findall 查找所有匹配结果
      • finditer 返回一个迭代结果
      • compile 正则表达式的预编译
      • purge 清除缓存
      • escape 正则表达式格式化

什么是正则

正则很简单的哦,不要因为看到和乱码差不多的样子就望而却步了,他很有规律的。

简单来说,就是用来描述字符串格式的,比如数字就可以是 [0-9],也可以是 [0123456789],还可以是 \d

而日常我们使用正则,则就是用这个描述格式来验证或匹配提取或批量替换用的,比如 html 代码,用 <[^<>]*?> 来匹配或删除所有标签

关于正则的入门知识,可以围观一下老顾的文章《文盲的正则表达式入门》,在这个文章里,老顾以正则各种符号使用的场景用途做了区分,和其他正则入门的教程不一样哦,当然,也没那么完整就是了。这个文章主要是用来方便理解正则的。

在 python 中使用正则

大部分的开发语言中,都有对正则的支持,毕竟,这也是字符验证的一个主要流派了,甚至不少数据库、命令行都有正则的支持,比如 linux系统,比如 mysql、oracle 数据库等,所以正则还是有必要学一学的。

在 python 中,正则的内容则放在 re 这个包里,使用也很简单,只要引用这个包就可以了

import re
print(re.findall('\d','123456 78/*9')) # 一个简单的例子,找出字符串中所有数字

在这里插入图片描述
在 python 的 re 包里,他提供了不少的指令,咱们来一一查看并学习,为了不遗漏内容,咱们把 re.py 这个文件打开,照着这个文档的内容来进行。文件位置如图,在 python 安装路径下的 lib 文件夹内。
在这里插入图片描述

一些正则的定义

在 re.py 这个文件中,从第29行开始,他简单的介绍了一些基本的特殊符号定义

The special characters are:
特殊字符为:"."      Matches any character except a newline.匹配除换行符以外的任何字符。"^"      Matches the start of the string.匹配字符串的开头。"$"      Matches the end of the string or just before the newline at the end of the string.匹配字符串的末尾或位于的换行符之前新行的末端。"*"      Matches 0 or more (greedy) repetitions of the preceding RE.Greedy means that it will match as many repetitions as possible.匹配0个或多个(默认是贪婪模式)由前边的正则片段定义的重复内容。贪婪模式意味着它会匹配尽可能多的重复。"+"      Matches 1 or more (greedy) repetitions of the preceding RE.匹配1个或多个(默认是贪婪模式)由前边的正则片段定义的重复内容。"?"      Matches 0 or 1 (greedy) of the preceding RE.匹配0个或1个(默认是贪婪模式)由前边的正则片段定义的内容。*?,+?,?? Non-greedy versions of the previous three special characters.前边三个定义的非贪婪版本。(老顾的正则入门里说过的哦,+*? 是长度定义,在长度定义后加 ? 就表示非贪婪模式,这个可以引用到任何长度定义中,包括下边也有出现){m,n}    Matches from m to n repetitions of the preceding RE.匹配至少 m 个,最多 n 个由前边正则片段定义的内容。默认是贪婪模式{m,n}?   Non-greedy version of the above.上边长度定义的非贪婪版本。"\\"     Either escapes special characters or signals a special sequence.要么转义特殊字符,要么就是一个已经预定义的特殊序列。(好吧,老顾也不知道怎么准确翻译,这里的特殊序列指的就是预定义的字符集,比如\d表示数字,\n表示回车)[]       Indicates a set of characters.A "^" as the first character indicates a complementing set.表示一组字符。(也就是老顾说的字符集,比如 \d 是已经预定义好的,但是是全部数字,也许我们只需要123,不需要其他数字,那么就必须自己定义字符集 [123] 这个样子)作为第一个字符的“^”表示一个补集。(在字符集定义中,如果 ^ 是第一个字符,则表示这个是个补集,换个说法,就是不包含之后定义的字符集的其他字符集,比如 [^\d] 就是所有非数字的字符)"|"      A|B, creates an RE that will match either A or B.A|B,创建一个匹配A或B的正则片段。(...)    Matches the RE inside the parentheses.The contents can be retrieved or matched later in the string.匹配括号内的正则片段。稍后可以在字符串中检索或匹配内容。(事实上,除了稍后在其他地方使用,在当前正则片段后的部分也可以使用)(?aiLmsux) The letters set the corresponding flags defined below.字母设置下面定义的相应标志。(这个用法老顾还没试过,翻译为百度翻译的结果)(?:...)  Non-grouping version of regular parentheses.正则圆括号的非分组版本。(也就是不可引用、追溯的部分)(?P<name>...) The substring matched by the group is accessible by name.组匹配的子字符串可以通过名称访问。(给分组定个名字,然后通过名字访问)(?P=name)     Matches the text matched earlier by the group named name.匹配前面由组名称匹配的文本。(?#...)  A comment; ignored.一个注释,忽略掉。(嗯,我讨厌写注释)(?=...)  Matches if ... matches next, but doesn't consume the string.右断言,位置修饰,如果能匹配成功则匹配之后的正则片段,该修饰不占用匹配空间。(?!...)  Matches if ... doesn't match next.右断言,与上一个修饰意思相反,如果不符合之后的正则片段,则继续匹配。(?<=...) Matches if preceded by ... (must be fixed length).左断言,如果前边的字符符合正则片段...(必须定长)(?<!...) Matches if not preceded by ... (must be fixed length).左断言,如果前边的字符不符合正则片段...(必须定长)(?(id/name)yes|no) Matches yes pattern if the group with id/name matched, the (optional) no pattern otherwise.如果具有id/name的组匹配,则匹配yes模式,否则为(可选)无模式。(同样没用过的方法)

通过这个内容,我们基本就可以确定 python 中,正则能完成哪些描述,这样的描述又有什么样的限制了。

再之后,从62 行开始,还介绍了一些预定义字符集,这里就不翻译了,大家随便看看就好,基本都差不多。

再然后,87 行开始,说明了, re 这个包之中包含的各种方法,嗯,之后一一讲解

以及102行开始的,正则修饰。。。。老顾最常用的就是忽略大小写修饰,这个修饰是在正则定义之外的,还有多行匹配也偶尔用用,其他的说实话,老顾基本用不上。

python 正则的方法

约定,之后的所有内容,默认都已经引入了 re 包,即,代码中已经包含了 import re

match 从字符串开头匹配

Match a regular expression pattern to the beginning of a string.
将正则表达式模式与字符串的开头匹配。

语法:
match(pattern, string, flags=0)
match(正则表达式,匹配字符串,正则表达式修饰(默认为0))

需要注意,这个方法时严格从字符串开头进行匹配的,如果想在字符串中间找到匹配,不要用这个方法哦。

示例:

print(re.match('\d+','123.548'))
# 返回结果
<re.Match object; span=(0, 3), match='123'>
print(re.match('\s\d+','123.548'))
# 返回结果,匹配失败,没有Match信息
None

在python中,使用匹配返回的结果,大部分都是 re.Match 对象,他包含了一个 span 元组,这个元组就是匹配字符串对应的切片位置,match 就是匹配的结果了

正则返回的结果分析(重要)

ans = re.match('\d+','123.548')
print(ans)
<re.Match object; span=(0, 3), match='123'>

我们通过一个变量,来分析返回的结果该如何使用

print(ans.re) # 输出这个匹配的正则
re.compile('\\d+') # 包含了我们正则片段的正则编译对象
print(ans.string) # 输出这个匹配的原始匹配字符串
123.548
print(ans.pos,ans.endpos) # 输出原始字符串的切片,包括起始位置和结束位置
0 7
print(ans.regs) # 输出匹配的结果,注意,这里是元组,每一个内层元组是一个匹配
((0, 3),)
print(ans.span) # 输出span方法
<built-in method span of re.Match object at 0x00000148614E76C0>
print(ans.span(0)) # 输出第一个匹配的切片信息
(0, 3)
print(ans.start(0),ans.end(0)) # 返回指定匹配顺序的切片开始和结束位置
0 3
print(ans.group(0)) # 返回第一个匹配结果匹配到的内容
123

嗯,由于刚才的匹配,我们简单的匹配了一个 \d+,没有带分组信息,这次我们加上一个分组来尝试

ans = re.match('(\d+)\.(\d+)','123.548') # 带有两个分组的正则匹配
print(ans)
<re.Match object; span=(0, 7), match='123.548'>
print(ans.span(0),ans.span(1),ans.span(2)) # 返回所有的切片信息
#这里需要注意的是,如果带有分组,那么原始字符串自动占据返回结果的第一个分组,即下标为0的分组
#所以,实时上我们需要提取的结果,应该是从下标1开始的
(0, 7) (0, 3) (4, 7)
print(ans.group(0),ans.group(1),ans.group(2)) # 输出该匹配结果的分组结果
123.548 123 548
print(ans.groups()) # 输出该匹配的分组内容,注意,这里的groups没有参数,输入参数也是无效的
# 所以想要引用结果,必须使用 group
('123', '548')
print(ans.groups(101))
('123', '548')
print(ans.expand('\\1 is \\2')) # 将匹配结果按照指定的字符串格式输出,这里可以引用分组结果
123 is 548

好了,经过这几个输出,我们应该明白了正则匹配结果返回的内容都包含哪些信息了,还有个别的属性方法老顾也不太清楚,这里就不再讲解了

有了这些方法和属性,我们就可以很方便的使用正则返回结果中的内容了

fullmatch 严格匹配整个字符串

Match a regular expression pattern to all of a string.
将正则表达式模式与所有字符串匹配。

语法:
fullmatch(pattern, string, flags=0)
fullmatch(正则表达式,匹配字符串,正则表达式修饰(默认为0))

print(re.fullmatch('[a-z]+','0123456')) # 由于没有字母,匹配失败
None
print(re.fullmatch('[a-z]+','Abcd efgh',re.IGNORECASE)) # 虽然忽略大小写了,但是中间有空格,不能严格匹配,匹配失败
None
print(re.fullmatch('\d+','0123456')) # 整个字符串严格匹配 \d+
<re.Match object; span=(0, 7), match='0123456'>
print(re.fullmatch('[a-z]+','Abcdefgh',re.IGNORECASE)) # 整个字符串严格匹配 [a-z]+
<re.Match object; span=(0, 8), match='Abcdefgh'>
print(re.fullmatch('[a-z]','Abcdefgh',re.IGNORECASE)) # 长度对应不上,没有长度修饰则只匹配一个字符的字符串才可以成功
None

嗯,就是严格匹配字符串首尾,默认相当于 search 的正则前边带 ^ 后边带 $

search 任意位置开始匹配

Search a string for the presence of a pattern.
在字符串中搜索是否存在模式。

语法:
search(pattern, string, flags=0)
search(正则表达式,匹配字符串,正则表达式修饰(默认为0))

print(re.search('[a-z]+','0123456')) # 没有匹配到结果,返回 None
None
print(re.search('[a-z]+','Abcd efgh',re.IGNORECASE)) # 返回第一个匹配成功的结果片段
<re.Match object; span=(0, 4), match='Abcd'>
print(re.search('\d+','abcde0123456')) # 返回第一个匹配成功的结果片段
<re.Match object; span=(5, 12), match='0123456'>
print(re.search('[a-z]+','Abcdefgh',re.IGNORECASE)) # 整个字符串都符合正则,所以完整返回了
<re.Match object; span=(0, 8), match='Abcdefgh'>
print(re.search('[a-z]','Abcdefgh',re.IGNORECASE)) # 返回第一个匹配成功的片段
<re.Match object; span=(0, 1), match='A'>

很明显,search 要比 match 和 fullmatch 要宽松很多,我们可以更自由的发挥

sub 替换匹配内容

Substitute occurrences of a pattern found in a string.
替换字符串中发现的模式的出现次数。(百度翻译的什么鬼?)
替换字符串中所有或指定次数的符合匹配的内容。(老顾翻译)

语法:
sub(pattern, repl, string, count=0, flags=0)
sub(正则表达式,替换表达式,匹配字符串,替换次数,正则表达式修饰(默认为0))

print(re.sub('(\d{2})','\\1-','18300183000')) # 将数字每两位之后加一个减号
18-30-01-83-00-0
----
print(re.sub('(?=\d+)(?<!\d)','\n','1开始2结束3运行4调试11开会12报告')) # 在每个连续数字前插入一个换行,大家可以和下一个比较一下有什么不同1开始
2结束
3运行
4调试
11开会
12报告
----
print(re.sub('(?=\d+)','\n','1开始2结束3运行4调试11开会12报告')) # 在每个数字前,插入一个换行1开始
2结束
3运行
4调试
1
1开会
1
2报告
----
print(re.sub('(?=\d+)(?<!\d)','\n','1开始2结束3运行4调试11开会12报告',count=5)) # 指定了替换次数的正则替换1开始
2结束
3运行
4调试
11开会12报告

正则替换还是很好用的,但是,这里要说一个但是,python 的正则替换里描述的内容,有一个很重要的部分,很多人可能都忽略掉了。原文如下

def sub(pattern, repl, string, count=0, flags=0):"""Return the string obtained by replacing the leftmostnon-overlapping occurrences of the pattern in string by thereplacement repl.  repl can be either a string or a callable;if a string, backslash escapes in it are processed.  If it isa callable, it's passed the Match object and must returna replacement string to be used."""return _compile(pattern, flags).sub(repl, string, count)

repl can be either a string or a callable;
repl可以是字符串,也可以是可调用的
If it is a callable, it’s passed the Match object and must return a replacement string to be used.
如果是可调用的的话,它传递Match对象,并且必须返回要使用的替换字符串。

也就是说,我可以通过委托的方法,来处理匹配到的内容,并返回指定的结果
来,我们试试看哦

def star(n): # 这里的 n 就是一个正则匹配对象Matchreturn '*' * int(n.group(0)) + '\n' # 将匹配到的数字转成星星的数量,并返回星星print(re.sub('\d',star,'123456789')) # 委托匹配到的内容使用 star 方法来处理
*
**
***
****
*****
******
*******
********
*********
import random
def star(n):return ''.join([random.choice('abcdefghijklmnopqrstuvwxyz') for _ in range(int(n.group(0)))]) + '\n'print(re.sub('\d',star,'123454321'))
v
sr
lvd
trtm
rnhrq
loxb
vjd
su
i

以及,老顾在《在学习爬虫的路上,有多少坑在前边》一文中,实际用到的两个转编码的函数

正则替换如此强大,你还不心动吗?

subn 以元组方式返回替换结果

Same as sub, but also return the number of substitutions made.
与sub相同,但也返回进行的替换次数。(百度翻译持续不说人话)
和 sub 方法一样,但是,在元组中,记录了替换的次数。(老顾翻译)

语法:
subn(pattern, repl, string, count=0, flags=0)
subn(正则表达式,替换表达式,匹配字符串,替换次数,正则表达式修饰(默认为0))

print(re.subn('(?=\d+)(?<!\d)','\n','1开始2结束3运行4调试11开会12报告'))
('\n1开始\n2结束\n3运行\n4调试\n11开会\n12报告', 6) # 元组第一个元素为返回结果,第二个元素为替换次数def star(n):return '*' * int(n.group(0)) + '\n'print(re.subn('\d',star,'13243546576879'))
('*\n***\n**\n****\n***\n*****\n****\n******\n*****\n*******\n******\n********\n*******\n*********\n', 14)

subn 就不细说了,和sub一样的玩法

split 正则切割

Split a string by the occurrences of a pattern.
按出现的模式拆分字符串。

语法:
split(pattern, string, maxsplit=0, flags=0)
split(正则表达式,匹配字符串,最大切割次数(默认0,即不限制次数),正则表达式修饰(默认为0))

print(re.split('[^\d]','13 243,54uu6576879')) # 以非数字的字符为切割符,切割字符串
['13', '243', '54', '', '6576879'] # 由于两个 u 之间没有数字,所以结果中会出现空字符串print(re.split('[^\d]','13 243,54uu6576879',maxsplit=2)) # 指定最大切割次数的分割字符串
['13', '243', '54uu6576879']

嗯。。。。很常用实用以及很容易用,不细说了

需要注意的是,这里返回的就是字符串数组,没有Match信息了

findall 查找所有匹配结果

Find all occurrences of a pattern in a string.
查找字符串中出现的所有模式。

语法:
findall(pattern, string, flags=0)
findall(正则表达式,匹配字符串,正则表达式修饰(默认为0))

额。。。怎么说呢,在抛弃了Match信息的情况下,以字符串数组方式返回所有匹配

print(re.findall('\d+','13 243,54uu6576879'))
['13', '243', '54', '6576879']

在大部分的工作中,其实这个也就够用了,因为我们并不关心他出现在什么地方,前后文有什么,但在一些需求比较复杂的时候,这个 findall 就不如 finditer 了,因为 findall 丢失了Match信息,而finditer还保留着Match信息

finditer 返回一个迭代结果

Return an iterator yielding a Match object for each match.
返回一个迭代器,为每个匹配生成一个Match对象。

语法:
finditer(pattern, string, flags=0)
finditer(正则表达式,匹配字符串,正则表达式修饰(默认为0))

print(re.finditer('\d+','13 243,54uu6576879')) # 返回了一个迭代器
<callable_iterator object at 0x00000148615CD540>for m in re.finditer('\d+','13 243,54uu6576879'): #使用迭代,将所有匹配输出print(m)
<re.Match object; span=(0, 2), match='13'>  # 第一个匹配
<re.Match object; span=(3, 6), match='243'> # 第二个匹配
<re.Match object; span=(7, 9), match='54'>  # 第三个匹配
<re.Match object; span=(11, 18), match='6576879'> # 第四个匹配

由于这里完整的保留了Match的信息,所以我们可以对匹配到的结果进行定位,以及检查前后文之类的操作,需要用的人自然会用到,也就不细说了

compile 正则表达式的预编译

Compile a pattern into a Pattern object.
将模式编译为pattern对象。

语法:
compile(pattern, flags=0)

嗯。。。定义一个正则,也许复杂的正则会用到这个,老顾一般都直接写正则片段,除了上了1k的正则,老顾一般不会用这个

purge 清除缓存

Clear the regular expression cache.
清除正则表达式缓存。

语法:
purge()

没啥用,这是针对预编译的正则来说的,老顾基本上都是随手写正则随手用,没有预编译过

escape 正则表达式格式化

Backslash all non-alphanumerics in a string.
对正则表达式字符串中的所有非字母数字进行转义。

语法:
escape(pattern)

这个还算有点用,算是正则格式化的部分,比如我们想从字符串中提取到一部分作为匹配内容,但是匹配到的内容可能会有特殊符号,比如*号,?号之类的,这些在正则中有其他意义,所以我们需要提前将这些符号转义,escape就是干这个用的,将特殊符号都转义。老顾在 c# 中使用正则,还单独自己写了个FormatRegex 方法,就是干这个用的。郁闷。

print(re.escape('123.456*789=$100')) # 格式化正则片段,将其特殊符号都进行转义
123\.456\*789=\$100

http://www.ppmy.cn/news/32489.html

相关文章

蓝桥杯刷题冲刺 | 倒计时24天

作者&#xff1a;指针不指南吗 专栏&#xff1a;蓝桥杯倒计时冲刺 &#x1f43e;马上就要蓝桥杯了&#xff0c;最后的这几天尤为重要&#xff0c;不可懈怠哦&#x1f43e; 文章目录1.修剪灌木2.统计子矩阵1.修剪灌木 题目 链接&#xff1a; 修剪灌木 - 蓝桥云课 (lanqiao.cn) 找…

震撼,支持多模态模型的ChatGPT 4.0发布了

最近几个月&#xff0c;互联网和科技圈几乎ChatGPT刷屏了&#xff0c;各种关于ChatGPT的概念和应用的帖子也是围绕在周围。当去年年底ChatGPT发布的那几天&#xff0c;ChatGPT确实震撼到了所有人&#xff0c;原来AI还可以这么玩&#xff0c;并且对国内的那些所谓的人工智能公司…

Kubernetes详细安装

By&#xff1a;雪月三十 参考&#xff1a; https://blog.csdn.net/qq_43580215/article/details/125153959 https://juejin.cn/post/6844903943051411469 https://mp.weixin.qq.com/s?__bizMzI0MDQ4MTM5NQ&mid2247502359&idx1&sn8c16100c9731359b9864403183f44233…

C语言/动态通讯录

本文使用了malloc、realloc、calloc等和内存开辟有关的函数。 文章目录 前言 二、头文件 三、主界面 四、通讯录功能函数 1.全代码 2.增加联系人 3.删除联系人 4.查找联系人 5.修改联系人 6.展示联系人 7.清空联系人 8.退出通讯录 总结 前言 为了使用通讯录时&#xff0c;可以…

哈佛与冯诺依曼结构

1. 下图是典型的冯诺依曼结构 2. CPU分为三部分&#xff1a;ALU运算单元&#xff0c;CU控制单元&#xff0c;寄存器组。 3. 分析51单片机为何能使用汇编进行编程 51指令集&#xff08;Instruction Set&#xff09;是单片机CPU能够执行的所有指令的集合。在编写51单片机程序时&a…

线程池的使用:如何写出高效的多线程程序?

目录1.线程池的使用2.编写高效的多线程程序Java提供了Executor框架来支持线程池的实现&#xff0c;通过Executor框架&#xff0c;可以快速地创建和管理线程池&#xff0c;从而更加方便地编写多线程程序。 1.线程池的使用 在使用线程池时&#xff0c;需要注意以下几点&#xff…

Spark SQL支持DataFrame操作的数据源

DataFrame提供统一接口加载和保存数据源中的数据&#xff0c;包括&#xff1a;结构化数据、Parquet文件、JSON文件、Hive表&#xff0c;以及通过JDBC连接外部数据源。一个DataFrame可以作为普通的RDD操作&#xff0c;也可以通过&#xff08;registerTempTable&#xff09;注册成…

【基础算法】单链表的OJ练习(5) # 环形链表 # 环形链表II # 对环形链表II的解法给出证明(面试常问到)

文章目录前言环形链表环形链表 II写在最后前言 本章的OJ练习相对于OJ练习(4)较为简单。不过&#xff0c;本章的OJ最重要的是要我们证明为何可以这么做。这也是面试中常出现的。 对于OJ练习(4)&#xff1a;-> 传送门 <-&#xff0c;分割链表以一种类似于归并的思想解得&a…