第四周
python学习笔记和做的一些习题 (python编程快速上手——让繁琐工作自动化)
第七章节
贪心和非贪心匹配
Python 的正则表达式默认是“贪心”的,这表示在有二义的情况下,它们会尽
可能匹配最长的字符串。花括号的“非贪心”版本匹配尽可能最短的字符串,即在结束的花括号后跟着一个问号。
注:问号在正则表达式中可能有两个含义:声明非贪心匹配、表示可选的分组。
findall()方法
除了search方法外,Regex对象也有一个findall()方法。
== search()方法与findall()方法的区别==:search()将返回一个Match
对象,包含被查找字符串中的“第一次”匹配的文本,而 findall()方法将返回一组字符串,包含被查找字符串中的所有匹配。而且如果正则表达式中没有分组,findall()不是返回一个 Match 对象,而是返回一个字符串列表。
字符分类
常用字符分类的缩写代码:
缩写字符分类 | 表示 |
---|---|
\d | 0到9的任何数字 |
\D | 除0到9的数字以外的任何字符 |
\w | 任何字母、数字或下划线字符(可以认为是匹配“单词”字符) |
\W | 除字母、数字和下划线以外的任何字符 |
\s | 空格、制表符或换行符(可以认为是匹配“空白”字符) |
\S | 除空格、制表符和换行符以外的任何字符 |
其中:
创建自己的字符分类:
可以用方括号定义自己的字符分类,也可以使用短横表示字母或数字的范围,通过在字符分类的左方括号后加上一个插入字符(^),就可以得到“非字符类”,非字符类将匹配不在这个字符类中的所有字符。
插入字符和美元字符
可以在正则表达式的开始处使用插入符号(^),表明匹配必须发生在被查找文本开始处。
可以在正则表达式的末尾加上美元符号($),表示该字符串必须以这个正则表达式的模式结束。
若这两个符号同时使用,表明整个字符串必须匹配该模式。
通配字符
.(句点)字符称为“通配符”。它匹配除了换行之外的所有字符,他只能匹配一个字符。
正则表达式符号总结
- ?匹配零次或一次前面的分组。
- *匹配零次或多次前面的分组。
- +匹配一次或多次前面的分组。
- {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]匹配不在方括号内的任意字符。
不区分大小写的匹配
通常来讲,正则表达式用你指定的大小写匹配文本,但是,有时候你只关心匹配字母,不关心它们是大写或小写。要让正则表达式不区分大小写,可以向 re.compile()传入 re.IGNORECASE 或 re.I,作为第二个参数。
用sub()方法替换字符串
Regex对象的 sub()方法需要传入两个参数。第一个参数是一个字符串,用于取代发现的匹配。第二个参数是一个字符串,即正则表达式。sub()方法返回替换完成后的字符串。
管理复杂的正则表达式
匹配复杂的文本模式,可能需要长的、费解的正则表达式。你可以告诉 re.compile(),忽略正则表达式字符串中的空白符和注释,从而缓解这一点。要实现这种详细模式,可以向 re.compile()传入变量 re.VERBOSE,作为第二个参数。
phoneRegex = re.compile(r'''(
(\d{3}|\(\d{3}\))? # 区号
(\s|-|\.)? # 分隔符
(\d{3}) # 前3位数字
(\s|-|\.) # 分隔符
(\d{4}) # 后4位数字
(\s*(ext|x|ext.)\s*\d{2,5})? # 扩展名
)''', re.VERBOSE)
组合使用re.IGNORECASE、re.DOTALL和re.VERBOSE:
如果要同时使用re.VERBOSE、re.IGNOREC ASE,可以使用管道字符(|)将变量组合起来,这样就不受re.compile()函数只接收一个值作为第二个参数的限制。管道字符在这里称为“按位或”操作符。
项目:电话号码和E-mail地址提取程序
import re,pyperclip
#为电话号码写一个正则表达式
phoneRegex = re.compile(r'''(
(\d{3})
(\s|-|\.)?
(\d{3})
(\s|-|\.)
(\d{4})
(\s*(ext|x|ext.)\s*\d{2,5})?
)''', re.VERBOSE)
#为E-mail地址创建一个正则表达式
emailRegex = re.compile(r'''(
[a-zA-Z0-9._%+-]+
@
[a-zA-Z0-9.-]+
(\.[a-zA-Z]{2,4})
)''',re.VERBOSE )
#在剪贴文本中找到所有匹配
text = str(pyperclip.paste())
matches = []
for groups in phoneRegex.findall(text):
phoneNum = '-'.join([groups[1],groups[3],groups[5]])
#不知道书上下面两句是什么意思
# if groups[8] != ' ':
# phoneNum += ' x' + groups[8]
matches.append(phoneNum)
for groups in emailRegex.findall(text):
matches.append(groups[0])
# 所有匹配连接成一个字符串,复制到剪贴板
if len(matches) > 0:
pyperclip.copy('\n'.join(matches))
print('复制到剪贴板:')
print('\n'.join(matches))
else:
print('没有找到电话号码和邮箱')
第七章节习题
1.创建 Regex 对象的函数是什么?
re.compile()函数。
2.在创建 Regex 对象时,为什么常用原始字符串?
使用原始字符串是为了让反斜杠不必转义。
3.search()方法返回什么?
Match对象。
4.通过 Match 对象,如何得到匹配该模式的实际字符串?
group()方法返回匹配文本的字符串。
5.用 r’(\d\d\d)-(\d\d\d-\d\d\d\d)'创建的正则表达式中,分组 0 表示什么?分组 1呢?分组 2 呢?
分组0表示整个匹配,分组1包含第一组括号,分组2包含第二组括号。
6.括号和句点在正则表达式语法中有特殊的含义。如何指定正则表达式匹配真正的括号和句点字符?
句号和括号可以用反斜杠转义:.、(和) 。
7.findall()方法返回一个字符串的列表,或字符串元组的列表。是什么决定它提供哪种返回?
如果正则表达式没有分组,就返回字符串的列表。如果正则表达式有分组,就返回字符串元组的列表。
8.在正则表达式中,|字符表示什么意思?
表示匹配符号左右两边任意一个。
9.在正则表达式中,?字符有哪两种含义?
?字符可以表示"匹配前面分组0次或1次",或用于表示非贪婪匹配。
10.在正则表达式中,+和*字符之间的区别是什么?
+匹配1次或多次。*匹配0次或多次。
11.在正则表达式中,{3}和{3,5}之间的区别是什么?
{3}精确匹配前面分组的3次实例。{3,5}匹配3至5次实例。
比如(Ha){3}表示HaHaHa
(Ha){3,5}表示HaHaHa或HaHaHaHa或HaHaHaHaHa
12.在正则表达式中,\d、\w 和\s 缩写字符类是什么意思?
数字、单词和空白字符。
13.在正则表达式中,\D、\W 和\S 缩写字符类是什么意思?
分别匹配一个字符,它不是数字、单词或空白字符。
14.如何让正则表达式不区分大小写?
将re.I 或者 re.IGNORECASE作为第二个参数传入re.compile(),让匹配不区分大小写。
15.字符.通常匹配什么?如果 re.DOTALL 作为第二个参数传递给re.compile(),它会匹配什么?
字符.通常匹配任何字符,换行符除外。
16..* 和 *?之间的区别是什么?
.表示贪婪匹配,.?表示非贪婪匹配。
17.匹配所有数字和小写字母的字符分类语法是什么?
[0-9a-z]或[a-z0-9]
18.如果 numRegex = re.compile(r’\d+’),那么 numRegex.sub(‘X’, ‘12 drummers, 11 pipers, five rings, 3 hens’)返回什么?
‘X drummers,X pipers,five rings,X hens’
19.将 re.VERBOSE 作为第二个参数传递给 re.compile(),让你能做什么?
添加空格和注释
20.如何写一个正则表达式,匹配每 3 位就有一个逗号的数字?它必须匹配以下数字:
- ‘42’
- ‘1,234’
- ‘6,368,745’
但不会匹配: - ‘12,34,567’ (逗号之间只有两位数字)
- ‘1234’ (缺少逗号)
参考链接https://blog.csdn.net/daaa2019/article/details/102963975
import re
threeNumRegex = re.compile(r'''
(?:(?<![\d|\,])\d{1,3}(?=\s)) #匹配只有1-3位数,左边
|
(?<![\d|\,])(?:\d{1,3})(?:\,\d{3})+(?=\s) #匹配带有","的情况
''', re.VERBOSE)
text = '''
42
1,234
6,368,745
1234
12,34,567
'''
result = threeNumRegex.findall(text)
print(result)
21.如何写一个正则表达式,匹配姓 Nakamoto 的完整姓名?你可以假定名字总是出现在姓前面,是一个大写字母开头的单词。该正则表达式必须匹配:
- ‘Satoshi Nakamoto’
- ‘Alice Nakamoto’
- ‘RoboCop Nakamoto’
但不匹配: - ‘satoshi Nakamoto’(名字没有大写首字母)
- ‘Mr. Nakamoto’(前面的单词包含非字母字符)
- ‘Nakamoto’ (没有名字)
- ‘Satoshi nakamoto’(姓没有首字母大写)
import re
nameRegex = re.compile(r'[A-Z][a-zA-Z]*\sNakamoto')
text = '''
Satoshi Nakamoto
Alice Nakamoto
RoboCop Nakamoto
satoshi Nakamoto
Mr. Nakamoto
Nakamoto
Satoshi nakamoto
'''
result = nameRegex.findall(text)
print(result)
22.如何编写一个正则表达式匹配一个句子,它的第一个词是 Alice、Bob 或Carol,第二个词是 eats、pets 或 throws,第三个词是 apples、cats 或 baseballs。该句子以句点结束。这个正则表达式应该不区分大小写。它必须匹配:
- ‘Alice eats apples.’
- ‘Bob pets cats.’
- ‘Carol throws baseballs.’
- ‘Alice throws Apples.’
- ‘BOB EATS CATS.’
但不匹配: - ‘RoboCop eats apples.’
- ‘ALICE THROWS FOOTBALLS.’
- ‘Carol eats 7 cats.’
import re
senRegex = re.compile(r'(Alice|Bob|Carol)\s(eats|pets|throws)\s(apples|cats|baseablls)\.',re.I)
text = '''
'Alice eats apples. '
'Bob pets cats.'
'Carol throws baseballs.'
'Alice throws Apples.'
'BOB EATS CATS.'
'RoboCop eats apples.'
'ALICE THROWS FOOTBALLS.'
'Carol eats 7 cats.'
'''
result = senRegex.findall(text)
print(''.join(text))
这里还有一点问题,因为输出的不是预期中的句子
第七章实践项目
强口令检测
写一个函数,它使用正则表达式,确保传入的口令字符串是强口令。强口令的定义是:长度不少于 8 个字符,同时包含大写和小写字符,至少有一位数字。你可能需要用多个正则表达式来测试该字符串,以保证它的强度。
import re
print("强口令要求:字符串长度不少于8个,同时包含大小写,且至少含有一位数字。")
strongStr = input("请输入一个强口令:")
def strongPassword(str): #定义正则表达式函数
lengthRex1 = re.compile(r'[a-zA-Z0-9]{8,}')
lengthRex2 = re.compile(r'[A-Z]+')
lengthRex3 = re.compile(r'[a-z]+')
lengthRex4 = re.compile(r'[0-9]+')
if lengthRex1.search(strongStr) != None:
print('长度8以上')
if lengthRex2.search(strongStr) != None:
print('有大写')
if lengthRex3.search(strongStr) != None:
print('有小写')
if lengthRex4.search(strongStr) != None:
print('有数字')
print(lengthRex1.search(strongStr))
return True
else:
print('没有数字')
return False
else:
print('没有小写')
return False
else:
print('没有大写')
return False
else:
print('长度不够8,或者有其他字符')
return False
while strongPassword(strongStr) == False: #判断函数返回值是否为False,循环调用函数strongPassword()
print('口令强度不够!请重新输入:')
strongStr = input() #重新输入更新字符变量
strongPassword(strongStr)#再次调用函数
else:
print('Nice!强口令强度足够,可以使用!')
这里不知道为啥一个强口令会重复判断两次
strip()的正则表达式版本
写一个函数,它接受一个字符串,做的事情和 strip()字符串方法一样。如果只传入了要去除的字符串,没有其他参数,那么就从该字符串首尾去除空白字符。否则,函数第二个参数指定的字符将从该字符串中去除。
这个题没有看太明白,参考了链接:https://blog.csdn.net/dongyu1703/article/details/81782081
import re
def REstrip(text, param=' '):
mo = re.compile(r'^([' + str(param) + r']*)(.*?)([' + str(param) + ']*)$')
result = mo.search(text)
if (result != None):
print(result.group(2))
text = input("Please input the text: ")
param = input("Please input the param: ")
REstrip(text, param)