python王者归来—学习笔记(19)

第十六章 正则表达式(search与findall方法、大括号重复、小括号分组、使用管道、使用多个分组管道、使用?号*号+号查找、忽略大小写查找、贪婪与非贪婪查找)

正则表达式(Regular Expression)主要功能是执行模式的比对与查找,甚至Word文件也可以使用正则表达式处理查找(search)与替代(replace)功能。这里我们以查找一段文本中是否含有手机号码为例来说明,假设要查找的手机号码格式为 xxx-xxxx-xxxx,其中x为0-9的数字。如果不用正则表达式,使用已有知识查找,可以参考的代码如下:

def isPhoneNum(string):
    """检查字符串是否有xxx-xxxx-xxxx格式的手机号码"""
    if len(string) != 13: #长度不为12
        return False

    for i in range(3):    #前3个字符有非数字字符
        if string[i].isdecimal() == False:
            return False

    if string[3] != '-':  #第4个字符不是'-'
        return False

    for i in range(4, 8): #第5-8个字符有非数字字符
        if string[i].isdecimal() == False:
            return False

    if string[8] != '-':   #第9个字符不是'-'
        return False

    for i in range(9, 13): #第10-13个字符有非数字字符
        if string[i].isdecimal() == False:
            return False    
    return True

def parseString(string):
    """解析字符串是否含有电话号码"""
    notFound = True
    for i in range(len(string)): #用循环逐步抽取12个字符做测试
        msg = string[i:i+13]
        if isPhoneNum(msg):
            print("电话号码:", msg) #如果找到电话号码则打印电话号码
            notFound = False
    if notFound:
        print("%s 字符串不含电话号码" % string) #如果没找到电话号码则打印找不到
        
msg1 = 'Please call me using 130-2345-4565 or 150-3456-7854'
msg2 = 'lafsjflasjfjsfjsl1 13709876543'
parseString(msg1)
parseString(msg2)

一、正则表达式基础。Python有关正则表达式的方法是在re模块内,所以使用正则表达式需要导入re模块:import re。在上面程序中我们使用isdecimal( )方法判断字符是否是0—9的数字。正则表达式则使用\d表示0—9的数字字符,我们可以将手机号码xxxx-xxx-xxx改用下列正则表达式表示:'\d\d\d-\d\d\d\d-\d\d\d\d',将该表达式当作字符串放入函数内需增加转义字符'\',所以整个表达式为:'\\d\\d\\d-\\d\\d\\d\\d-\\d\\d\\d\\d',当然也可以在字符串前加r防止字符串内的转义字符被转义:r'\d\d\d-\d\d\d\d-\d\d\d\d'。从该表达式可以看到\d重复出现,对于重复出现的字符串可以用大括号内部加上重复次数方式表达,所以可以用下列方式表达:r'\d{3}-\d{4}-\d{4}'。

下面介绍两种使用正则表达式的方式查找字符串中是否含有手机号码。一种方式是使用re.compile()建立Regex对象的方式。在re模块内有compile()方法,可以将要查找的正则表达式当作字符串参数放在此方法内,然后会传回一个Regex对象:phoneRule = re.compile(r'\d{3}-\d{4}-\d{4}')。在Regex对象内有search( )方法,可以由Regex对象启用,然后将待查找的字符串放在这个方法内:phoneNum = phoneRule.search(msg)。如果找不到比对相符的字符串会传回None,如果找到比对相符的字符串会将结果传回所设定的phoneNum变量对象,这个对象在Python中称之为MatchObject对象。我们可以用MatchObject对象的group( )方法将结果传回,不过search( )将只传回第一个比对相符的字符串。使用findall()所有比对相符的字符串:phoneNum = phoneRule.findall(msg)。这个方法会将搜寻到的结果用列表方式传回,这样就不会有只显示第一个匹配字符串的缺点,如果没有比对相符的字符串就传回[ ]空列表:

import re

def parseString1(string):
    """使用正则表达式的seach方法解析字符串是否含有电话号码"""
    phoneRule = re.compile(r'\d{3}-\d{4}-\d{4}')
    phoneNum = phoneRule.search(string)  #search方法查找,返回MatchObject对象
    if phoneNum != None:
        print("电话号码:", phoneNum.group())  #使用group()方法将结果传回
    else:
        print("%s 字符串不含电话号码" % string) #如果没找到电话号码则打印找不到

def parseString2(string):
    """使用正则表达式的findall方法解析字符串是否含有电话号码"""
    phoneRule = re.compile(r'\d{3}-\d{4}-\d{4}')
    phoneNum = phoneRule.findall(string)  #findall方法查找,返回列表
    if phoneNum:
        print("电话号码:", phoneNum)            #打印列表中匹配的字符串
    else:
        print("%s 字符串不含电话号码" % string)  #如果没找到电话号码则打印找不到

msg1 = 'Please call me using 130-2345-4565 or 150-3456-7854'
msg2 = 'lafsjflasjfjsfjsl1 13709876543'
parseString1(msg1)
parseString1(msg2)

parseString2(msg1)
parseString2(msg2)

另一种方式是不使用re.compile( )方法直接将比对模式放在search和findall各自的参数内,语法格式如下:re.search(pattern, string, flag) 和 re.findall(pattern, string, flag)。上述pattern是查找的正则表达方式,string是要查找的字符串,flags可以省略,后面会介绍几个flags常用相关参数的应用。建议用第二种方式使用正则表达式:

def parseString1(string):
    """使用正则表达式的seach方法解析字符串是否含有电话号码"""
    pattern = r'\d{3}-\d{4}-\d{4}'
    phoneNum = re.search(pattern, string)  #re.search方法查找,返回MatchObject对象
    if phoneNum != None:
        print("电话号码:", phoneNum.group())  #使用group()方法将结果传回
    else:
        print("%s 字符串不含电话号码" % string) #如果没找到电话号码则打印找不到

def parseString2(string):
    """使用正则表达式的findall方法解析字符串是否含有电话号码"""
    pattern = r'\d{3}-\d{4}-\d{4}'
    phoneNum = re.findall(pattern, string)  #re.findall方法查找,返回列表
    if phoneNum:
        print("电话号码:", phoneNum)            #打印列表中匹配的字符串
    else:
        print("%s 字符串不含电话号码" % string)  #如果没找到电话号码则打印找不到

二、更多查找比对模式。

1、小括号分组。所谓小括号分组是以连字符“-”区别,然后用小括号隔开群组,可以用下列方式重新规划之前表达式:r'(\d{3})-(\d{4})-(\d{4})'。当使用re.search( )执行比对时,后面可以使用group( )传回比对符合的不同分组,例如:group( )或group(0)传回第一个比对相符的文字。group(1)则传回小括号的第一组文字,group(2)则传回小括号的第二组文字,依次类推。如果在group后面加上s,当我们使用re.search( )搜寻字符串时,可以使用groups()方法取得分组的内容,这时还可以使用多重指定获得各个分组号码。如果所查找比对的正则表达式字符串有用小括号分组,若是使用findall( )方法处理,会传回元组(tuple)的列表(list),元组内的每个元素就是查找的分组内容。

import re

string = 'Please call me using 130-2345-4565 or 150-3456-7854'
#小括号分组比对模式
pattern = r'(\d{3})-(\d{4})-(\d{4})'

phoneNum = re.search(pattern, string)
#使用group()返回比对符合的不同分组
print("完整号码是:", phoneNum.group())
print("完整号码是:", phoneNum.group(0))
print("前三位号码是:", phoneNum.group(1))
print("中间四位号码是:", phoneNum.group(2))
print("后四位号码是:", phoneNum.group(3))
#多重指定获得各个分组号码
num1, num2, num3 = phoneNum.groups()
print("groups取得分组内容:", num1, num2, num3)

#返回含元组的列表
phoneNum = re.findall(pattern, string)
print(phoneNum)

如果要查找的固化号码本身就带有小括号,比如(023)-89762564,则在处理小括号时,方式是用转义符号:\(和\),正则表达式为:r'(\(\d{3}\))-(\d{8})'。

2、使用管道|。使用管道我们可以同时查找比对多个字符串,例如,想要搜寻Mary和Tom字符串,可以使用下列表达式: pattern = 'Mary|Tom',这里单引号'和|旁边不可留空白,字母区分大小写。字符串中只要包含Mary或者Tom的字符串都会比对出来。

3、使用多个分组的管道。如果想要查找字符串John后面可以是son、nason、nathan任一个字符串的组合,可以使用下列正则表达式:pattern = John(son|nason|nathan)

import re

string = 'Johnson, Johnnason and Johnnathan will attend my party tonight.'
pattern = 'John(son|nason|nathan)'
txt1 = re.search(pattern, string)
print(txt1.group()) #打印第一个查找结果
print(txt1.group(1)) #打印第一个分组

txt2 = re.findall(pattern, string)
print(txt2)  #findall只返回各分组查找的字符串
for txt in txt2:
    print('John' + txt) #为每个分组字符串加上前缀字符串John

4、使用?号做查找。在正则表达式中若某些括号内的字符串或正则表达式可有可无(0个或1个),执行查找时皆算成功,例如,na字符串可有可无,表达方式是(na)?

import re

string = 'Please call me using 023-65782341 or 09753564'
#查找区号可有可无的固话号码
pattern = r'(\d{3}-)?(\d{8})'

phoneNum = re.search(pattern, string)
#使用group()返回比对符合的不同分组
print(phoneNum.group())

#返回含元组的列表
phoneNum = re.findall(pattern, string)
print(phoneNum)

5、使用*号做查找。在正则表达式中若某些字符串或正则表达式可从0次到多次,执行查找时皆算成功,例如,na字符串可从0次到多次,表达方式是(na)*。

6、使用+号做查找。在正则表达式中若是某些字符串或正则表达式可从1次到多次,执行查找时皆算成功,例如,na字符串可从1次到多次,表达方式是(na)+。

import re

string = 'Johnson will attend my party tonight.'
pattern = 'John(na)*son'  #匹配0个或多个na
txt = re.search(pattern, string)
print(txt.group())

string = 'Johnnananason will attend my party tonight.'
pattern = 'John(na)*son'  #匹配0个或多个na
txt = re.search(pattern, string)
print(txt.group())


string = 'Johnson will attend my party tonight.'
pattern = 'John(na)+son'  #匹配1个或多个na
txt = re.search(pattern, string)
print(txt) #直接打印对象,而不是txt.group()

string = 'Johnnanason will attend my party tonight.'
pattern = 'John(na)+son'  #匹配1个或多个na
txt = re.search(pattern, string)
print(txt.group)

7、查找时忽略大小写。查找时若是在search( )或findall( )内增加第三个参数re.I或re.IGNORECASE,搜寻时就会忽略大小写,至于打印输出时将以原字符串的格式显示

import re

string = 'john and TOM will attend my party tonight. JOHN is my best friend'
pattern = 'John|Tom'
txt = re.findall(pattern, string, re.I) #查找字符串忽略大小写
print(txt)

三、贪婪与非贪婪查找。我们之前有使用过大括号,当时讲解\d{4}代表重复4次,也就是大括号的数字是设定重复次数。可以将这个概念应用在查找一般字符串,例如,(son){3}代表所查找的字符串是‘sonsonson’,如果有一字符串是‘sonson’,则查找结果是不符。大括号除了可以设定重复次数,也可以设定指定范围,例如,(son){3,5}代表所查找的字符串如果是‘sonsonson’‘sonsonsonson’或‘sonsonsonsonson’皆算是相符合的字符串。使用大括号时,也可以省略第一或第二个数字,这相当于不设定最小或最大重复次数。例如:(son){3,}代表重复3次以上皆符合,(son){,10}代表重复10次以下皆符合

import re

def searchStr(pattern, msg):
    txt = re.search(pattern, msg)
    if txt == None:
        print("查找失败 ", txt)
    else:
        print("查找成功 ", txt.group())

msg1 = 'son'
msg2 = 'sonson'
msg3 = 'sonsonson'
msg4 = 'sonsonsonson'
msg5 = 'sonsonsonsonson'

pattern = '(son){2,4}'
searchStr(pattern, msg1)
searchStr(pattern, msg2)
searchStr(pattern, msg3)
searchStr(pattern, msg4)
searchStr(pattern, msg5)

pattern = '(son){4,}'
searchStr(pattern, msg1)
searchStr(pattern, msg2)
searchStr(pattern, msg3)
searchStr(pattern, msg4)
searchStr(pattern, msg5)

pattern = '(son){,3}'
searchStr(pattern, msg1)
searchStr(pattern, msg2)
searchStr(pattern, msg3)
searchStr(pattern, msg4)
searchStr(pattern, msg5)

上述程序中,msg4执行查找模式(son){2,4}时,表示查找2、3或4个son重复就算找到了,可是Python执行结果是列出最多重复的字符串,4次重复,这是Python的默认模式,这种模式又称贪婪(greedy)模式。另一种是列出最少重复的字符串,以这个示例代码而言是重复2次,这称为非贪婪模式,方法是在正则表达式的搜寻模式右边增加?符号:pattern = '(son){2, 4}?'

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值