自学内容网 自学内容网

《Python编程实训快速上手》第五天--模式匹配与正则表达式

一、不用正则表达式查找文本模式

文本模式是一种人为规定的结构,现在有一个模式:3个数字-3个数字-4个数字

使用isPhoneNumber()函数来判断字符串是否匹配该模式

def isPhoneNumber(number):
    if len(number) != 12:
        return False
    for i in range(0,3):
        if not number[i].isdecimal():
            return False
    if number[3] != "-":
        return False
    for i in range(4,7):
        if not number[i].isdecimal():
            return False
    if number[7] != "-":
        return False
    for i in range(8,12):
        if not number[i].isdecimal():
            return False

    return True

print(isPhoneNumber(""))
print(isPhoneNumber("112-564-7896"))
print(isPhoneNumber("112-7896-532"))

 上述代码缺陷在于它仅能处理一种模式,新增一个模式后,就要增加新的代码

如果想在更长的字符串中寻找电话号码,就必须添加更多代码来寻找电话号码模式:

message = "Call me at 415-555-1011 tomorrow, 415-555-9865 is my office"
for i in range(len(message)):
    chunk = message[i:i+12]
    if isPhoneNumber(chunk):
        print("Phone number %s is found"%chunk)
print("done")

总结上述代码发现,如果在一个长度为百万的字符串中匹配模式,那么效率是十分低下的 

 二、使用正则表达式查找文本模式

1、创建正则表达式对象

1)正则表达式的函数均在re模块中,导入即可:import re

2)创建Regex模式对象:phoneNumRegex = re.compile(r"\d\d\d-\d\d\d-\d\d\d\d")

\d在正则表达式中表示一位数字字符,即0~9的数字 

2、匹配Regex对象

整体代码如下:

import re
phoneNumRegex = re.compile(r"\d{3}-\d{3}-\d{4}")
mo = phoneNumRegex.search("My number is 123-456-7890")
print("My phone number is found: " + mo.group())

首先,对象使用search方法查找传入的字符串,寻找正则表达式的所有匹配。如果未找到正则表达式模式,则返回None。如果找到了,则返回一个Match对象,使用Match对象中的group()方法,返回查找字符串中实际匹配的文本 

3、正则表达式过程匹配回顾

  1. import re
  2. re.compile()函数创建Regex对象
  3. 使用search()方法 返回Match对象
  4. 使用group()方法返回实际匹配文本的字符串

三、用正则表达式匹配更多模式

1、利用()分组

现在想将\d\d\d-\d\d\d-\d\d\d\d分成两组即\d\d\d和\d\d\d-\d\d\d\d,做法是(\d\d\d)-(\d\d\d-\d\d\d\d)

上边提到使用group()方法进行匹配字符串返回 ,因此操作如下:

mo.group()---->"123-456-7890"

mo.group(0)---->"123-456-7890"

mo.group(1)---->"123"

mo.group(2)---->"456-7890"

mo.groups()---->("123","456-7890")

group中的数字表示第几个分组

前边使用compile()方法时曾使用r进行原始字符串的转换, 从而消除\d中\的转义作用。

在正则表达式中,也存在一些特殊字符:.  ^  $  *  +  ?  {  }  [  ]  \  |  (  )

如果模式中刚好出现上述特殊字符,则需要用\进行转义操作,例如:r"(\(\d\))"

2、用管道匹配多个分组

字符 | 表示管道,r"a|b"表示匹配"a" 或"b",即若先遇到a,则返回a;先遇到b,则返回b

batRegex = re.compile(r"Batman|computer")
mo = batRegex.search("Batman has a computer")
print(mo.group())
mo = batRegex.search("A computer was bought by Batman")
print(mo.group())

也可匹配多个模式中的一个,允许提取公共项方式,假设希望匹配Batman、Batmobile、Batcopter和Batbar中的任意一个,则代码如下

batRegex = re.compile(r"Bat(man|mobile|copter|bar)")
mo = batRegex.search("Batman is rich")
print(mo.group())
print(mo.group(1))

3、用?实现可选择匹配

字符?表明它前边的分组在这个模式中是可选的,即无论这段文本在不在,正则表达式都会认为匹配。利用前边电话例子,寻找包含区号或不包含区号的电话号码:

phoneNumRegex = re.compile(r"(\d{3}-)?\d{3}-\d{4}")
mo = phoneNumRegex.search("My number is 123-456-7890")
print("My phone number is found: " + mo.group())
mo = phoneNumRegex.search("My number is 456-7890")
print("My phone number is found: " + mo.group())

4、用*匹配0次或多次

 *前的分组可以在文本中出现任意次(包括0次)

phoneNumRegex = re.compile(r"(\d{3}-)*\d{3}-\d{4}")
mo = phoneNumRegex.search("My number is 456-7890")
print("My phone number is found: " + mo.group())
mo = phoneNumRegex.search("My number is 123-456-7890")
print("My phone number is found: " + mo.group())
mo = phoneNumRegex.search("My number is 123-456-223-7890")
print("My phone number is found: " + mo.group())

 

 5、用+匹配1次或多次

+前的分组必须“至少出现1次” 

phoneNumRegex = re.compile(r"(\d{3}-)+\d{3}-\d{4}")
mo = phoneNumRegex.search("My number is 456-7890")
if mo is None:
    print("My phone number is not found")
mo = phoneNumRegex.search("My number is 123-456-7890")
print("My phone number is found: " + mo.group())
mo = phoneNumRegex.search("My number is 123-456-223-7890")
print("My phone number is found: " + mo.group())

 6、用{}匹配特定次数

\d{3}表示让\d重复3次,即\d\d\d

1)指定循环次数:将一个分组重复特定次数,就在该分组后跟上{数字}

2)指定范围:

(ha){1,3}将匹配"ha"、"haha"、"hahaha"

(ha){1,}表示不限定最大值

(ha){,5}表示匹配0~5次

四、贪心和非贪心匹配

Python的正则表达式默认是“贪心的”,在有二义性的情况下,会尽可能匹配最长的字符串,例如(ha){1,3}去查找"hahaha"时只会返回"hahaha"。

{}?表示“非贪心”版本,即会尽可能匹配最短的字符串

greedyRegex = re.compile(r"(Ha){3,5}")
mo1 = greedyRegex.search("HaHaHaHaHa")
print(mo1.group())

unGreedyRegex = re.compile(r"(Ha){3,5}?")
mo2 = unGreedyRegex.search("HaHaHaHaHa")
print(mo2.group())

五、findall()方法

search()方法只返回一个match对象,包含被查找字符串中的“第一次”匹配的文本

findall()方法将返回一组字符串列表, 包含被查找字符串中的“所有”匹配的文本

findall()方法返回结果总结:

  1. 没有分组,将返回一个匹配字符串的列表
  2. 有分组,返回一个字符串的元组的列表,每个分组对应一个字符串 

1)没有分组: 

phoneNumRegex = re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
print(phoneNumRegex.findall("Cell: 415-856-7854 work:235-000-7459"))

 

2)有分组

phoneNumRegex = re.compile(r'(\d\d\d)-(\d\d\d)-(\d\d\d\d)')
print(phoneNumRegex.findall("Cell: 415-856-7854 work:235-000-7459"))

 

六、字符分类

常见字符分类缩写代码
\d0~9的任意数字
\D除0~9数字外的任何字符
\w任何字母、数字或下划线字符(匹配“单词”字符)
\W除字母、数字和下划线以外的任何字符
\s空格、制表符或换行符(匹配“空白”字符)
\S除空格、制表符或换行符外的任何字符

 \d+\s\w+表示匹配的文本有一个或多个数字( \d+),然后是一个空白字符(\s),接下来是一个或多个字母/数字/下划线字符(\w+)

七、建立自己的字符分类

使用[]定义自己的字符分类,在[]内普通的正则表达式符号不会被解释,因此无需加\进行转义

在做方括号后加上一个^字符,即可得到“非字符类”,即不在这个字符类中的所有字符

phoneNumRegex = re.compile(r'[aeiouAEIOU]')
print(phoneNumRegex.findall("Bittersweet as life is, It's still wonderful, and It's fascinating even in tragedy"))
phoneNumRegex = re.compile(r'[^aeiouAEIOU]')
print(phoneNumRegex.findall("Bittersweet as life is, It's still wonderful, and It's fascinating even in tragedy"))

八、插入字符和美元符号

在正则表达式开始处使用^符号,表示匹配必须发生在被查找文本开始处

在正则表达式末尾使用$符号,表示该字符串必须以这个正则表达式的模式结束

同时使用^和$符号,表示整个字符串必须匹配该模式

如果字符串违背上述规定,则查找结果返回None

九、通配字符

.表示通配字符,可以匹配除换行符以外的所有字符,一个.只能匹配一个字符

 1、用.*匹配所有字符

默认为贪心模式,如果想使用非贪心模式,则用.*?

nameRegex = re.compile(r"<.*?>")
mo = nameRegex.search("<to serve man> for dinner>")
print(mo.group())

nameRegex2 = re.compile(r"<.*>")
mo = nameRegex2.search("<to serve man> for dinner>")
print(mo.group())

 

2、 句点字符匹配换行符

向re.compile()中传入re.DOTALL参数,即可让.匹配所有字符

  • 如果没有re.DOTALL参数,.将匹配所有字符直到第一个\n出现
  • 如果有该参数,将匹配所有字符
nameRegex = re.compile(r".*")
mo = nameRegex.search("serve the public trust. \nProtect the innocent. \nUphold the law")
print(mo.group())

print("----------------------")

nameRegex2 = re.compile(r".*",re.DOTALL)
mo = nameRegex2.search("serve the public trust. \nProtect the innocent. \nUphold the law")
print(mo.group())

 

十、正则表达式符号匹配回顾

 

?      匹配0次或1次前边的分组
*匹配0次或多次前边的分组
+匹配1次或多次前边的分组
{n}匹配n次前边的分组
{n,}匹配n次或更多次前边的分组
{,m}匹配0次到m次前边的分组
{n,m}匹配至少n次,至多m次前边的分组

{n,m}?

*?

+?

对前边的分组进行非贪心匹配
^spam字符换必须以spam开始
spam$字符串必须以spam结束
.匹配所有字符,换行符除外
\d,\w,\s分别匹配数字,单词和空格
\D,\W,\S分别匹配数字、单词和空格外的所有字符
[abc]匹配方括号内的任意字符
[^abc]匹配不在方括号内的任意字符

十一、不区分大小写匹配 

向re.compile()中传入re.IGNORECASE或re.I

十二、用sub()方法替换字符串

sub()方法实现找到文本模式后,用新的文本替换掉这些模式

sub(参数1,参数2)

  • 参数1:一个字符串,用于替换发现的匹配
  • 参数2:一个字符串,即正则表达式

sub返回替换完成的字符串

namesRegex = re.compile(r"Agent \w+")
print(namesRegex.sub("censored","Agent Alice gave the secret documents to Agent Bob"))

 

当使用匹配的文本本身作为替换的一部分时,在第一个参数中输入\1,\2,\3表示“在替换中输入分组1,2,3的文本

namesRegex = re.compile(r"Agent (\w)\w*")
print(namesRegex.sub(r"\1***","Agent Alice gave the secret documents to Agent Bob"))

理解:使用\1***替换匹配到的文本内容,\1表示显示分组1内容

十三、管理复杂的正则表达式

如果正则表达式太长,想要分行写,则向compile中的第二个参数传入re.VERBOSE,表示忽视正则表达式字符串中的空白符和注释

phoneRegex = re.compile(r"""(
    (\d{3} | \(\d{3}\))?          #area code
    (\s|-|\.)?                    #separator
    \d{3}                         #first 3 digits
    (\s|-|\.)                     #separator
    \d{4}                         #last 4 digits
    (\s*(ext|x|ext.)\s*\d{2,5})?  #extension
    )""",re.VERBOSE)

十四、组合使用compile中的第二个参数

使用|方法进行连接

someRegexValue = re.compile(r"foo",re.IGNORECASE | re.DOTALL | re.VERBOSE)


原文地址:https://blog.csdn.net/m0_67804957/article/details/143692978

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!