python-正则表达式

作者 by Joab / 2022-04-02 / 1 评论 / 41 个足迹

本文章转载至公众号:麦叔编程(回复re即可查看该文章)

正则表达式是爬虫,Web开发,数据处理等各类涉及到文本处理的一把锋利的瑞士军刀,是每个程序员必须掌握的一门技能。本文稍微有点长,但耐心看完,实践其中的例子,你的功力必会更精进一层!

如果你对正则表达式还是小白,强烈建议去B站看我的配套视频,在B站搜“麦叔编程 正则表达式”就可以找到了。

如果你已经有一定基础了,看本文应该就够了!

不管有没有基础,本文最后有我整理的正则表达式语法清单,建议下载保存!

640.jpg

1. 正则表达式的7个境界

假设有一段文字:

text = '身高:178,体重:168,学号:123456,密码:9527'

要确定文本中是否包含数字123456,我们可以用in运算符,也可以使用index函数:

text = '身高:178,体重:168,学号:123456,密码:9527'target = '123456'if target in text: print('找到了')print(text.index(target))

但当问题变得复杂的时候,比如找出字符串中所有数字,用基本的字符串处理就不行了。

这不再是一个固定的字符串匹配问题,而是一个模式,一种规则的匹配。为了解决这个问题,有位叫Stephen的大神在1951年提出了正则表达式。

几乎任何一门通用编程语言都有专门的正则表达式的模块,正则表达式英文是regular expressesion,所以编程语言中的模块名字一般叫re,或者regex等。Python中的正则表达式处理模块是re

正则表达式就是为了找到符合某种模式的字符串,这些模式包括:是什么字符重复多少次在什么位置有哪些额外的约束。请注意:这里的每一句话都对应了正则表达式中的一类语法。

下面我们通过8个例子,来熟悉正则表达式,后面再讲写正则表达式的套路和正则表达式的语法。

level1 - 固定的字符串

要求:确定字符串中是否有123456

import retext = '麦叔身高:178,体重:168,学号:123456,密码:9527'print(re.findall(r'123456', text))

代码说明:

  • 第1行,引入正则表达式模块re
  • 第3行,使用re的findall()方法找到所有符合模式的字符串,这里的模式就是123456,也就是说找到字符串中所有的123456。
  • findall()方法的第1个参数是模式,第2个参数是要查找的字符串。
  • 模式中会有一些特殊字符,所以用r表示这是一个raw字符串,让Python不要去转义里面的特殊字符。

上面程序的运行结果是:[123456],因为整个字符串中就1个123456。

level1就是一个固定的字符串匹配,可以用传统的字符串匹配解决。

level2 - 某一类字符

要求:找出所有的单个的数字

text = '麦叔身高:178,体重:168,学号:123456,密码:9527'print(re.findall(r'\d', text))

这个表达式\d表示所有的数字,所以1,7,8,1,6,8等都可以匹配到。这是很简单的模式,只匹配1个单独的数字。

如果你完全没有接触过正则表达式,看着有点懵逼,去我看在B站的视频吧,让我手把手教你!

level3 - 重复某一类字符

要求:找所有的数字,比如178,168,123456,9527等。

text = '麦叔身高:178,体重:168,学号:123456,密码:9527'print(re.findall(r'\d+', text))

这个模式\d+在\d的后面增加了+号,表示数字可以出现1到多次,所以178等都符合它的要求。

leve4 - 组合level2

要求:找出座机号码

text = '麦叔电话是18812345678,他还有一个电话号码是18887654321,他爱好的数字是01234567891,他的座机是:0571-52152166'print(re.findall(r'\d{4}-\d{8}', text))

\d{4}-\d{8}这是一个组合的模式,表示前面4个数字,中间一个横杠,后面8个数字。

这里会匹配一个结果:0571-52152166

leve5 - 多种情况

要求:找出手机号码或者座机号码

text = '麦叔电话是18812345678,他还有一个电话号码是18887654321,他爱好的数字是01234567891,他的座机是:0571-52152166'print(re.findall(r'\d{4}-\d{8}|1\d{10}', text))

比上面有复杂了点,因为使用竖线(|)来表示”或“的关系,就是手机号码和电话号码都可以。

这里会匹配三个结果:18812345678,18887654321,0571-52152166。

level6 - 限定位置

要求:在句子开头的手机号码,或座机

text = '18812345678,他还有一个电话号码是18887654321,他爱好的数字是01234567891,他的座机是:0571-52152166'print(re.findall(r'^1\d{10}|^\d{4}-\d{8}', text))

在表达式的最开始使用了^符号,表示一定要在句子的开头才行。这时候只有18812345678能匹配上。

level7 - 内部约束

要求:找出形如barbar, dardar的前后三个字母重复的字符串

text = 'barbar carcar harhel'print(re.findall(r'(\w{3})(\1)', text))

这里barbar和carcar会匹配上,但harhel不会被匹配,因为它不是3个字符重复的。

\w{3}表示3个字符,放在小括号中(\w{3})就成为一个分组,而后面的(\1)表示它里面的内容和第1个括号里的内容必须相同,其中的1就表示第1个括号,也就是说3个字符要重复出现两次。

2. 写正则表达式的步骤

如何写正则表达式呢?我总结了几个步骤。不管多复杂,基本上都百试不爽。

我们仍然以包含分机号码的座机电话号码为例,比如0571-88776655-9527,演示下面的步骤:

  • 确定模式包含几个子模式

    它包含3个子模式:0571-88776655-9527。这3个子模式用固定字符连接。

  • 各个部分的字符分类是什么

    这3个子模式都是数字类型,可以用\d。现在可以写出模式为:\d-\d-\d

  • 各个子模式如何重复

    第1个子模式重复3到4次,因为有010和021等直辖市

    第2个子模式重复7到8次,有的地区只有7位电话号码

    第3个子模式重复3-4次

    加上次数限制后,模式成为:\d{3,4}-\d{7,8}-\d{3,4}

    但有的座机没有分机号,所以我们用或运算符让它支持两者:

    \d{3,4}-\d{7,8}-\d{3,4}|\d{3,4}-\d{7,8}

  • 是否有外部位置限制

    没有

  • 是否有内部制约关系

    没有

经过一通分析,最后的正则就写成了,测试一下:

text = '随机数字:01234567891,座机1:0571-52152166,座机2:0571-52152188-1234'print(re.findall(r'\d{3,4}-\d{7,8}-\d{3,4}|\d{3,4}-\d{7,8}', text))

最后的结果是:两个座机都可以找出来。

3. 语法规则

正则表达式几十个符号,看似很复杂,但如果能否分清楚类别和作用,就没那么复杂了。

  • 字符类别表达 - 表达某一类字符,比如数字,字母,3到9之间的任何数字等

640.png

  • 字符的重复次数,也叫做量词。比如身份证是数字重复15或18次,也就是:\d{15}或者\d{18}。

640-1648708318847.png

  • 组合模式:把多个简单的模式组合在一起,可以是拼接,也可以是二者选其一。

640-1648708326860.png

  • 位置:限定模式出现的位置,比如行首,行尾,或者在特定字符之后等。

640-1648708329540.png

  • 分组,把一个正则表达式分成几个部分,这样可以重复某个分组,或者指定两个分组必须相同等额外的要求。

640-1648708332650.png

  • 其他

640-1648708337895.png

4. Python正则模块re的用法

python的re模块还比较简单,包括以下几个方法:

  • re.search():查找符合模式的字符,只返回第一个,返回Match对象
  • re.match():和search一样,但要求必须从字符串开头匹配
  • re.findall():返回所有匹配的字符串列表
  • re.finditer():返回一个迭代器,其中包含所有的匹配,也就是Match对象
  • re.sub():替换匹配的字符串,返回替换完成的文本
  • re.subn():替换匹配的字符串,返回替换完成的文本和替换的次数
  • re.split():用匹配表达式的字符串做分隔符分割原字符串
  • re.compile():把正则表达式编译成一个对象,方便后面使用

5. 更多例子

下面我举了更多的例子,供大家练习和熟悉。

建议先自己尝试去写出相关的正则表达式,再回来看下面的参考答案。

  • 匹配Email地址的正则表达式
  • 匹配网址URL的正则表达式
  • 匹配帐号是否合法(字母开头,允许5-16字节,允许字母数字下划线)
  • 匹配国内电话号码(0571-87655322,001-87666622,920-209642-964)
  • 匹配腾讯qq号
  • 匹配中国邮政编码(6位数字)
  • 匹配身份证(15位或18位)
  • 匹配ip地址
  • 形如"carcar"或"barbar"等会重复的三个字符的单词

==============分隔符==============

现在来看参考答案:

  • 匹配Email地址的正则表达式:\w+([-+.]\w+)*@\w+([-.]\w+)*.\w+([-.]\w+)*
  • 匹配网址URL的正则表达式:[a-zA-z]+://[^s]*
  • 匹配帐号是否合法(字母开头,允许5-16字母,允许字母数字下划线):^[a-zA-Z][a-zA-Z0-9_]{4,15}$
  • 匹配国内电话号码,格式为:区号-号码-分机号,分机号可能没有。d{3,4}-d{7,8}-d{3,4}|d{3,4}-d{7,8}
  • 匹配腾讯qq号:[1-9][0-9]{4,}
  • 匹配中国邮政编码(6位数字):[1-9]\d{5}(?!\d)
  • 匹配身份证(15位或18位):\d{15}|\d{18}
  • 匹配ip地址:\d+.\d+.\d+.\d+
  • 形如"carcar"或"barbar"等会重复的三个字符的单词

    (\w{3})(\1)

  • 获取以"密码:"开头的数字:

    (?<=密码.)\d+

6. 正则表达式Cheatsheet

建议收藏和下载这个cheatsheet,在必要的时候查询。

如果对你有帮助,请给我点赞,并点在看,谢谢!

640-1648708352005.png

独特见解

 评论 1 条