用正则表达式查找文本模式
创建正则表达式对象
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()方法,选择性的输出分组所匹配的文本。
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))
用管道匹配多个分组
管道符 | ,或关系,有多个正则表达式,只匹配一个就返回。
如果多个正则表达式都匹配上了,只返回第一个匹配上的。
假设你希望匹配'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))
用问号实现可选匹配
?匹配零次或一次
你可以认为?是在说,“匹配这个问号之前的分组零次或一次”。
用星号匹配零次或多次
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())
用加号匹配一次或多次
*意味着“匹配零次或多次”,+(加号)则意味着“匹配一次或多次”。星号不要求分组出现在匹配的字符串中,但加号不同,加号前面的分组必须“至少出现一次”。这不是可选的。
mo1没有匹配到字符串,因为+号要求wo至少要有一个,上不封顶。
用花括号匹配特定次数
(ha){3}正则表达式,将匹配字符串'hahaha'
(ha){3,5}将匹配:hahaha hahahaha hahahahaha
花括号让正则表达式更简单:
贪心匹配和非贪心匹配
'hahahahahahahahahahahahahahaha'中(ha){3,5}既能匹配到hahaha 也能匹配到hahahaha还能匹配到hahahahahaha,但是最后却返回了hahahahaha
这是因为:Python正则表达式默认是贪心的,会匹配到最长的字符串。
非贪心匹配:在结束的花括号后跟一个问号。
问号在正则表达式中可能有两种含义:声明非贪心匹配或表示可选的分组。
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')]。
字符分类
建立自己的字符分类
1、可以用方括号定义自己的字符分类。
比如:[aeiouAEIOU]可以匹配所有的元音字符。
2、使用短横表示字母或数字的范围。
3、方括号后加^,表示非字符类,非字符类将匹配不在这个字符类中的所有字符。相当于“非”。
现在,不是匹配所有元音字符,而是匹配所有非元音字符。
插入字符和美元字符
插入符号^:一般用在正则表达式开头,表示被查找的字符串必须以这个正则开头。
尽管第二个字符串‘wolrd hello’中也有hello,但是它没有开头,所以没有匹配到。
$:该符号和插入符合相反,用在正则表达式结尾,表示被查找字符串必须以这个正则结尾。
比如:r'\d$' 表示字符串结尾必须是0-9的数字。
通配字符
. 在我前面有个点,是不是很小,都看不清楚,正因为它小,所以它在正则表达式中匹配除了换行之外的所有字符。简称:通配符。
句点字符只匹配一个字符,这就是为什么在前面的例子中,对于文本flat,只匹配 lat。要匹配真正的句点,就是用倒斜杠转义:\.。
用点—星匹配所有的字符 .*
.* 贪心模式
.*? 非贪心匹配
用句点字符匹配换行
正则表达式符号复习
本章介绍了许多表示法,所以这里快速复习一下学到的内容:
?匹配零次或一次前面的分组。
*匹配零次或多次前面的分组。
+匹配一次或多次前面的分组。
{n}匹配 n 次前面的分组。
{n,}匹配 n 次或更多前面的分组。
{,m}匹配零次到 m 次前面的分组。
{n,m}匹配至少 n 次、至多 m 次前面的分组。
{n,m}?或*?或+?对前面的分组进行非贪心匹配。
^spam 意味着字符串必须以 spam 开始。
spam$意味着字符串必须以 spam 结束。
.匹配所有字符,换行符除外。
\d、\w 和\s 分别匹配数字、单词和空格。
\D、\W 和\S 分别匹配出数字、单词和空格外的所有字符。
[abc]匹配方括号内的任意字符(诸如 a、b 或 c)。
[^abc]匹配不在方括号内的任意字符
不区分大小写的匹配
用 sub()方法替换字符串
sub()方法可以用新的字符串替换正则表达式匹配到的文本。
sub()方法返回替换完成后的字符串。
有时候,你可能需要使用匹配的文本本身,作为替换的一部分。在 sub()的第一 个参数中,可以输入\1、\2、\3……。表示“在替换中输入分组 1、2、3……的文本”。 例如,假定想要隐去密探的姓名,只显示他们姓名的第一个字母。要做到这一 点,可以使用正则表达式 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
实践项目:电话号码、邮箱号码提取程序
初学者都有一个问题,我也有,接到一个项目后,第一件时间就去敲代码,这样会出现很多问题,我就不细说了。这个时候就需要我们后退一步,站在更高的角度,考虑更大的图景,先不要考虑程序,不要思考代码,先构建框架。
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('你的口令是强口令!你真的是太厉害了')