Python基础学习之匹配与正则表达式
文章目录
前言
正则表达式很有用,但如果不是程序员,很少会有人了解它,尽管大多数现代文本编辑器和文字处理器(诸如微软的 Word 或 OpenOffice),都有查找和查找替换功能,可以根据正则表达式查找。正则表达式可以节约大量时间,不仅适用于软件用户,也适用于程序员。本文章主要讲python的字符匹配与正则表达式
一、不用正则表达式查找
在python学习中经常会遇到字符串匹配提取问题,比如:
Q1:告诉你某国手机号组成为:xxxx-xxx-xxx-xxxx(其中x为数字,如1234-123-123-1234),需要你输入一个字符串判断是不是电话。
你会怎么解决呢?如果是我,我会先判断字符串的长度,如果长度都不一致,那肯定不是号码;然后判断每个’-’的位置对没;最后判断每段子字符串是纯数字。代码示例如下:
def checkIsPhone(num):
if len(num) != 17:
return False
for i in range(0, 4):
if not num[i].isdecimal():
return False
for i in range(5, 8):
if not num[i].isdecimal():
return False
for i in range(9, 12):
if not num[i].isdecimal():
return False
for i in range(13, 17):
if not num[i].isdecimal():
return False
if num[4] != '-' or num[8] != '-' or num[12] != '-':
return False
return True
if __name__ == '__main__':
phone = '1234-123-123-1234'
phone1 = '1235-abc-123-1234'
print(checkIsPhone(phone))
#>>>True
print(checkIsPhone(phone1))
#>>>False
因为号码只有17个,所以代码看起来还不算复杂。但是如果号码有很多个,里面的分段循环查找和符号’-’的查找是不是会有很多部分,代码就会很多且单一,让其他人阅读起来费劲。
Q2:有一字符串里面可能包含电话号码,你需要从里面提取出来所有可能是电话号码的子字符串。在刚刚代码基础上添加如下代码:
def getPhone(str0):
list = []
if len(str0) < 17:
return False
else:
for i in range(0, len(str0) - 17):
str1 = str0[i:i + 17]
if checkIsPhone(str1):
list.append(str1)
return list
示例字符串长度并不大,所以很快就有了结果,如果字符串长度十分庞大,运行时长也会增长。
二、用正则表达式进行匹配查找
就刚刚的问题,用正则表达式\d{4}-\d{3}-\d{3}-\d{3}处理,代码示例如下:
import re
string = '这是我的电话号码:1234-123-123-1234,4321-321-321-4321是我的银行卡号'
# 创建一个正则表达式对象
Reg = re.compile(r'\d{4}-\d{3}-\d{3}-\d{4}')
# 查找字符串所有符合的子字符串,返回子字符串列表
match = Reg.findall(string)
print(match)
# >>>['1234-123-123-1234', '4321-321-321-4321']
# 查找第一个符合的子字符串,返回一个Match对象 Match 对象有一个 group()方法,它返回被查找字
# 符串中实际匹配的文本
match1 = Reg.search(string).group()
print(match1)
# >>>1234-123-123-1234
可以很明显的看出,使用正则表达式进行匹配查找,能高速有效的得出结果,且使你的代码简短明了。
三、正则表达式的更多匹配方式
1.利用括号分组
假如你需要从一个句子里面提取身份证号里面的生日,添加括号在正则表达式里创建分组,用group()提取我们需要的生日号码。代码示例如下:
import re
# 其中20000125 是生日号码
str0 = '这是我的身份证号码:510211-20000125-3910'
Reg = re.compile(r'(\d{6})-(\d{8})-(\d{4})')
match = Reg.search(str0)
print(match.group(0))
# >>>510211-20000125-3910
print(match.group(1))
# >>>510211
print(match.group(2))
# >>>20000125
print(match.group(3))
# >>>3910
向 group() 匹配对象方法传入整数 1 或2或3,就可以取得匹配文本的不同部分。向 group()方法传入 0 或不传入参数,将返回整个匹配的文本。
2.用管道匹配多个分组
字符|称为“管道”。希望匹配许多表达式中的一个时,就可以使用它。例如正则表达式r’Tom|Jerry’,将匹配’Tom’或者’Jerry’。代码示例如下:
import re
str1 = '波妞喜欢宗介,中介喜欢赚差价'
Reg1 = re.compile(r'波妞|宗介')
match = Reg1.findall(str1)
print(match)
# >>>['波妞', '宗介']
batRegex = re.compile(r'Bat(man|mobile|copter|bat)')
mo = batRegex.search('Batmobile lost a wheel')
print(mo.group())
# >>>Batmobile
print(mo.group(1))
# >>>mobile
如果要匹配’|’,则需要转义字符’\|’。
3.用问号实现可选匹配
代码示例如下:
Reg = re.compile(r'super(wo)?man')
mo1 = Reg.search('The china of superman')
print(mo1.group())
# >>>superman
mo2 = Reg.search('The china of superwoman')
print(mo2.group())
# >>>superwoman
正则表达式中的(wo)?部分表明,模式 wo 是可选的分组。该正则表达式匹配的文本中,wo 将出现零次或一次。
如果需要匹配真正的问号字符,就使用转义字符\?。
4.用星号匹配零次或多次
*(星号)意味着“匹配零次或多次”,即星号之前的分组,可以在文本中出现任意次。它可以完全不存在,或一次又一次地重复。代码示例如下:
batRegex = re.compile(r'super(wo)*man')
mo1 = batRegex.search('The china of superman')
print(mo1.group())
# >>>superman
mo2 = batRegex.search('The china of superwoman')
print(mo2.group())
# >>>superwoman
mo3 = batRegex.search('The china of superwowowowoman')
print(mo3.group())
# >>>superwowowowoman
如果需要匹配真正的星号字符,就在正则表达式的星号字符前加上倒斜杠,即\*。
5.用加号匹配一次或多次
*意味着“匹配零次或多次”,+(加号)则意味着“匹配一次或多次”。星号不要求分组出现在匹配的字符串中,但加号不同,加号前面的分组必须“至少出现一次”。这不是可选的。代码示例如下:
batRegex = re.compile(r'super(wo)+man')
mo1 = batRegex.search('The china of superman')
print(mo1)
# >>>None
mo2 = batRegex.search('The china of superwoman')
print(mo2.group())
# >>>superwoman
mo3 = batRegex.search('The china of superwowowowoman')
print(mo3.group())
# >>>superwowowowoman
6.用花括号匹配特定次数与贪心和非贪心匹配
如果想要一个分组重复特定次数,就在正则表达式中该分组的后面,跟上花括号包围的数字。例如,正则表达式(Ha){3}将匹配字符串'HaHaHa',但不会匹配'HaHa',因为后者只重复了(Ha)分组两次。
除了一个数字,还可以指定一个范围,即在花括号中写下一个最小值、一个逗号和一个最大值。例如,正则表达式(Ha){3,5}将匹配'HaHaHa'、'HaHaHaHa'和'HaHaHaHaHa'。也可以不写花括号中的第一个或第二个数字,不限定最小值或最大值。例如,(Ha){3,}将匹配 3 次或更多次实例,(Ha){,5}将匹配 0 到 5 次实例。花括号让正则表达式更简短。
Python 的正则表达式默认是“贪心”的,这表示在有二义的情况下,它们会尽可能匹配最长的字符串。花括号的“非贪心”版本匹配尽可能最短的字符串,即在结束的花括号后跟着一个问号。在交互式环境中输入以下代码,注意在查找相同字符串时,花括号的贪心形式 和非贪心形式之间的区别:
Regex = re.compile(r'(Ha){3,5}')
mo1 = Regex.search('HaHaHaHaHa')
print(mo1.group())
# >>>HaHaHaHaHa
Regex = re.compile(r'(Ha){3,5}?')
mo2 = Regex.search('HaHaHaHaHa')
print(mo2.group())
# >>>HaHaHa
7.findall()方法
回一个Match 对象,包含被查找字符串中的“第一次”匹配的文本,而 findall()方法将返回一组字符串,包含被查找字符串中的所有匹配。findall()不是返回一个 Match 对象,而是返回一个字符串列表,只要 在正则表达式中没有分组。列表中的每个字符串都是一段被查找的文本,它匹配该正则表达式。代码示例如下:
reg = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d') # has no groups
mo = reg.findall('Cell: 415-555-9999 Work: 212-555-0000')
print(mo)
# >>>['415-555-9999', '212-555-0000']
如果在正则表达式中有分组,那么 findall 将返回元组的列表。每个元组表示一个找到的匹配,其中的项就是正则表达式中每个分组的匹配字符串。代码示例如下:
reg = re.compile(r'(\d\d\d)-(\d\d\d)-(\d\d\d\d)') # has groups
mo = reg.findall('Cell: 415-555-9999 Work: 212-555-0000')
print(mo)
# >>>[('415', '555', '1122'), ('212', '555', '0000')]
作为 findall()方法的返回结果的总结,请记住下面两点:
1.如果调用在一个没有分组的正则表达式上,例如\d\d\d-\d\d\d-\d\d\d\d,方法findall()将返回一个匹配字符串的列表,例如['415-555-9999', '212-555-0000']。
2.如果调用在一个有分组的正则表达式上,例如(\d\d\d)-(\d\d\d)-(\d\d\d\d),方法 findall()将返回一个字符串的元组的列表(每个分组对应一个字符串),例如[('415', '555', '1122'), ('212', '555', '0000')]。
8.常用字符分类的缩写代码
缩写字符分类 | 表示 |
\w | 任何字母、数字或下划线字符(可以认为是匹配“单词”字符) |
\W | 除字母、数字和下划线以外的任何字符 |
\s | 空格、制表符或换行符(可以认为是匹配“空白”字符) |
\S | 除空格、制表符和换行符以外的任何字符 |
\d | 0 到 9 的任何数字 |
\D | 除 0 到 9 的数字以外的任何字符 |
9.用点-星匹配所有字符
有时候想要匹配所有字符串。例如,假定想要匹配字符串'First Name:',接下来是任意文本,接下来是'Last Name:',然后又是任意文本。可以用点-星(.*)表示“任意文本”。示例代码如下:
# 9.用点-星匹配所有字符
nameRegex = re.compile(r'First Name: (.*) Last Name: (.*)')
mo = nameRegex.search('First Name: 波妞 Last Name: 宗介')
print(mo.group(1))
# >>>波妞
print(mo.group(2))
# >>>宗介
点-星使用“贪心”模式:它总是匹配尽可能多的文本。要用“非贪心”模式匹配所有文本,就使用点-星和问号。像和大括号一起使用时那样,问号告诉 Python 用非贪心模式匹配。
10.用句点字符匹配换行
点-星将匹配除换行外的所有字符。通过传入 re.DOTALL 作为 re.compile()的第二个参数,可以让句点字符匹配所有字符,包括换行字符。 示例代码如下:
# 10.用句点字符匹配换行
noNewlineRegex = re.compile('.*')
n = noNewlineRegex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.').group()
print(n)
# >>>'Serve the public trust.'
newlineRegex = re.compile('.*', re.DOTALL)
m = newlineRegex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.').group()
print(m)
# >>>'Serve the public trust.\nProtect the innocent.\nUphold the law.'
都看到这里了,如果觉得有用就留下你宝贵的赞吧!!!