目录

初识 Python 正则表达式

正则表达式是一个特殊的字符序列,用于判断一个字符串是否与我们所设定的字符序列是否匹配,也就是说检查一个字符串是否与某种模式匹配。

Python 自 1.5 版本起增加了re 模块,它提供 Perl 风格的正则表达式模式。re 模块使 Python 语言拥有全部的正则表达式功能。

下面通过实例,一步一步来初步认识正则表达式。

比如在一段字符串中寻找是否含有某个字符或某些字符,通常我们使用内置函数来实现,如下:

# 设定一个常量
a = '两点水|twowater|liangdianshui|草根程序员|ReadingWithU'# 判断是否有 “两点水” 这个字符串,使用 PY 自带函数print('是否含有“两点水”这个字符串:{0}'.format(a.index('两点水') > -1))
print('是否含有“两点水”这个字符串:{0}'.format('两点水' in a))
复制代码

输出的结果如下:

是否含有“两点水”这个字符串:True是否含有“两点水”这个字符串:True复制代码

那么,如果使用正则表达式呢?

刚刚提到过,Python 给我们提供了 re 模块来实现正则表达式的所有功能,那么我们先使用其中的一个函数:

re.findall(pattern, string[, flags])
复制代码

该函数实现了在字符串中找到正则表达式所匹配的所有子串,并组成一个列表返回,具体操作如下:


import re# 设定一个常量
a = '两点水|twowater|liangdianshui|草根程序员|ReadingWithU'# 正则表达式findall = re.findall('两点水', a)
print(findall)if len(findall) > 0:print('a 含有“两点水”这个字符串')
else:print('a 不含有“两点水”这个字符串')复制代码

输出的结果:

['两点水']a 含有“两点水”这个字符串复制代码

从输出结果可以看到,可以实现和内置函数一样的功能,可是在这里也要强调一点,上面这个例子只是方便我们理解正则表达式,这个正则表达式的写法是毫无意义的。为什么这样说呢?

因为用 Python 自带函数就能解决的问题,我们就没必要使用正则表达式了,这样做多此一举。而且上面例子中的正则表达式设置成为了一个常量,并不是一个正则表达式的规则,正则表达式的灵魂在于规则,所以这样做意义不大。

那么正则表达式的规则怎么写呢?先不急,我们一步一步来,先来一个简单的,找出字符串中的所有小写字母。首先我们在 findall 函数中第一个参数写正则表达式的规则,其中 [a-z] 就是匹配任何小写字母,第二个参数只要填写要匹配的字符串就行了。具体如下:


import re# 设定一个常量
a = '两点水|twowater|liangdianshui|草根程序员|ReadingWithU'# 选择 a 里面的所有小写英文字母re_findall = re.findall('[a-z]', a)print(re_findall)复制代码

输出的结果:

['t', 'w', 'o', 'w', 'a', 't', 'e', 'r', 'l', 'i', 'a', 'n', 'g', 'd', 'i', 'a', 'n', 's', 'h', 'u', 'i', 'e', 'a', 'd', 'i', 'n', 'g', 'i', 't', 'h']复制代码

这样我们就拿到了字符串中的所有小写字母了。

字符集

好了,通过上面的几个实例我们初步认识了 Python 的正则表达式,可能你就会问,正则表达式还有什么规则,什么字母代表什么意思呢?

其实,这些都不急,在本章后面会给出对应的正则表达式规则列表,而且这些东西在网上随便都能 Google 到。所以现在,我们还是进一步加深对正则表达式的理解,讲一下正则表达式的字符集。

字符集是由一对方括号 “[]” 括起来的字符集合。使用字符集,可以匹配多个字符中的一个。

举个例子,比如你使用 C[ET]O 匹配到的是 CEO 或 CTO ,也就是说 [ET] 代表的是一个 E 或者一个 T 。像上面提到的 [a-z] ,就是所有小写字母中的其中一个,这里使用了连字符 “-” 定义一个连续字符的字符范围。当然,像这种写法,里面可以包含多个字符范围的,比如:[0-9a-fA-F] ,匹配单个的十六进制数字,且不分大小写。注意了,字符和范围定义的先后顺序对匹配的结果是没有任何影响的。

其实说了那么多,只是想证明,字符集一对方括号 “[]” 里面的字符关系是或关系,下面看一个例子:


import re
a = 'uav,ubv,ucv,uwv,uzv,ucv,uov'# 字符集# 取 u 和 v 中间是 a 或 b 或 c 的字符
findall = re.findall('u[abc]v', a)
print(findall)
# 如果是连续的字母,数字可以使用 - 来代替
l = re.findall('u[a-c]v', a)
print(l)# 取 u 和 v 中间不是 a 或 b 或 c 的字符
re_findall = re.findall('u[^abc]v', a)
print(re_findall)复制代码

输出的结果:

['uav', 'ubv', 'ucv', 'ucv']['uav', 'ubv', 'ucv', 'ucv']['uwv', 'uzv', 'uov']复制代码

在例子中,使用了取反字符集,也就是在左方括号 “[” 后面紧跟一个尖括号 “^”,就会对字符集取反。需要记住的一点是,取反字符集必须要匹配一个字符。比如:q[^u] 并不意味着:匹配一个 q,后面没有 u 跟着。它意味着:匹配一个 q,后面跟着一个不是 u 的字符。具体可以对比上面例子中输出的结果来理解。

我们都知道,正则表达式本身就定义了一些规则,比如 \d,匹配所有数字字符,其实它是等价于 [0-9],下面也写了个例子,通过字符集的形式解释了这些特殊字符。

import rea = 'uav_ubv_ucv_uwv_uzv_ucv_uov&123-456-789'# 概括字符集# \d 相当于 [0-9] ,匹配所有数字字符
# \D 相当于 [^0-9] , 匹配所有非数字字符
findall1 = re.findall('\d', a)
findall2 = re.findall('[0-9]', a)
findall3 = re.findall('\D', a)
findall4 = re.findall('[^0-9]', a)
print(findall1)
print(findall2)
print(findall3)
print(findall4)# \w 匹配包括下划线的任何单词字符,等价于 [A-Za-z0-9_]
findall5 = re.findall('\w', a)
findall6 = re.findall('[A-Za-z0-9_]', a)
print(findall5)
print(findall6)复制代码

输出结果:

['1', '2', '3', '4', '5', '6', '7', '8', '9']['1', '2', '3', '4', '5', '6', '7', '8', '9']['u', 'a', 'v', '_', 'u', 'b', 'v', '_', 'u', 'c', 'v', '_', 'u', 'w', 'v', '_', 'u', 'z', 'v', '_', 'u', 'c', 'v', '_', 'u', 'o', 'v', '&', '-', '-']['u', 'a', 'v', '_', 'u', 'b', 'v', '_', 'u', 'c', 'v', '_', 'u', 'w', 'v', '_', 'u', 'z', 'v', '_', 'u', 'c', 'v', '_', 'u', 'o', 'v', '&', '-', '-']['u', 'a', 'v', '_', 'u', 'b', 'v', '_', 'u', 'c', 'v', '_', 'u', 'w', 'v', '_', 'u', 'z', 'v', '_', 'u', 'c', 'v', '_', 'u', 'o', 'v', '1', '2', '3', '4', '5', '6', '7', '8', '9']['u', 'a', 'v', '_', 'u', 'b', 'v', '_', 'u', 'c', 'v', '_', 'u', 'w', 'v', '_', 'u', 'z', 'v', '_', 'u', 'c', 'v', '_', 'u', 'o', 'v', '1', '2', '3', '4', '5', '6', '7', '8', '9']复制代码

数量词

来,继续加深对正则表达式的理解,这部分理解一下数量词,为什么要用数量词,想想都知道,如果你要匹配几十上百的字符时,难道你要一个一个的写,所以就出现了数量词。

数量词的词法是:{min,max} 。min 和 max 都是非负整数。如果逗号有而 max 被忽略了,则 max 没有限制。如果逗号和 max 都被忽略了,则重复 min 次。比如,\b[1-9][0-9]{3}\b,匹配的是 1000 ~ 9999 之间的数字( “\b” 表示单词边界),而 \b[1-9][0-9]{2,4}\b,匹配的是一个在 100 ~ 99999 之间的数字。

下面看一个实例,匹配出字符串中 4 到 7 个字母的英文

import rea = 'java*&39android##@@python'# 数量词findall = re.findall('[a-z]{4,7}', a)
print(findall)
复制代码

输出结果:

['java', 'android', 'python']复制代码

注意,这里有贪婪和非贪婪之分。那么我们先看下相关的概念:

贪婪模式:它的特性是一次性地读入整个字符串,如果不匹配就吐掉最右边的一个字符再匹配,直到找到匹配的字符串或字符串的长度为 0 为止。它的宗旨是读尽可能多的字符,所以当读到第一个匹配时就立刻返回。

懒惰模式:它的特性是从字符串的左边开始,试图不读入字符串中的字符进行匹配,失败,则多读一个字符,再匹配,如此循环,当找到一个匹配时会返回该匹配的字符串,然后再次进行匹配直到字符串结束。

上面例子中的就是贪婪的,如果要使用非贪婪,也就是懒惰模式,怎么呢?

如果要使用非贪婪,则加一个 ? ,上面的例子修改如下:

import rea = 'java*&39android##@@python'# 贪婪与非贪婪re_findall = re.findall('[a-z]{4,7}?', a)
print(re_findall)复制代码

输出结果如下:

['java', 'andr', 'pyth']复制代码

从输出的结果可以看出,android 只打印除了 andr ,Python 只打印除了 pyth ,因为这里使用的是懒惰模式。

当然,还有一些特殊字符也是可以表示数量的,比如:

?:告诉引擎匹配前导字符 0 次或 1 次

+:告诉引擎匹配前导字符 1 次或多次

*:告诉引擎匹配前导字符 0 次或多次

把这部分的知识点总结一下,就是下面这个表了:

贪 婪 惰 性 描 述
?? 零次或一次出现,等价于{0,1}
+ +? 一次或多次出现 ,等价于{1,}
* *? 零次或多次出现 ,等价于{0,}
{n} {n}? 恰好 n 次出现
{n,m} {n,m}? 至少 n 次枝多 m 次出现
{n,} {n,}? 至少 n 次出现

边界匹配符和组

将上面几个点,就用了很大的篇幅了,现在介绍一些边界匹配符和组的概念。

一般的边界匹配符有以下几个:

语法 描述
^ 匹配字符串开头(在有多行的情况中匹配每行的开头)
$ 匹配字符串的末尾(在有多行的情况中匹配每行的末尾)
\A 仅匹配字符串开头
\Z 仅匹配字符串末尾
\b 匹配 \w 和 \W 之间
\B [^\b]

分组,被括号括起来的表达式就是分组。分组表达式 (...) 其实就是把这部分字符作为一个整体,当然,可以有多分组的情况,每遇到一个分组,编号就会加 1 ,而且分组后面也是可以加数量词的。

此处本应有例子,考虑到篇幅问题,就不贴了

re.sub

实战过程中,我们很多时候需要替换字符串中的字符,这时候就可以用到 def sub(pattern, repl, string, count=0, flags=0) 函数了,re.sub 共有五个参数。其中三个必选参数:pattern, repl, string ; 两个可选参数:count, flags .

具体参数意义如下:

参数 描述
pattern 表示正则中的模式字符串
repl repl,就是replacement,被替换的字符串的意思
string 即表示要被处理,要被替换的那个 string 字符串
count 对于pattern中匹配到的结果,count可以控制对前几个group进行替换
flags 正则表达式修饰符

具体使用可以看下下面的这个实例,注释都写的很清楚的了,主要是注意一下,第二个参数是可以传递一个函数的,这也是这个方法的强大之处,例如例子里面的函数 convert ,对传递进来要替换的字符进行判断,替换成不同的字符。

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-import rea = 'Python*Android*Java-888'# 把字符串中的 * 字符替换成 & 字符
sub1 = re.sub('\*', '&', a)
print(sub1)# 把字符串中的第一个 * 字符替换成 & 字符
sub2 = re.sub('\*', '&', a, 1)
print(sub2)# 把字符串中的 * 字符替换成 & 字符,把字符 - 换成 |# 1、先定义一个函数
def convert(value):group = value.group()if (group == '*'):return '&'elif (group == '-'):return '|'# 第二个参数,要替换的字符可以为一个函数
sub3 = re.sub('[\*-]', convert, a)
print(sub3)
复制代码

输出的结果:

Python&Android&Java-888Python&Android*Java-888Python&Android&Java|888复制代码

re.match 和 re.search

re.match 函数

语法:

re.match(pattern, string, flags=0)
复制代码

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

re.search 函数

语法:

re.search(pattern, string, flags=0)
复制代码

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

re.match 和 re.search 的参数,基本一致的,具体描述如下:

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

那么它们之间有什么区别呢?

re.match 只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回 None;而 re.search 匹配整个字符串,直到找到一个匹配。这就是它们之间的区别了。

re.match 和 re.search 在网上有很多详细的介绍了,可是再个人的使用中,还是喜欢使用 re.findall

看下下面的实例,可以对比下 re.search 和 re.findall 的区别,还有多分组的使用。具体看下注释,对比一下输出的结果:

示例:


#!/usr/bin/env python3
# -*- coding: UTF-8 -*-# 提取图片的地址import rea = '<img src="https://s-media-cache-ak0.pinimg.com/originals/a8/c4/9e/a8c49ef606e0e1f3ee39a7b219b5c05e.jpg">'# 使用 re.search
search = re.search('<img src="(.*)">', a)
# group(0) 是一个完整的分组
print(search.group(0))
print(search.group(1))# 使用 re.findall
findall = re.findall('<img src="(.*)">', a)
print(findall)# 多个分组的使用(比如我们需要提取 img 字段和图片地址字段)
re_search = re.search('<(.*) src="(.*)">', a)
# 打印 img
print(re_search.group(1))
# 打印图片地址
print(re_search.group(2))
# 打印 img 和图片地址,以元祖的形式
print(re_search.group(1, 2))
# 或者使用 groups
print(re_search.groups())复制代码

输出的结果:

<img src="https://s-media-cache-ak0.pinimg.com/originals/a8/c4/9e/a8c49ef606e0e1f3ee39a7b219b5c05e.jpg">https://s-media-cache-ak0.pinimg.com/originals/a8/c4/9e/a8c49ef606e0e1f3ee39a7b219b5c05e.jpg['https://s-media-cache-ak0.pinimg.com/originals/a8/c4/9e/a8c49ef606e0e1f3ee39a7b219b5c05e.jpg']imghttps://s-media-cache-ak0.pinimg.com/originals/a8/c4/9e/a8c49ef606e0e1f3ee39a7b219b5c05e.jpg('img', 'https://s-media-cache-ak0.pinimg.com/originals/a8/c4/9e/a8c49ef606e0e1f3ee39a7b219b5c05e.jpg')('img', 'https://s-media-cache-ak0.pinimg.com/originals/a8/c4/9e/a8c49ef606e0e1f3ee39a7b219b5c05e.jpg')复制代码

最后,正则表达式是非常厉害的工具,通常可以用来解决字符串内置函数无法解决的问题,而且正则表达式大部分语言都是有的。python 的用途很多,但在爬虫和数据分析这连个模块中都是离不开正则表达式的。所以正则表达式对于学习 Python 来说,真的很重要。最后,附送一些常用的正则表达式和正则表达式和 Python 支持的正则表达式元字符和语法文档。

github:https://github.com/TwoWater/Python/blob/master/python14/%E5%B8%B8%E7%94%A8%E7%9A%84%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F.md

欢迎大家 start ,https://github.com/TwoWater/Python 一下,这是草根学 Python 系列博客的库。也可以关注我的微信公众号:

草根学Python(十四) 一步一步了解正则表达式相关推荐

  1. 0基础学Python第四天:Python3的基础语法

    文章目录 目录 文章目录 前言 一.Python3基础语法 二.编码 三.标识符 四.Python保留字 五.注释 六.行和缩进 七.多行语句 八.数字类型 九:字符串 十.空行 十一.同一行显示多条 ...

  2. 教妹学Java(十四):switch 语句详解

    大家好,我是沉默王二,一个和黄家驹一样身高,和刘德华一样颜值的程序员.本篇文章通过我和三妹对话的形式来谈一谈"switch 语句". 教妹学 Java,没见过这么有趣的标题吧?&q ...

  3. Linux就该这么学第十四天(配置ssh服务,不间断服务,处理日志)

    [第十四天] ssh 主配置文件:保存着最重要的配置参数 一般配置文件:保存着一般重要的配置参数 /etc/服务名称/服务名称.xxx 主配置文件: vim /etc/ssh/ssh_config P ...

  4. Linux就该这么学第十四节课学习心得

    Ansible是最近几年特别火的一款开源运维自动化工具,它能够帮助运维人员肉眼可见地提高工作效率,并减少人为失误.Ansible有上千个功能丰富且实用的模块,而且有详尽的帮助信息可供查阅,因此即便是小 ...

  5. “字节跳动-文远知行杯”广东工业大学第十四届程序设计竞赛 1,2,3,4,5,7,8,9,10

    1001: hzy 和zsl 的生存挑战 Problem Description zsl 和hzy 来到了臭臭城堡,打算挑战臭臭城堡的大魔王hyz,大魔王hyz设置了这样的一个挑战: zsl 和hzy ...

  6. python(十四)--Django学习快速入门

    Django学习快速入门 1. 预备知识 1.1 什么是http协议 1.2 MVC模式和MTV模式 1.2.1 MVC 模式 1.2.2 MTV 模式 2. Django框架介绍 2.1 什么是框架 ...

  7. python tuple list_草根学Python(三)List 和 Tuple

    前言 目录 一.List(列表) Python 内置的一种数据类型是列表:list. list 是一种有序的集合,可以随时添加和删除其中的元素. 1.创建 List(列表) 创建一个列表,只要把逗号分 ...

  8. 少儿编程100讲轻松学python(四)-python如何判断是否为数字字符串

    前言 python判断是否为数字字符串的方法: 1.通过创建自定义函数[is_number()]方法来判断字符串是否为数字: 2.可以使用内嵌if语句来实现. python判断是否为数字字符串的方法: ...

  9. 为什么学python十大理由-python用于什么

    语言多元化是PayPal编程文化中一个重要的组成部分.在C++和Java长期流行的同时,更多的团队选择了Jva和Scala.同时,Braintree的收购也引入了一个久经世故的Ruby社区.Pytho ...

  10. python重复元素判定编程_从零开始学Python编程四:条件判断与循环

    前面已经介绍了不少Python基础知识,大家不要觉得不耐烦,想要学好Python,做好Python开发,一定要打牢基础.大家也发现了,Python中很多基础知识和数学算法是一样的,比如今天要教给大家的 ...

最新文章

  1. eselasticsearch入门_ElasticSearch入门学习-基础示例(1)
  2. webservice发送xml报文_海关总署公告2020年第120号(关于发布进出境公路运输工具货运舱单电子传输报文格式V1.1的公告)...
  3. python教育学_使用Python处理教育学领域的数据——以某篇期刊论文为例
  4. VMware View 5万点虚拟桌面项目案例详析
  5. [react] 如果组件的属性没有传值,那么它的默认值是什么?
  6. 戴尔服务器显示e1810,戴尔服务器提示: PowerEdge2950 E1810 HDD 1 Fault该如何解决?求帮助!!!...
  7. 各类型土地利用图例_给排水系统各部件及图纸你还有多少不了解?
  8. gnu java_【Java学习笔记】修饰符
  9. 关于j2ee工程发布到was上后,部分更新,例修改web.xml配置文件不起作用的原因解析【转】...
  10. 走进C++程序世界-----函数相关(全局变量)
  11. 剥开比原看代码03:比原是如何监听p2p端口的
  12. WPF笔记(1.8 资源与映射)——Hello,WPF!
  13. mysql双机热备份windows_window下使用mysql双机热备份
  14. rfid考勤系统mysql_RFID智能考勤管理系统
  15. js切换图片会闪动_js 图片闪动,间隔几分钟闪动一下
  16. 创意h5游戏案例:记忆类H5手机游戏评测
  17. C++流式输入输出加速 给 cin/cout 装个小火箭
  18. centos7.x 搭建php运行环境
  19. 微信小程序--图片懒加载
  20. 【DP】CF940E Cashback

热门文章

  1. sqlserver替换特殊字符
  2. git 暂存文件操作 stash
  3. 原力值13872,不知道排名多少
  4. 给tomcat指定JDK
  5. 一句“哭什么哭”,说得好
  6. JDK自带的int值的sun.text.IntHashtable
  7. 对‘avformat_find_stream_info’未定义的引用、to the PKG_CONFIG_PATH environment variable
  8. 无法解析的外部符号:GetWindowThreadProcessId/EnumWindow
  9. matplotlib 2.2.4 has requirement python-dateutil=2.1, but you'll have python-dateutil 1.5
  10. 时间穿越问题:时间因速度引力而不同,空间又无法精确定位