正则表达式在文本验证、查找和替换方面有着广泛的应用。本节介绍正则表达式的基本概念,结合简单的实例让您对正则表达式的编写和应用有感性认识。
什么是正则表达式
关于文本的查找和替换,常见的有两种典型应用。一种是在Windows资源管理器中查找指定目录下的文件,通常是指定文件名称或文件名称中的一部分进行查找,还可以指定通配符?和*,分别表示一个字符或任意个字符,比如“*.exe”表示所有可执行文件。另一种是在记事本、Word软件等办公软件中一般都有的查找功能和替换功能。这两种情况下给出的搜索文本都是简单的正则表达式。
很多时候,我们需要匹配形式更复杂的文本,比如从一个网页的文本中提取出电话号码、手机号码、电子邮箱等,比如从给定文本中提取出以某字符串打头、以某字符串结尾的子文本等,这就需要用到正则表达式。
正则表达式是由普通字符和一些元字符组成的逻辑表达式,普通字符包括数字和大小写字母,元字符则用字符或字符的组合表达特殊的含义。所以,正则表达式其实就是按照事先定义好的规则来组合普通字符和元字符,表达字符串的匹配逻辑,执行查找时对表达式进行解析,了解它所表达的意图并进行匹配,最后找到需要查找的内容。
由于文本查找和替换的需求很常见,在各种语言中都有正则表达式的内容。在不同的语言中,正则表达式的编写规则几乎是相同的,区别在于编译和处理正则表达式的语法有所不同。
正则表达式示例
假如有下面一段取自某网页的略显杂乱的文字:
>>> a='''各地普通中小学班额信息公示网站链接、监督电话及邮箱
市 县市区 网站链接 监督电话 监督邮箱
省教育厅 http://sbe.sei.edu.cn:8094/ 0591-81916517 jjc@shaong.cn
某某市 http://jndu.jinn.gov.cn/art/2019/3/5/art_18904_2865186.html?tdsourcetag=s_pcqq_aiomsg 0591-66608024 jnsjyjjjc@jn.shndong.cn
某某市 某某区 http://www.liia.gov.cn/art/2019/3/6/art_1317_2853507.html 0591-86553601 jnlxjyjbgs@jn.shadong.cn
某某市 某某区 http://60.316.102.41/wznrYongRi/ArticleID/45001 0591-67987522 jnsz820296@163.com'''
现在要执行3个任务,包括把文本中的"某某区"替换为"某某县"、查找出所有电话号码和查找出所有电子邮箱。首先导入re模块。
>>> import re
使用re模块的sub函数实现替换,该函数的第1个参数指定要匹配的字符串,第2个参数指定进行替换的字符串,第3个参数指定给定的文本。
>>> m=re.sub("某某区","某某县",a)
>>> m
'各地普通中小学班额信息公示网站链接、监督电话及邮箱\n市 \t县市区 \t网站链接 \t监督电话 \t监督邮箱\n省教育厅 \thttp://sbe.sei.edu.cn:8094/ \t0591-81916517 \tjjc@shaong.cn\n某某市 \thttp://jndu.jinn.gov.cn/art/2019/3/5/art_18904_2865186.html?tdsourcetag=s_pcqq_aiomsg \t0591-66608024 \tjnsjyjjjc@jn.shndong.cn\n某某市 \t某某县 \thttp://www.liia.gov.cn/art/2019/3/6/art_1317_2853507.html \t0591-86553601 \tjnlxjyjbgs@jn.shadong.cn\n某某市 \t某某县 \thttp://60.316.102.41/wznrYongRi/ArticleID/45001 \t0591-67987522 \tjnsz820296@163.com'
可见,文本中已经进行了正确的替换。其中,\t表示制表符间隔,\n表示换行。
下面找出文本中的所有电话号码。注意电话号码的格式,前面4个数字为区号,后面跟短横线,后面再跟8个数字。所以正则表达式编写为:
"[0-9]{4}-[0-9]{8}"
[0-9]表示取自0到9的一个数字,{4}表示重复4次,即区号取4个数字。后面跟短横线,然后是8个数字,这个跟区号的编写规则类似。
使用re模块的findall函数查找出所有符合规则的电话号码。
>>> cl=re.findall("[0-9]{4}-[0-9]{8}",a)
>>> cl
[‘0591-81916517’, ‘0591-66608024’, ‘0591-86553601’, ‘0591-67987522’]
这样,全部电话号码被找出来了。
下面找出文本中的所有电子邮箱。注意电子邮箱的格式,字符@前面为用户名,由数字、大小写字母和下划线组成,后面为域名,域名后缀前面由数字、大小写字母、下划线和点组成,域名后缀为com, net, org和cn中的一种,长度1-3个字符。所以正则表达式编写为:
"[0-9a-zA-Z_]*)@([0-9a-zA-Z._]*\.[com,net,org,cn]{1,3}"
[0-9 a-zA-Z_]表示取自数字、大小写字母和下划线中的一个字符,*表示重复任意次。
使用re模块的findall函数查找出所有符合规则的电子邮箱。
>>> em=re.findall("[0-9a-zA-Z_]*)@([0-9a-zA-Z._]*\.[com,net,org,cn]{1,3}",a)
>>> em
[‘jjc@shaong.cn’, ‘jnsjyjjjc@jn.shndong.cn’, ‘jnlxjyjbgs@jn.shadong.cn’, ‘jnsz820296@163.com’]
所有电子邮箱被正确找出来的。
如果希望分别得到每个电子邮箱的用户名和域名信息,可以在正则表达式中用小括号进行分组,将定义用户名和域名的部分分别用小括号括起来,然后使用re模块的finditer函数进行查找。
>>> em=re.finditer("([0-9a-zA-Z_]*)@([0-9a-zA-Z._]*\.[com,net,org,cn]{1,3})",a)
finditer函数返回的是一个可迭代对象,使用for循环可以获取匹配的每个电子邮箱对象,使用对象的group方法可以获取每个邮箱的分组信息。
>>> for email in em:
un=email.group(1)
dn=email.group(2)
print(un+"\t"+dn)
jjc shaong.cn
jnsjyjjjc jn.shndong.cn
jnlxjyjbgs jn.shadong.cn
jnsz820296 163.com
这样,分别得到了每个电子邮箱的用户名和域名信息。