133 python|第六部分:正则表达式

语言: CN / TW / HK

点击关注 「缓缓而行」, 让我们一起探索 python 学习之路~

前言:本篇文章是Python数据管理中的第二部分内容——正则表达式。

目录如下:

01 概述

1.1 定义

1.2 原理

02 元字符使用

2.1 匹配字符

2.2 匹配重复

2.3 匹配位置

2.4 其他

03 匹配规则

     3.1 特殊字符匹配

3.2 正则表达式分组

3.3 贪婪和非贪婪模式

3.4 正则表达式匹配原则

04 re模块

4.1 re模块概述 

    4.2 得到match对象的函数 finditer、search、 match 

4.3 查找多个匹配项 findall finditer

4.4 分割 split

4.5 替换 sub

引 言

我们日常在office、wps办公软件、在python的集成开发环境pycharm等软件中,通过ctrl f /ctrl r 可以实现对文本的搜索、定位和提取功能,大大提高了工作效率,其中的底层原理和正则表达式是有关联的。那让我们来看看什么是正则表达式呢?

01 概述

        

1.1 定 义

  

正则表达式(Regular Expression),通常缩写成 regex、RE 。单数有regexp、regex,复数有regexps、regexes、regexen,是计算机科学的一个概念。 

正则表达式是 使用单个字符串来描述、匹配一系列匹配某个句法规则的字符 。在很多文本编辑器里,通常被用来 检索、替换那些匹配某个模式的文本 。许多程序设计语言都支持利用正则表达式进行字符串操作。

例如,在Perl中就内建了一个功能强大的正则表达式引擎。最早起的RE源于编程语言Perl,大多数编程语言都支持RE。 

维基百科

1.2 原 理

正则表达式是一种文本模式,包括 普通字符 (例如,a 到 z 之间的字母)和 特殊字符 (称为"元字符"),用以 描述一定的字符串规则 ,比如重复、位置等,来表达某类特定的字符串,进而匹配。

可以查看本文参考资料[1]详细了解。

正则表达式的执行过程

图源:猪哥66

正则表达式的两种引擎:

DFA (Deterministic finite automaton) 确定型有穷自动机  

NFA(Non-deterministic finite automaton) 非确定型有穷自动机

正则表达式的引擎执行图

图源:猪哥66

02 元字符使用

2.1 匹配字符

01.元字符使用

                                     

类别:匹配字符
元字符 作用 匹配规则
. 匹配单个字符 匹配除换行外任意一个字符
[...] 匹配字符集 匹配字符集中任意一个字符
[^...] 匹配字符集反集 匹配除了字符集以外的任意一个字符

\d

匹配任意数字字符

\D

匹配任意非数字字符

\w

匹配任意普通字符 注:普通字符指数字、字母、下划线、汉字

\W

匹配任意非普通字符

\s

匹配任意空字符 注:空字符指\r \n \t \v \f

\S

匹配任意非空字符

02.实例

# 先导入re模块,re模块的详细内容在本文第四部分进行详细梳理
import re


#普通字符
result = re.findall('ab','abcdabcdcdjs')
# findall函数是re模块的函数之一,表示匹配目标字符串所有内容,在本文第四部分进行梳理
print(result) #['ab', 'ab']


# . 匹配单个字符
result = re.findall('.角大王','金角大王,银角大王,铜角大王')
print(result)#['金角大王', '银角大王', '铜角大王']


# [...] 匹配字符集
result = re.findall('[A-Z]','Time is Money')
print(result)#['T', 'M']


# [^...] 匹配字符集
result = re.findall('[^A-Z]','Time is Money')
print(result)#['i', 'm', 'e', ' ', 'i', 's', ' ', 'o', 'n', 'e', 'y']


# \d 匹配任意数字字符
result = re.findall('\d{1,6}','邮编:231078, 地址: 快乐星球52号')
print(result)#['231078', '52']


# \D 匹配任意非数字字符
result = re.findall('\D+','https://blog.csdn.net/u014044812')
print(result)#['https://blog.csdn.net/u']


# \w 匹配任意普通字符
result = re.findall('\w+','1+1>2吗')
print(result)#['1', '1', '2吗']


# \W 匹配任意非普通字符
result = re.findall('\W+','1+1>2吗')
print(result)#['+', '>']


# \s 匹配任意空字符
result = re.findall('\w+\s+\w+','叫你一声 你敢答应吗')
print(result)#['叫你一声 你敢答应吗']


# \S 匹配任意非空字符
result = re.findall('\S+','叫你一声 你敢答应吗')
print(result)#['叫你一声', '你敢答应吗']


result = re.findall('\S+\s+\S+','叫你一声 你敢答应吗')
print(result)#['叫你一声 你敢答应吗']

2.2 匹配重复

01.元字符使用

                                     

类别: 匹配重复
元字符 匹配规则
* 匹配前面的字符出现0次或多次
+ 配前面的字符出现1 次或多次
配前面的字符出现0 次或1次
{n}

配前面的字符出现n

{m,n}

配前面的字符出现m-n

写法: { m , n } { m ,}{, n }

注:匹配重复的元字符(除{n}外)会有贪婪模式和非贪婪模式之分,在下文中进行介绍。

02.实例

import re # 导入re模块


1. * 匹配前面的字符出现0次或多次
实例1
result = re.findall('go*','goooooo-g')
print(result)#['goooooo', 'g']


实例2 匹配大写字母开头的单词
result = re.findall('[A-Z][a-z]*','Out of Sight , out of Mind')
print(result)#['Out', 'Sight', 'Mind']


2. + 匹配前面的字符出现1次或多次
实例1
result = re.findall('[0-9]+','2022-01-18')
print(result)#['2022', '01', '18']


实例2
result = re.findall('go+','goooooo-g')
print(result)#['goooooo']


3. ? 前面的字符出现0次或者1
实例1
result = re.findall('go?','goooooo-g')
print(result)#['go', 'g']


实例2 匹配出正负数
result = re.findall('-?[0-9]+','南方的艳阳20°,北方的冰棍-5°')
print(result)#['20', '-5']


4. {n} 匹配出{}前面的字符出现n次
实例1 匹配手机号码(非精细匹配,精细匹配可查阅本文参考资料[3])
result = re.findall('[1][3-9][0-9]{9}','18345678911')
print(result)#['18345678911']


5. {m,n} 匹配前面的字符出现m-n次
实例1 匹配qq号
result = re.findall('[1-9][0-9]{4,}','5678990')
print(result)#['5678990']

2.3 匹配位置

01.元字符使用

                                     

类别:匹配位置
元字符 作用 匹配规则
^ 匹配目标字符串的开头位置
$ 匹配目标字符串的结束位置
\b 匹配单词的边界位置 注:单词边界指的是数字、字母、汉字、下划线和其他字符的交界位置
\B 匹配非单词的边界 位置

02.实例

import re 


1.匹配位置 开头^
result = re.findall('^hi','hi , pei qi')
print(result)#['hi']


2.匹配位置 结尾$
result = re.findall('qi$','hi , pei qi')
print(result)#['qi']


#开头^ 结尾$ 判断用户名是否为 6-12位数字字母下划线构成
result = re.findall('^[_0-9a-zA-Z]{6,12}$','60_peiqi')
print(result)#['60_peiqi']


3.匹配单词边界位置 \b
result = re.findall('is','This is a girl')
print(result) #['is', 'is']


实例1 \b是转义字符,需要在前面加r去掉特殊含义,表示字符本身,和加/同理
result = re.findall(r'\bis','This is a girl')
print(result) #['is'] 表示第2个is


实例2 两边都是单词边界,可以更精确匹配,这个例子就把电话和非电话长度的数字区分开了
result = re.findall(r'\b[1][3-9][0-9]{9}\b','18345678990,1386253429473048324')
print(result)#['18345678990']


4.匹配非单词边界位置 \B不是转义字符 不用加r
result = re.findall('\Bis','This is a girl')
print(result) #['is'] 表示第1个is

2.4 其他

01.元字符使用

                                     

类别:匹配其他
元字符 作用 匹配规则
| 匹配|两侧任意的正则表达式
\ 转义字符 表示正则表达式中的特殊字符原本的含义
( ) 正则表达式分组 下文细讲

02. 或关系实例

import re 


| 或 匹配|两侧任意的正则表达式,如果左侧已经匹配到,就不会继续判断右侧
result = re.findall('http|https','https://www.runoob.com')
print(result)#['http']

03 匹配规则

3.1 特殊字符匹配

如果匹配的目标字符串中 包含正则表达式特殊字符 ,而在表达式中元字符 只想表示其本身含义 时需要进行 \处理

特殊字符有:. * + ? ^ $  / [ ] ( ) |

操作方法:在正则表达式元字符前加\使元字符失去其特殊含义,只表示字符本身。

import re 


\ 表示\后元字符本身含义
# 匹配小数 \.表示小数点,而非任意一个单个字符
result = re.findall('\d+\.?\d*','2.33 1024 0.1')
print(result) #['2.33', '1024', '0.1']


#匹配每天工资 \$表示$,而非字符串的结束位置
result = re.findall('\$\d+','日薪:$267')
print(result)

3.2 正则表达式分组

在正则表达式中,可以 通过( )建立正则表达式的子组 ,改变元字符的操作对象, 进行整体操作

import re 
# +原来只匹配+前面一个字符,加()后,匹配的是()里的所有内容,即ab
result = re.search('(ab)+','abababab')
# search函数是re模块的函数之一,表示匹配目标字符串第一个符合内容,在本文第四部分进行梳理
print(result.group())#abababab


# 如果不加(),|后的 猪 和 狗\w{1,} 会被分别看做两个整体,
# 本题的需求是匹配猪\w{1,} 或 狗\w{1,}
result = re.search('(猪|狗)\w{1,}','猪爸爸,猪妈妈')
print(result.group())#猪爸爸 search只匹配目标字符串第一个符合的内容


#匹配IP地址 把IP分成四段,通过()表示每一段的操作方式都相同
result = re.search('(\d{1,3}\.?){4}','IP 192.168.50.12')
print(result.group()) #192.168.50.12


#匹配身份证(简单匹配)
result = re.search('\d{17}(\d|x)','12345678912345678x')
print(result.group())#12345678912345678x

注:

一个正则表达式可以包含多个子组;子组可以嵌套,但内容不宜过于复杂;子组序列号一般从外到内,从左到右计数。

捕获组:也属于子组,可以通过一个名称来表达子组的意义。

格式:(?P<name>pattern)

import re
#捕获组,对功能没有影响,kind在这里表示动物的种类
result = re.search('(?P<kind>猪|狗)\w{1,}','猪爸爸,猪妈妈')
print(result.group()) # 猪爸爸

3.3 贪婪模式&非贪婪模式

贪婪模式 非贪婪模式
定义 默认情况下,匹配重复的元字符总是 尽可能多地 向后匹配内容 又称为 懒惰模式 ,让匹配重复的元字符 尽可能少地 向后匹配内容
转换
??

贪婪模式→非贪婪模式,只需要在元字符后加?即可。

import re
# 贪婪模式 -->非贪婪
result = re.findall('ab*?','abbbbbbbbc')
print(result)#['a'] *匹配的是目标字符串出现0次或多次,默认匹配多次,加?之后从贪婪转换成非贪婪,匹配0次


#+匹配的是目标字符串出现1次或多次,默认匹配多次,加?之后从贪婪转换成非贪婪,匹配1次
result = re.findall('ab+?','abbbbbbbbc')
print(result)#['ab']


#?匹配的是目标字符串出现0次或1次,默认匹配1次,加?之后从贪婪转换成非贪婪,匹配0次
result = re.findall('ab??','abbbbbbbbc')
print(result)#['a']


#{m,n}匹配的是目标字符串出现m~n次,默认匹配多次,加?之后从贪婪转换成非贪婪,匹配m次
result = re.findall('ab{1,3}?','abbbbbbbbc')
print(result)#['ab']


#非贪婪模式也要在符合规则的前提下进行匹配(+变成非贪婪后,应匹配1次,但还要和c进行匹配,所以继续匹配下去)
result = re.findall('ab+?c','abbbbbbbbc')
print(result)#['abbbbbbbbc']


# 匹配书名
books = "《大明王朝~~ 1566》,《后宫·甄嬛传》,《琅琊榜——风起长林》"
#['《大明王朝~~ 1566》,《后宫·甄嬛传》,《琅琊榜——风起长林》']
result = re.findall('《.+》',books) # 贪婪模式下,所有书一起组成列表的第一个元素


#['《大明王朝~~ 1566》', '《后宫·甄嬛传》', '《琅琊榜——风起长林》']
result = re.findall('《.+?》',books)# 非贪婪模式下,每本书单独作为列表的一个元素
print(result)

3.4 正则表达式匹配原则

4

  1. 正确性,能够正确匹配出目标字符串

  2. 排他性,除了目标字符串之外,尽可能少地匹配其他内容

  3. 全面性,尽可能匹配所有目标字符串,不遗漏

04 python re模块使用

4.1 re模块概述

python的模块分为 四种类型 ,分别是 内置模块,标准库模块,第三方模块和用户自己编写的模块 。在 128 python|第三部分:python高级(一)程序结构和异常处理 中对标准库模块之一的time模块进行了梳理,在 132 python|第五部分:文件管理 中也对标准库模块之一的os模块进行了简单梳理。

re模块也是python的标准库模块之一。re模块一共有12个函数,本文就其中的 search、match、findall、finditer、split、sub 六个函数进行梳理,剩下6个函数可以在本文参考资料[6]进行查阅。

图源:猪哥66

re模块的6个函数

查找一个匹配项
函数名 功能 语法 返回值
search 匹配目标字符串 第一个 符合内容 re.search(pattern,string) 匹配内容 match object
  match  匹配目标字符串 开始位置 re.match(pattern,string) 匹配内容 match object
查找多个匹配项
findall 匹配目标字符串 所有内容 re.findall(pattern,string) 匹配到的内容列表,如果正则表达式有子组,则只能获得子组对应的内容
finditer 匹配目标字符串 所有内容 re.finditer(pattern,string)
分割
split 切割 目标字符串 re.split(pattern,string,max) 切割后的列表
替换
sub 使用一个字符串 替换 匹配到的内容 re.sub(pattern,replace,string,count) 替换后的字符串

注:

pattern:匹配的正则表达式

string:要匹配的字符串

max:最多切割几部分

replace:要替换的字符串

count:最多替换几处,默认全部替换

其中 finditer、 search、   match 3个函数返回的是 match对象 ,即除了匹配正则表达式对应的目标字符串,还会获取更丰富的匹配信息,比如位置信息。

4.2 得到match对象的函数

01.finditer

finditer 的返回值是迭代器,通过for循环遍历,直接打印的结果是三个对象,并非正则表达式直接匹配到的结果。

为了获取match对象更具体的匹配内容,需要用到两个方法:

span() 获取匹配内容的起始位置;

group(n=0)获取match对象的匹配内容

默认值为0表示获取整个match对象内容,如果是序列号或组名表示获取对应子组内容。

返回值:匹配字符串

02.search

#search  第一个符合内容
import re
result = re.search('\d+',"农夫山泉:5,依云:9")
print(result.group())#5


result = re.search('\w+:\d+',"农夫山泉:5,依云:9")
print(result.group())#农夫山泉:5


result = re.search('(\w+):(?P<brand>\d+)',"农夫山泉:5,依云:9")
print(result.group(1))#农夫山泉 通过组序号获取对应内容
print(result.group('brand'))#5 通过组名获取对应内容

02.match

import re


#match 开始位置
result = re.match('\w+','农夫山泉,有点甜')
print(result.group())#农夫山泉

4.2 查找多个匹配项

findall函数实例

需求:打印李玉的身高


import re
data = "李玉:188 ,俞风城:187 ,赵锦辛:189"


#操作1:打印结果为所有人的身高
# result = re.findall("\d{3}",data) #['188', '187', '189']

#操作2:打印结果为李玉的名字和身高
# result = re.findall("李玉:\d{3}",data) #['李玉:188']


#操作3:打印结果为李玉的身高(符合需求)
#如果正则表达式有一个子组,虽然能够匹配到复合目标的整体内容,即李玉:188,但只能返回组中的一部分,即188
# result = re.findall("李玉:(\d{3})",data) # ['188']


#操作4:打印结果为
#如果正则表达式有多个子组,返回的是元组列表,每个子组构成一个元组
result = re.findall("(\w+):(\d{3})",data)
print(result) #[('李玉', '188'), ('俞风城', '187'), ('赵锦辛', '189')]

4.3 分割

split实例

#让字符串更灵活地被分割
datetime = "2022-1-17 11:34:20"
result = re.split('\W+',datetime) # \W+表示非普通字符,本题对应的是- :
print(result)#['2022', '1', '17', '11', '34', '20']

4.4 替换

sub实例

# 文件整理 多空格替换成1个
data = """Stray birds of summer come to my window to sing and fly away.
And yellow leaves of autumn, which have no songs, flutter and fall
there with a sign.
"""
result = re.sub("\s+"," ",data)
print(result)
# Stray birds of summer come to my window to sing and fly away. And yellow leaves of autumn, which have no songs, flutter and fall there with a sign.

下篇文章预告:数据库

                                                                    

参考资料

[1] 正则表达式引擎执行原理

https://blog.csdn.net/u014044812/article/details/104250147

[2] Python正则表达式,这一篇就够了!

https://blog.csdn.net/u014044812/article/details/104822722

[3]  正则表达式在线测试

http://c.runoob.com/front-end/854

[4]  Python3 正则表达式

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

[5] 正则表达式 - 教程

https://www.runoob.com/regexp/regexp-tutorial.html

[6]  常用的python标准库模块

https://www.cnblogs.com/xixi18/p/8406301.html

花样早餐展位