Python之第七章 模式匹配与正则表达式

发布于:2022-11-28 ⋅ 阅读:(309) ⋅ 点赞:(0)

用正则表达式查找文本模式

创建正则表达式对象

phoneNumRegex变量包含了一个Rege对象。

re模块:Python中所有正则表达式的方法都在re模块

向re.compile()传入一个正则表达式的字符串,返回一个Regex对象,有了对象,就可以调用方法了。

匹配Regex对象

import re
phoneNumRegex=re.compile(r'\d\d\d-\d\d\d-\d\d\d\d')
mo=phoneNumRegex.search('My number is 415-666-6789.')
print(mo)
print(mo.group())

<re.Match object; span=(13, 25), match='415-666-6789'>
415-666-6789
[Finished in 72ms]

正则表达式匹配复习

1、用import re导入正则表达式模块

2、用re.compile()函数创建一个Regex对象(记得使用原始字符串)

3、向Regex对象的search()方法传入想查找的字符串。它返回一个Match对象。

4、调用Match对象的group()方法,返回实际匹配文本的字符串。

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

利用括号分组

利用括号()将正则表达式分组,使用group()方法,选择性的输出分组所匹配的文本。

向 group() 方法传 入 0 或不传入参数,将返回整个匹配的文本。

groups()方法:一次性获取所有分组

 因为 mo.groups()返回多个值的元组,所以你可以使用多重复制的技巧,每个值赋给一个独立的变量

areacode,mainnumber=mo.groups()

 括号在正则表达式中有特殊的含义,但是如果你需要在文本中匹配括号,就需要用倒斜杠对(和)进行字符转义。

import re
phoneNumRegex=re.compile(r'(\(\d\d\d\))-(\d\d\d-\d\d\d\d)')
mo=phoneNumRegex.search('My number is (415)-666-6789.')
print(mo.group(1))
print(mo.group(2))

传递给 re.compile() 的原始字符串中, \( \) 转义字符将匹配实际的括号字符。

 用管道匹配多个分组

管道符 | ,或关系,有多个正则表达式,只匹配一个就返回。

如果多个正则表达式都匹配上了,只返回第一个匹配上的。

 假设你希望匹配'Batman''Batmobile''Batcopter''Batbat'中任意一个。因为所有这 些字符串都以 Bat 开始,所以如果能够只指定一次前缀,就很方便。这可以通过括 号实现。

import re
batRegex = re.compile(r'Bat(man|mobile|copter|bat)')
mo = batRegex.search('Batmobile lost a wheel')
print(mo.group())
print(mo.group(1))

方法调用 mo.group() 返回了完全匹配的文本 'Batmobile' ,而 mo.group(1) 只是返 回第一个括号分组内匹配的文本'mobile' 。通过使用管道字符和分组括号,可以指定 几种可选的模式,让正则表达式去匹配。
如果需要匹配真正的管道字符,就用倒斜杠转义,即\|

 用问号实现可选匹配

?匹配零次或一次

字符 ? 表明它前面的分组在这个模式中是可选的。

 你可以认为?是在说,“匹配这个问号之前的分组零次或一次”。

用星号匹配零次或多次

*(称为星号)意味着“匹配零次或多次”,即星号之前的分组,可以在文本中出 现任意次。它可以完全不存在,或一次又一次地重复。让我们再来看看 Batman 的例子。
import re

batRegex = re.compile(r'Bat(wo)*man') 
mo1 = batRegex.search('The Adventures of Batman')
print(mo1.group())

mo2 = batRegex.search('The Adventures of Batwoman')
print(mo2.group())

mo3 = batRegex.search('The Adventures of Batwowowowowowoman')
print(mo3.group())
对于 'Batman' ,正则表达式的 (wo)* 部分匹配 wo 的零个实例。对于 'Batwoman' , (wo)*匹配 wo 的一个实例。对于 'Batwowowowoman' (wo)* 匹配 wo 4 个实例。

用加号匹配一次或多次

*意味着“匹配零次或多次”,+(加号)则意味着“匹配一次或多次”。星号不要求分组出现在匹配的字符串中,但加号不同,加号前面的分组必须“至少出现一次”。这不是可选的。

mo1没有匹配到字符串,因为+号要求wo至少要有一个,上不封顶。

 用花括号匹配特定次数

(ha){3}正则表达式,将匹配字符串'hahaha'

(ha){3,5}将匹配:hahaha hahahaha hahahahaha

(Ha){3,} 将匹配 3 次或更多次实例, (Ha){,5} 将匹配 0 5 次实例

花括号让正则表达式更简单:

 贪心匹配和非贪心匹配

'hahahahahahahahahahahahahahaha'中(ha){3,5}既能匹配到hahaha 也能匹配到hahahaha还能匹配到hahahahahaha,但是最后却返回了hahahahaha

这是因为:Python正则表达式默认是贪心的,会匹配到最长的字符串。

非贪心匹配:在结束的花括号后跟一个问号。

 问号在正则表达式中可能有两种含义:声明非贪心匹配或表示可选的分组。

findall()方法

search()方法 将返回一个 Match对象,包含被查找字符串中的“第一次”匹配的文本
findall() 方法将返回一组字符串,包含被查找字符串中的所有匹配

作为 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')]。

字符分类

\d 是正则表达式(0|1|2|3|4|5|6|7|8|9) 的缩写。
而Python中有许多这样的缩写字符分类:
\d                 0 到 9 的任何数字
\D                除 0 9 的数字以外的任何字符
\w                任何字母、数字或下划线字符(可以认为是匹配“单词”字符)
\W               除字母、数字和下划线以外的任何字符
\s                 空格、制表符或换行符(可以认为是匹配“空白”字符)
\S                除空格、制表符和换行符以外的任何字符
字符分类 [0-5] 只匹配数字 0 5 ,这比输入(0|1|2|3|4|5) 要短很多。

建立自己的字符分类

1、可以用方括号定义自己的字符分类。

比如:[aeiouAEIOU]可以匹配所有的元音字符。

2、使用短横表示字母或数字的范围。

例如,字符分类 [a-zA-Z0-9] 将匹配所有小写字母、大写字母和数字。
注意,在方括号内,普通的正则表达式符号不会被解释。这意味着,你不需要前面加上倒斜杠转义. * ? () 字符。例如,字符分类将匹配数字 0 5 和一个句点。你不需要将它写成[0-5\.]

3、方括号后加^,表示非字符类,非字符类将匹配不在这个字符类中的所有字符。相当于“非”。

现在,不是匹配所有元音字符,而是匹配所有非元音字符。

插入字符和美元字符

插入符号^:一般用在正则表达式开头,表示被查找的字符串必须以这个正则开头。

尽管第二个字符串‘wolrd hello’中也有hello,但是它没有开头,所以没有匹配到。

 $:该符号和插入符合相反,用在正则表达式结尾,表示被查找字符串必须以这个正则结尾。

比如:r'\d$' 表示字符串结尾必须是0-9的数字。

 通配字符

. 在我前面有个点,是不是很小,都看不清楚,正因为它小,所以它在正则表达式中匹配除了换行之外的所有字符。简称:通配符。

句点字符只匹配一个字符,这就是为什么在前面的例子中,对于文本flat,只匹配 lat。要匹配真正的句点,就是用倒斜杠转义:\.

用点—星匹配所有的字符 .*

 .*  贪心模式

.*? 非贪心匹配

两个正则表达式都可以翻译成“匹配一个左尖括号,接下来是任意字符,接下 来是一个右尖括号”。但是字符串'<To serve man> for dinner.>' 对右肩括号有两种可能的 匹配。在非贪心的正则表达式中,Python 匹配最短可能的字符串: '<To serve man>' 。 在贪心版本中,Python 匹配最长可能的字符串: '<To serve man> for dinner.>'

用句点字符匹配换行

- 星将匹配除换行外的所有字符。通过传入 re.DOTALL 作为 re.compile() 的第 二个参数,可以让句点字符匹配所有字符,包括换行字符。

正则表达式符号复习

本章介绍了许多表示法,所以这里快速复习一下学到的内容:

?匹配零次或一次前面的分组。

*匹配零次或多次前面的分组。

+匹配一次或多次前面的分组。

{n}匹配 n 次前面的分组。

{n,}匹配 n 次或更多前面的分组。

{,m}匹配零次到 m 次前面的分组。

{n,m}匹配至少 n 次、至多 m 次前面的分组。

{n,m}?*?+?对前面的分组进行非贪心匹配。

^spam 意味着字符串必须以 spam 开始。

spam$意味着字符串必须以 spam 结束。

.匹配所有字符,换行符除外。

\d\w \s 分别匹配数字、单词和空格。

\D\W \S 分别匹配出数字、单词和空格外的所有字符。

[abc]匹配方括号内的任意字符(诸如 ab c)。

[^abc]匹配不在方括号内的任意字符

不区分大小写的匹配

re.compile() 传入 re.IGNORECASE re.I ,作为第二个参数。

用 sub()方法替换字符串

sub()方法可以用新的字符串替换正则表达式匹配到的文本。

sub()方法返回替换完成后的字符串。

 有时候,你可能需要使用匹配的文本本身,作为替换的一部分。在 sub()的第一 个参数中,可以输入\1、\2、\3……。表示“在替换中输入分组 123……的文本”。 例如,假定想要隐去密探的姓名,只显示他们姓名的第一个字母。要做到这一 点,可以使用正则表达式 Agent (\w)\w*,传入 r'\1****'作为 sub()的第一个参数。字 符串中的\1 将由分组 1 匹配的文本所替代,也就是正则表达式的(\w)分组。

\1 表示正则匹配到的第一个分组保留,替换其他部分

 管理复杂的正则表达式

有时候正则表达式太长了,不便我们理解与读取,比如下面这个正则表达式:

phoneRegex = re.compile(r'((\d{3}|\(\d{3}\))?(\s|-|\.)?\d{3}(\s|-|\.)\d{4}
(\s*(ext|x|ext.)\s*\d{2,5})?)')

是不是理解起来很难,很难读。这个时候就可以添加注释与换行。但是必须要向 re.compile() 传入变量 re.VERBOSE,作为第二个参数

import re

phoneRegex = re.compile(r'''((\d{3}|\(\d{3}\))? #区号
	(\s|-|\.)? #连接符号
	\d{3} #号码头三位
	(\s|-|\.) # 连接符号
	\d{4}   #号码四位
(\s*(ext|x|ext.)\s*\d{2,5})? # 后缀
)''',reVERBOSE)

组合使用 re.IGNOREC ASE、re.DOTALL 和 re.VERBOSE

re.VERBOSE 来编写注释,还希望使用 re.IGNORECASE 来忽略大小写,该怎么办?遗憾的是, re.compile() 函数只接受一 个值作为它的第二参数。可以使用管道字符(|)将变量组合起来,从而绕过这个限 制。管道字符在这里称为“按位或”操作符。
someRegexValue = re.compile('foo', re.IGNORECASE | re.DOTALL | re.VERBOSE)

实践项目:电话号码、邮箱号码提取程序

初学者都有一个问题,我也有,接到一个项目后,第一件时间就去敲代码,这样会出现很多问题,我就不细说了。这个时候就需要我们后退一步,站在更高的角度,考虑更大的图景,先不要考虑程序,不要思考代码,先构建框架。

import pyperclip
import re

# 从剪切板获取文本
#text = 'ni de 13993919129,jkl@123.com,'
text = pyperclip.paste()

# 创建手机号码正则表达式
phoneRegex=re.compile(r'1\d{10}')
# 创建邮箱正则表达式
emailRegex=re.compile(r'\d*\w*@\d*\w*.\w*')
# 调整匹配到的字符串
phone=phoneRegex.findall(text)
phone1='\n'.join(phone)
email=emailRegex.findall(text)
email1='\n'.join(email)

retext=phone1+'\n'+email1

# 返回调整后的字符串到剪切板
print(retext)
pyperclip.copy(retext)

 网上随便找个有手机号码的网页进行复制,

 然后运行程序,就自动提取出了其中包含的手机号码,并且自动复制到了剪切板当中。

15872860000
15872869999

 实践项目:强口令检测

强口令要求:1、8位以上 2、有大小写字母 3、有数字 4、有特殊字符 ~!@#$%^&*()_+

获取用户输入 检测长度 、字母、数字、特殊字符,符合检测下一项,不符合给出提示,最终输入符合。

import pyperclip
import re


# 提示用户,获取用户输入
print('请输入需要检测的口令:')
passwd=input()

# 判断长度
if len(passwd) < 8:
	print('8位以上')
# 判断是否含有大写字母
wordRegex=re.compile(r'[A-Z]+')
word=wordRegex.search(passwd)
if word==None:
	print('必须包含大写字母')
# 判断是否含有小写字母
lordRegex=re.compile(r'[A-Z]+')
lord=wordRegex.search(passwd)
if lord==None:
	print('必须包含小写字母')
# 判断是否含有数字
numRegex=re.compile(r'\d+')
num=numRegex.search(passwd)
if num==None:
	print('必须含有数字')
specialRegex=re.compile(r'[~!@#$%^&*()_+]')
special=specialRegex.search(passwd)
if special==None:
	print('必须有特殊字符')
if len(passwd) > 8 and word!=None and lord!=None and num!=None and special!=None:
	print('你的口令是强口令!你真的是太厉害了')


本文含有隐藏内容,请 开通VIP 后查看