引言
最近看了一些数据分析的岗位笔试题,发现正则匹配还是蛮重要的,然而我每次写正则都是在我的代码库里直接copy过来的hhh,还是没有掌握的很好嗯!!!所以记下这篇笔记来认真地梳理一遍。
未完待续.......
什么是正则表达式
首先,什么是正则表达式?
- 正则表达式本身是一种小型的、高度专业化的编程语言,是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎(C编写的)。
- 正则表达式并不是Python的一部分,但很多编程语言都对它进行了支持,在提供了正则表达式的语言里,正则表达式的语法都是一样的,区别只在于不同的编程语言实现支持的语法数量不同;但不用担心,不被支持的语法通常是不常用的部分。
所以,学习正则表达式是一劳永逸的,一通则百通!
正则表达式与普通是字符串处理具有一定的区别,其主要流程可以大概分为2个步骤:
- 正则表达式引擎会把你所写的正则表达式文本编译成一个正则表达式对象;
- 然后再将这个对象放到待匹配文本中从头到位进行一个匹配,最后返回匹配结果。
正则表达式支持大量的特殊字符和语法来制定正则表达式对象的匹配规则,具体的大家可以直接去python的官网文档查看,因为太多了看了你也会忘记,待会直接上代码看实践来理解吧!
上代码前,我们还需要进行几个重要知识点的了解补充:
- 数量词的贪婪与非贪婪模式
我们知道,正则表达式中 有许多代表数量词的特殊字符,如*、 、{}、?等。这里的贪婪即表示尽可能多的匹配,非贪婪即表示尽可能少的匹配,如我们用“ab*”在“abbbbcd”中匹配,可以找到“abbbb”。这就是贪婪模式下的匹配结果,如果为非贪婪模式下,应匹配找到“ab”。(Python里默认为贪婪模式)
- 反斜杠的困扰
正则表达式里使用‘’作为转义字符,这也就意味着,如果你需要匹配文本中的字符,那么使用编程语言表示的正则表达式里将需要4个反斜杠‘\\’。Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用“r'\'”表示。同样,匹配一个数字的“\d”可以写成“r'd'”。使用原生字符串,既防止漏写了反斜杠,写出来的表达式也更直观。
正则表达式使用
说到正则表达式的使用,很多教程都是噼里啪啦就把特殊字符和语法一顿正上来了,看完后又看了正则的一些函数,瞬间又懵了,这些又是啥?这也是我第一次学正则时的感受,那时的我内心OS:遇到问题了,睡大觉~结果就是后来正则只能靠copy了。所以这里把学习顺序重新编排了一下,用我自己的思路来进行记忆:
首先,我们来看下正则的两种书写方式(命名为本人总结,非官方):
- 函数嵌入式:即直接嵌入在函数中
import re
a = re.findall("樱花", "春天和爱情的樱花")
print(a)
代码语言:javascript复制['樱花']
- 分解式:
import re
# 将正则表达式编译成Pattern对象
pattern = re.compile(r'hi')
# 使用Pattern匹配文本,获得匹配结果,无法匹配时将返回None
match = pattern.match('hi,Sakura')
if match:
# 使用Match获得分组信息
print(match.group())
代码语言:javascript复制hi
很明显,相对来说第一种书写方法是比较方便的,所以推荐大家用第一种来写,当然第二种就比较清晰明了,相对来说解耦性比较强,比较灵活,所以还是有很大部分人采用第二种的,建议可以认真理解下方便以后看懂别人的代码!
下面我们来看一下各种特殊元字符和语法:
接下来,我们来了解一下正则表达式的相关特殊字符:
- ^元字符
字符串开始位置与匹配规则符合就匹配,否则不匹配
^元字符如果写到[]字符集里就是反取
代码语言:javascript复制# ^元字符
a = re.findall("^春天", "春天和爱情的樱花") # 字符串开始位置与匹配规则符合就匹配,否则不匹配
b = re.findall("^樱花", "春天和爱情的樱花") # 字符串开始位置与匹配规则符合就匹配,否则不匹配
c = re.findall("[^a-z]", "春天and爱情的Sakura") # 反取,匹配出除字母外的字符
print(a,b,c)
代码语言:javascript复制['春天'] [] ['春', '天', '爱', '情', '的', 'S']
- $元字符
字符串结束位置与匹配规则符合就匹配,否则不匹配
代码语言:javascript复制# 元字符
a = re.findall("春天", "春天和爱情的樱花") # 字符串开始位置与匹配规则符合就匹配,否则不匹配
b = re.findall("樱花$", "春天和爱情的樱花") # 字符串开始位置与匹配规则符合就匹配,否则不匹配
print(a,b)
代码语言:javascript复制[] ['樱花']
- *元字符
需要字符串里完全符合,匹配规则,就匹配,(规则里的*元字符)前一个字符可以是0个或多个原本字符
匹配前一个字符0或多次,贪婪匹配前导字符有多少个就匹配多少个很贪婪
代码语言:javascript复制# *元字符
# 需要字符串里完全符合,匹配规则,就匹配,(规则里的*元字符)前面的一个字符可以是0或多个原本字符
a = re.findall("樱花*", "春天和爱情的樱花花花")
print(a)
代码语言:javascript复制['樱花花花']
- 元字符
需要字符串里完全符合,匹配规则,就匹配,(规则里的 元字符)前一个字符可以是1个或多个原本字符
匹配前一个字符1次或无限次,贪婪匹配前导字符有多少个就匹配多少个很贪婪
代码语言:javascript复制# 元字符
# 需要字符串里完全符合,匹配规则,就匹配,(规则里的 元字符)前面的一个字符可以是1个或多个原本字符
a = re.findall("樱花 ", "春天樱花和爱情的樱花花花")
print(a)
代码语言:javascript复制['樱花', '樱花花花']
- ?元字符(防止贪婪匹配)
需要字符串里完全符合,匹配规则,就匹配,(规则里的?元字符)前一个字符可以是0个或1个原本字符
匹配前一个字符0次或1次,还可以防止贪婪匹配
代码语言:javascript复制# ?元字符
# 需要字符串里完全符合,匹配规则,就匹配,(规则里的?元字符)前面的一个字符可以是0个或1个原本字符
a = re.findall("樱花?", "春天樱和爱情的樱花花花")
print(a)
代码语言:javascript复制['樱', '樱花']
- {}元字符(范围)
需要字符串里完全符合,匹配规则,就匹配,(规则里的 {} 元字符)前面的一个字符,是自定义字符数,位数的原本字符
{m}匹配前一个字符m次,{m,n}匹配前一个字符m至n次,若省略n,则匹配m至无限次
{0,}匹配前一个字符0或多次,等同于*元字符{ ,}匹配前一个字符1次或无限次,等同于 元字符{0,1}匹配前一个字符0次或1次,等同于?元字符
代码语言:javascript复制# {}元字符
# {m}匹配前一个字符m次,{m,n}匹配前一个字符m至n次,若省略n,则匹配m至无限次
a = re.findall("樱花{3}", "春天樱和爱情的樱花花花")
print(a)
代码语言:javascript复制['樱花花花']
- []元字符(字符集)
需要字符串里完全符合,匹配规则,就匹配,(规则里的 [] 元字符)对应位置是[]里的任意一个字符就匹配
字符集。对应的位置可以是字符集中任意字符。字符集中的字符可以逐个列出,也可以给出范围,如[abc]或[a-c]。[^abc]表示取反,即非abc。所有特殊字符在字符集中都失去其原有的特殊含义。用反斜杠转义恢复特殊字符的特殊含义。
代码语言:javascript复制# []元字符
# 需要字符串里完全符合,匹配规则,就匹配,(规则里的 [] 元字符)对应位置是[]里的任意一个字符就匹配
a = re.findall("[春天,爱情]的樱花", "春天的樱花和爱情的樱花花花")
print(a)
代码语言:javascript复制['天的樱花', '情的樱花']
- d
匹配任何十进制数,它相当于类[0-9]
代码语言:javascript复制# d
a = re.findall("d", "春天的4樱花和爱情的8樱花花花") # d匹配任何十进制数,它相当于类[0-9]
print(a)
代码语言:javascript复制['4', '8']
- d
匹配一位或者多位数的数字时用
代码语言:javascript复制# d
a = re.findall("d", "春天的4222樱花和爱情的8樱花花花") # d匹配任何十进制数,它相当于类[0-9]
print(a)
代码语言:javascript复制['4222', '8']
- D
匹配任何非数字字符,它相当于类[^0-9]
代码语言:javascript复制# D
a = re.findall("D", "春天的4222樱花和爱情的8樱花花花") # d匹配任何十进制数,它相当于类[0-9]
print(a)
代码语言:javascript复制['春', '天', '的', '樱', '花', '和', '爱', '情', '的', '樱', '花', '花', '花']
- s
匹配任何空白字符,它相当于类[tnrfv]
代码语言:javascript复制# s
# s匹配任何空白字符,它相当于类[tnrfv]
a = re.findall("s", "春天的4222樱 花和爱情的8樱n花花花")
print(a)
代码语言:javascript复制[' ', ' ', 'n']
- S
匹配任何非空白字符,它相当于类[^tnrfv]
代码语言:javascript复制# S
# S匹配任何空白字符,它相当于类[^tnrfv]
a = re.findall("S", "春天的4222樱 花和爱情的8樱n花花花")
print(a)
代码语言:javascript复制['春', '天', '的', '4', '2', '2', '2', '樱', '花', '和', '爱', '情', '的', '8', '樱', '花', '花', '花']
- w
匹配包括下划线在内任何字母数字字符,它相当于类[a-zA-Z0-9_]
代码语言:javascript复制# w
# w匹配包括下划线在内任何字母数字字符,它相当于类[a-zA-Z0-9_]
a = re.findall("w","https://www.wujunchao.top")
print(a)
代码语言:javascript复制['h', 't', 't', 'p', 'w', 'w', 'w', 'w', 'u', 'j', 'u', 'n', 'c', 'h', 'a', 'o', 't', 'o', 'p']
- W
匹配非任何字母数字字符包括下划线在内,它相当于类[^a-zA-Z0-9_]
代码语言:javascript复制# W
# W匹配包括下非划线在内任何字母数字字符,它相当于类[^a-zA-Z0-9_]
a = re.findall("W","https://www.wujunchao.top")
print(a)
代码语言:javascript复制[':', '/', '/', '.', '.']
- ()元字符(分组)
也就是分组匹配,()里面的为一个组也可以理解成一个整体
如果()后面跟的是特殊元字符如 (adc)* 那么*控制的前导字符就是()里的整体内容,不再是前导一个字符
代码语言:javascript复制# ()元字符(分组)
# 也就是分组匹配,()里面的为一个组也可以理解成一个整体
a = re.search("(樱花) ", "春天樱花樱花和爱情的花花") # 匹配一个或多个a4
b = a.group()
print(b)
# 也就是分组匹配,()里面的为一个组也可以理解成一个整体
# 匹配 (a) (d0-9的数字) ( 可以是1个到多个0-9的数字)
a = re.search("樱(d )", "春天樱215花和爱情的樱82花花花")
b = a.group()
print(b)
代码语言:javascript复制樱花樱花
樱215
- |元字符(或)
|或,或就是前后其中一个符合就匹配
代码语言:javascript复制# |元字符(或)
import re
a = re.findall(r"樱|花", "春天樱215花和爱情的樱82花花花") # |或,或就是前后其中一个符合就匹配
print(a)
代码语言:javascript复制['樱', '花', '樱', '花', '花', '花']
正则表达式常用函数
由于笔者最近时间安排较紧,只是把函数列出来介绍,之后有时间再具体研究下详细的代码实现和一些坑位提醒!
re.compile(Pattern,flags)函数
这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象。 第二个参数flag是匹配模式,取值可以使用按位或运算符'|'表示同时生效,比如re.I | re.M。另外,你也可以在regex字符串中指定模式,比如re.compile('pattern', re.I | re.M)与re.compile('(?im)pattern')是等价的。
re.match(pattern, string, flags)函数(常用)
match,从头匹配一个符合规则的字符串,从起始位置开始匹配,匹配成功返回一个对象,未匹配成功返回Nonematch(pattern, string, flags=0)
- pattern: 正则模型
- string : 要匹配的字符串
- falgs : 匹配模式
注意:match()函数 与 search()函数基本是一样的功能,不一样的就是match()匹配字符串开始位置的一个符合规则的字符串,search()是在字符串全局匹配第一个合规则的字符串
re.search(pattern, string, flags)函数
search,浏览全部字符串,匹配第一符合规则的字符串,浏览整个字符串去匹配第一个,未匹配成功返回None
search(pattern, string, flags=0)
- pattern: 正则模型
- string : 要匹配的字符串
- falgs : 匹配模式
注意:match()函数 与 search()函数基本是一样的功能,不一样的就是match()匹配字符串开始位置的一个符合规则的字符串,search()是在字符串全局匹配第一个合规则的字符串
re.findall(pattern, string, flags)函数(常用)
findall(pattern, string, flags=0)
- pattern: 正则模型
- string : 要匹配的字符串
- falgs : 匹配模式
浏览全部字符串,匹配所有合规则的字符串,匹配到的字符串放到一个列表中,未匹配成功返回空列表
注意:一旦匹配成,再次匹配,是从前一次匹配成功的,后面一位开始的,也可以理解为匹配成功的字符串,不在参与下次匹配
re.split(pattern, string, maxsplit)函数
根据正则匹配分割字符串,返回分割后的一个列表
split(pattern, string, maxsplit=0, flags=0)
- pattern: 正则模型
- string : 要匹配的字符串
- maxsplit:指定分割个数
- flags : 匹配模式
按照一个字符将全部字符串进行分割
re.sub(pattern, repl, string, count)
替换匹配成功的指定位置字符串
sub(pattern, repl, string, count=0, flags=0)
- pattern: 正则模型
- repl : 要替换的字符串
- string : 要匹配的字符串
- count : 指定匹配个数
- flags : 匹配模式
re.subn(pattern, repl, string, count ,flags)函数
替换匹配成功的指定位置字符串,并且返回替换次数,可以用两个变量分别接受
subn(pattern, repl, string, count=0, flags=0)
- pattern: 正则模型
- repl : 要替换的字符串
- string : 要匹配的字符串
- count : 指定匹配个数
- flags : 匹配模式
参考来源:
https://docs.python.org/zh-cn/3/library/re.html
https://www.cnblogs.com/nickchen121/p/10808645.html