面试官: 现在有一个需求,我们需要分析一个文本中的所有数据,需要抓出其中的所有人的电话号码,以 xxx-xxxx-xxxx 或者是 xxx-xxxxxxxx 或者是 xxxxxxxxxxx 为格式,请用python
来编写一个程序来实现。
我: 使用
re
模块,额。。。。。,我知道,又没了。
这边文章,我们将介绍python
正则表达式工具re
,当然了,还是要说明一下python
使用环境为: Python 3.6.8
什么是正则表达式
正则表达式定义
正则表达式(regex
) 是描述文本模式的一种表达式,由模式字符串和特殊字符组成,用于匹配、搜索、替换等等。
阐述模式和特殊字符
好了,正则表达式就介绍完了,咋的?还不明白啊。其实我刚开始看到时候,也不明白,我们举个小栗子来介绍一下模式和特殊字符吧。
假设有一个正则表达式是 abcdef
,那么它的模式字符串就是abcdef
,特殊字符没有。
假设还有一个正则表达式是^juejin.*pdudo$
,在此正则表达式中:
^
是特殊字符,表示匹配字符串的开头。juejin
是一个模式字符串,表示需要匹配的字面字符串。.
是一种特殊语法,.
表示匹配任意一个字符,*
表示重复前面的字符零次或多次。因此,".*" 表示匹配任意长度的字符串。pdudo
是一个模式字符串,表示需要匹配的字面字符串。$
是特殊字符,表示匹配字符串的结尾。
python
提供的特殊字符有哪些
额,这里又有一个问题了,你知道特殊字符有哪些么?
由于我们使用python
来写正则,所以我们直接看python
文档就可以了,例如:docs.python.org/zh-cn/3.7/l…
我们找到关于特殊字符的描述就可以了,例如:
这里就不原班搬运了。
在python
中,我们一般通过标准库re
来编写正则表达式,接下来,我们一起来看看吧。
为什么要使用正则表达式
在介绍这个段落内容之前,我们不妨写个小功能,我们想要搜寻目录下文本的内容,内容必须包含pdudo
。
由于码上掘金不太好写文件之类的,所以我们给抽离出来定义为列表的形式。
大概内容如下:
使用暴力搜索法来搜索特定字符串
我们想搜索上述案例中,关于pdudo
的字符串,先尝试一种不用正则表达式的方式。这个很简单,我们直接使用in
关键字就可以完成,来看代码:
def main() -> None: testTxt = [ ["今天,小狗pdduo迷路了。好心的主人找了很久也没找到它,心急如焚。终于,在一个角落里发现了它。小狗抬起头,看着主人,高兴地摇了摇尾巴,似乎在说:“谢谢你救了我。"], ["教室里传来一阵阵笑声,原来是pdudo调皮捣蛋。老师生气地走过去,pdudo马上低下了头。可当老师转过身时,pdudo又偷偷地对同桌saying:“快看,我藏起来了!"], ["爷爷退休后,经常给家里打电话聊天。他总是说:“最近有一个新朋友叫puddo,跟我聊得很开心。”后来才知道,那是他认识的一位年轻的志愿者,每周会给他打电话陪他聊天。"], ["在海滩上散步时,我们捡到了一个漂亮的贝壳,上面写着“pdduo”。这让我们想起了一个童话故事,于是我们决定将它带回家,留作纪念。"], ["昨天晚上,我在电视上看到了一个关于puddo的纪录片。它讲述了一只顽皮的小熊,经过几年的努力和奋斗,终于成为了丛林之王。这个故事让我想起了自己的梦想,也让我更加坚定要努力实现它。"] ] strs = "pdudo" for i in testTxt: if strs in i[0]: print(i) if __name__ == '__main__': main()
还是很简单嘛,但是现在问题来了,如果要匹配以p
开头以d
结尾,匹配的字符串为5个字符,这个如果硬写,应该怎么写呢?
额,这个要写,也难不倒,对吧? 我们可以遍历一下。
逻辑为: 如果找到p
字符,确定下标,再根据下标+4判断是否是字符d
,如果是就输出,对吧?来写下:
strs = "pdudo" for i in testTxt: j = 0 while j < len(i[0]): if strs[0] == i[0][j] : if (j+4 < len(i[0])) and i[0][j+len(strs)-1] == strs[len(strs)-1]: print("search ok,pdudo中的p下标为: ",j,"原字符串为:",i[0]) break j = j + 1
上述代码,就是用最原始的方法,遍历所有字符,如果找到了p
则再判断该字符后4个字符位置是否是o
,如果是则将p
的下标和整段话都打印出来。
详细代码,可以看这里:
def main() -> None: testTxt = [ ["今天,小狗pdduo迷路了。好心的主人找了很久也没找到它,心急如焚。终于,在一个角落里发现了它。小狗抬起头,看着主人,高兴地摇了摇尾巴,似乎在说:“谢谢你救了我。"], ["教室里传来一阵阵笑声,原来是pdudo调皮捣蛋。老师生气地走过去,pdudo马上低下了头。可当老师转过身时,pdudo又偷偷地对同桌saying:“快看,我藏起来了!"], ["爷爷退休后,经常给家里打电话聊天。他总是说:“最近有一个新朋友叫puddo,跟我聊得很开心。”后来才知道,那是他认识的一位年轻的志愿者,每周会给他打电话陪他聊天。"], ["在海滩上散步时,我们捡到了一个漂亮的贝壳,上面写着“pdduo”。这让我们想起了一个童话故事,于是我们决定将它带回家,留作纪念。"], ["昨天晚上,我在电视上看到了一个关于puddo的纪录片。它讲述了一只顽皮的小熊,经过几年的努力和奋斗,终于成为了丛林之王。这个故事让我想起了自己的梦想,也让我更加坚定要努力实现它。"] ] strs = "pdudo" for i in testTxt: j = 0 while j < len(i[0]): if strs[0] == i[0][j] : if (j+4 < len(i[0])) and i[0][j+len(strs)-1] == strs[len(strs)-1]: print("search ok,pdudo中的p下标为: ",j,"原字符串为:",i[0]) break j = j + 1 if __name__ == '__main__': main()
上面案例实现起来多很难吧,如果再上一层台阶,匹配字符串,必须是p
在前和d
灾后,中间的字符串必须由u
、d
、o
来组成,是不是更加头大了啊,这个时候,正则表达式就很好做这个事情。
使用正则表达式来匹配字符串
我们使用正则表达式,仅需定义一个匹配模式,而后无脑调用findall
即可,例如查询可以这样写:
pattern = "p[duo]{3}o" for i in testTxt: if re.findall(pattern, i[0]): print(i)
其中,p[duo]{3}o
代表以p
在前,以o
在后,中间必须包含3个字符,字符局限于d
、u
、o
之间。
使用的时候,可以使用python
标准库re
,调用方法findall
,参数为匹配模式 和 需要匹配的字符串即可。
可以看代码实现:
import re def main() -> None: testTxt = [ ["今天,小狗pdduo迷路了。好心的主人找了很久也没找到它,心急如焚。终于,在一个角落里发现了它。小狗抬起头,看着主人,高兴地摇了摇尾巴,似乎在说:“谢谢你救了我。"], ["教室里传来一阵阵笑声,原来是pdudo调皮捣蛋。老师生气地走过去,pdudo马上低下了头。可当老师转过身时,pdudo又偷偷地对同桌saying:“快看,我藏起来了!"], ["爷爷退休后,经常给家里打电话聊天。他总是说:“最近有一个新朋友叫puddo,跟我聊得很开心。”后来才知道,那是他认识的一位年轻的志愿者,每周会给他打电话陪他聊天。"], ["在海滩上散步时,我们捡到了一个漂亮的贝壳,上面写着“pdduo”。这让我们想起了一个童话故事,于是我们决定将它带回家,留作纪念。"], ["昨天晚上,我在电视上看到了一个关于puddo的纪录片。它讲述了一只顽皮的小熊,经过几年的努力和奋斗,终于成为了丛林之王。这个故事让我想起了自己的梦想,也让我更加坚定要努力实现它。"] ] pattern = "p[duo]{3}o" for i in testTxt: if re.findall(pattern, i[0]): print(i) if __name__ == '__main__': main()
使用标准库re
,很容易理解这个事情的。
正则表达式案例
还记得文章最开始的时候,我们需要写一个程序,以此来搜索以 xxx-xxxx-xxxx 或者是 xxx-xxxxxxxx 或者是 xxxxxxxxxxx 为格式的电话号码么? 我们了解了上述正则表达式后的写法后,应该会写了吧,我们来试试看:
我们构建了一份假的富婆通讯录,内容如下:
我们将其放置到列表中,我们想搜索一下这3种电话号码,分别为: xxx-xxxx-xxxx 、 xxx-xxxxxxxx 、 xxxxxxxxxxx ,就目前而言我们知晓手机号均为1开头,而后全是数字。所以如上三种方法,正则表达式可以这样写:
字符串 | 正则表达式 |
xxx-xxxx-xxxx | 1[0-9]{2}-[0-9]{4}-[0-9]{4} |
xxx-xxxxxxxx | 1[0-9]{2}-[0-9]{8} |
xxxxxxxxxxx | 1[0-9]{10} |
其中正则表达式的 [] 代表合集,即: [abc]
可以匹配a
、b
、c
的其中一个,而后面的{n}
则可以匹配前面[]
中数字n
次。
拿这个举例,模式 和 特殊字符,我们分别用 蓝色 和 红色 标注出来一下:
如果不明白的,我可以滑到最上面去看看。
但是我们需要匹配3个,我们如何用一个正则表达式来编写呢?答案是可以使用竖线分割|
,于是乎,正则表达式就成了这样的了: 1[0-9]{2}-[0-9]{4}-[0-9]{4}|1[0-9]{2}-[0-9]{8}|1[0-9]{10}
最后使用re
调用findall
就可以了。
import re def main() -> None: testTxt = [ ["姓名: 张X 女 英文名: Emily 北京市海淀区中关村大街X号 138-1234-5678 爱好: 游泳 职业: 律师"], ["姓名: 李X英 性别: 女 英文名: Olivia 地址: 成都市高新区天府大道中段XXX号 电话号码: 156-12345678 健身 职业: 医生"], ["姓名: 赵X华 女 英文名: James 武汉市江汉区解放大道XX号 15812345678 摄影 职业: 教育工作者"] ] pattern = "1[0-9]{2}-[0-9]{4}-[0-9]{4}|1[0-9]{2}-[0-9]{8}|1[0-9]{10}" for i in testTxt: print(re.findall(pattern,i[0])) if __name__ == '__main__': main()
如果我们我们还想将名字和职业以及电话号码串起来呢? 我们可以定义3个正则表达式,分别是电话、姓名以及职业:
telPattern = "1[0-9]{2}-[0-9]{4}-[0-9]{4}|1[0-9]{2}-[0-9]{8}|1[0-9]{10}" namePattern = "姓名: .*? " jobPattern = "职业: .*?$"
我们改改代码,就可以实现了:
关于更多的正则表达式,可以查看上述提供的文档,也可以看下一篇博客。
总结
该篇文章,先是介绍了正则表达式,而后解释了正则表达式中的模式和特殊字符,最后对比了一下使用正则表达式和不是用正则表达式的区别。最后的最后,提供了一个正则表达式搜索电话号码的案例。
特别是python
文档上的特殊字符,应该好好了解下。
后面,python
使用正则表达式,只能使用re.findall
么? 我们后面会详细介绍python re
。