1.不用正则表达式来查找文本模式
def isPhoneNumber(text):
if len(text) != 12:
return False
for i in range(0, 3):
if not text[i].isdecimal(): #检查字符串是否只包含十进制数字
return False
if text[3] != '-':
return False
for i in range(4, 7):
if not text[i].isdecimal():
return False
if text[7] != '-':
return False
for i in range(8, 12):
if not text[i].isdecimal():
return False
return True
print('415-555-422 is a phone number:')
print(isPhoneNumber('415-555-4242'))
print('Moshi moshi is a phone number:')
print(isPhoneNumber('Moshi moshi'))
上面代码中先定义一个isPhoneNumber()函数,先检查被传入的字符串长度是否为12,然后检查前3个字符是否都是数字,接着检查第4个字符是否为'-',然后检查第5-7个字符是否都是数字,接着检查第8个字符是否为'-',最后检查第9-12个字符是否都是数字。如果所有检查都通过,则返回True,否则返回False。比价繁琐
message = 'Call me at 415-555-1011 tomorrow. 415-555-9999 is my office.'
for i in range(len(message)): #遍历字符串
chunk = message[i:i+12] #切片,每次取12个字符
print(chunk)
if isPhoneNumber(chunk): #检查是否是电话号码
print('Phone number found: ' + chunk)
print('Done')
字典切片 每次取12个字符--还得多多实践啊
2.用正则表达式查找文本模式
2.1创建正则表达式对象
python中所有的正则表达式都在re模块中,要使用正则表达式,首先需要导入re模块
向re.compile()传入一个字符串值,re.compile()将返回一个Regex对象。将Regex对象赋值给一个变量,就可以使用Regex对象调用search()方法了。
Regex 对象的 search()方法查找传入的字符串,寻找该正则表达式的所有匹配。如果字符串中没有找到该正则表达式模式,search()方法将返回 None。如果找到了该模式,search()方法将返回一个 Match 对象,Match 对象有一个 group()方法,它返回被查找字符串中实际匹配的文本
import re
message = 'Call me at 415-555-1011 tomorrow. 415-555-9999 is my office.'
phoneNumRegex = re.compile(r'\d{3}-\d{3}-\d{4}') #创建Regex对象 r表示原始字符串 \d表示数字 {3}表示3个数字
mo = phoneNumRegex.search(message) # search()方法查找字符串中的模式
print('Phone number found: ' + mo.group()) # group()方法返回被查找字符串中实际匹配的文本 只会返回第一个匹配项
Phone number found: 415-555-1011
也可以直接调用search方法
import re
message = 'Call me at 415-555-1011 tomorrow. 415-555-9999 is my office.'
mo = re.search(r'\d{3}-\d{3}-\d{4}',message) # search()方法查找字符串中的模式
print('Phone number found: ' + mo.group()) # group()方法返回被查找字符串中实际匹配的文本 只会返回第一个匹配项
思考
这里compile()函数的作用是什么?为什么可以直接调用search(),findall(),match()方法?
compile()函数的作用是将正则表达式编译成一个Regex对象,然后调用Regex对象的search(),findall(),match()方法,可以更方便地使用正则表达式。
- 增加了代码可读性
- 提高了正则表达式的复用率
- 提高匹配效率:预编译正则表达式模式,避免在每次匹配时重新解析模式
2.2正则表达式复习
- 用 import re 导入正则表达式模块。
- 用 re.compile()函数创建一个 Regex 对象(记得使用原始字符串)。
- 向 Regex 对象的 search()方法传入想查找的字符串。它返回一个 Match 对象。
- 调用 Match 对象的 group()方法,返回实际匹配文本的字符串。
3.用正则表达式匹配多个模式
3.1利用括号分组
假定要将区号从电话号码中分离,在正则表达式中添加括号创建分组 (\d\d\d)-(\d\d\d-\d\d\d\d),括号内就是分组,group()方法将返回括号内匹配的文本
import re
message = 'Call me at 415-555-1011 tomorrow. 415-555-9999 is my office.'
phoneNumRegex = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)') #创建Regex对象 r表示原始字符串 \d表示数字 {3}表示3个数字
mo = phoneNumRegex.search(message) # search()方法查找字符串中的模式
print('Phone number found: ' + mo.group(1)) # group()方法返回被查找字符串中实际匹配的文本
print('Phone number found: ' + mo.group(2))
print('Phone number found: ' + mo.group(0))
Phone number found: 415
Phone number found: 555-1011
Phone number found: 415-555-1011
group()方法有一个可选的参数,用于获取指定分组的内容。group(0)总是返回整个匹配的文本。group(1)返回第一个括号内匹配的文本,group(2)返回第二个括号内匹配的文本,以此类推。
3.2用管道匹配多个模式
要查找多种分组,可以使用管道字符(|)来指定多种分组,用管道字符将不同的分组选项分隔开
import re
heroRegex = re.compile(r'Batman|Tina Fey') #如果 Batman 和 Tina Fey 都出现在被查找的字符串中,第一次出现的匹配文本,
将作为 Match 对象返回
mo1 = heroRegex.search('Batman and Tina Fey.') # search()方法查找字符串中的模式
print(mo1.group())
mo2 = heroRegex.search('Tina Fey and Batman.') # search()方法查找字符串中的模式
print(mo2.group())
3.3用问号实现可选匹配
有时候,想匹配的模式是可选的,也就是说,不论这段文本在不在,正则表达式都会认为匹配成功。要实现可选匹配,可以在模式中添加问号(?)。问号表明它前面的分组在匹配时是可选的
import re
batRegex = re.compile(r'Bat(wo)?man') # wo是可选的
mo1 = batRegex.search('The Adventures of Batman') # search()方法查找字符串中的模式
print(mo1.group())
mo2 = batRegex.search('The Adventures of Batwoman') # search()方法查找字符串中的模式
print(mo2.group())
利用前面的例子,可以让正则表达式寻找包含区号或不包含区号的电话号码。
import re
phoneRegex = re.compile(r'(\d\d\d-)?\d\d\d-\d\d\d\d') # ?表示前面的字符是可选的
mo1 = phoneRegex.search('My number is 415-555-4242.') # search()方法查找字符串中的模式
print(mo1.group())
mo2 = phoneRegex.search('My number is 555-4242.') # search()方法查找字符串中的模式
print(mo2.group())
3.4用星号匹配零次或多次
星号(*)匹配零次或多次前面的分组。星号不要求分组出现在匹配的文本中,因此它也是可选的。星号和问号都可以匹配前面的分组出现零次或多次,问号是匹配零次或一次,二者之间的唯一区别是星号匹配任意次数,包括零次。
import re
batRegex = re.compile(r'Bat(wo)*man') # wo是可选的
mo1 = batRegex.search('The Adventures of Batman') # search()方法查找字符串中的模式
print(mo1.group())
mo2 = batRegex.search('The Adventures of Batwoman') # search()方法查找字符串中的模式
print(mo2.group())
mo3 = batRegex.search('The Adventures of Batwowowowoman') # search()方法查找字符串中的模式
print(mo3.group())
3.5用加号匹配一次或多次
加号(+)和星号类似,也是匹配前面的分组出现一次或多次,但加号要求分组的至少出现一次。
import re
batRegex = re.compile(r'Bat(wo)+man') # wo至少出现一次
mo1 = batRegex.search('The Adventures of Batman') # search()方法查找字符串中的模式
print(mo1 == None)
mo2 = batRegex.search('The Adventures of Batwoman') # search()方法查找字符串中的模式
print(mo2.group())# mo2.group()返回匹配的文本
3.6用花括号匹配特定次数
要匹配的文本重复特定次数,可在花括号({})中指定重复次数。在花括号中只指定一个数字,正则表达式将匹配正好出现指定次数的文本。还可以在花括号中指定一个范围,花括号中的逗号之前是最低重复次数,之后是最高重复次数。
import re
haRegex = re.compile(r'(Ha){3}') #匹配3次Ha
mo1 = haRegex.search('HaHaHa') # search()方法查找字符串中的模式
print(mo1.group()) # mo1.group()返回匹配的文本 匹配三次
mo2 = haRegex.search('Ha') # search()方法查找字符串中的模式
print(mo2 == None) # mo2 == None表示没有匹配的文本 即没有匹配Ha三次
mo3 = haRegex.search('HaHa') # search()方法查找字符串中的模式
print(mo3 == None)
mo4 = haRegex.search('HaHaHaHa') # search()方法查找字符串中的模式
print(mo4 == None) # 匹配四次 包含了三次
mo5 = haRegex.search('HaHaHaHaHa') # search()方法查找字符串中的模式
print(mo5 == None)
指定范围
import re
haRegex = re.compile(r'(Ha){3,5}') #匹配3次Ha
mo1 = haRegex.search('HaHaHa') # search()方法查找字符串中的模式
print(mo1.group()) # mo1.group()返回匹配的文本 匹配三次
mo2 = haRegex.search('Ha') # search()方法查找字符串中的模式
print(mo2 == None) # mo2 == None表示没有匹配的文本 即没有匹配Ha三次
mo3 = haRegex.search('HaHa') # search()方法查找字符串中的模式
print(mo3 == None)
mo4 = haRegex.search('HaHaHaHa') # search()方法查找字符串中的模式
print(mo4 == None)
mo5 = haRegex.search('HaHaHaHaHa') # search()方法查找字符串中的模式
print(mo5 == None)
4.贪心和非谈心匹配
在字符串'HaHaHaHa'中,正则表达式(Ha){3,5}可以匹配3次、4次、5次或6次'Ha'。这种正则表达式被称为贪心匹配,它总是匹配尽可能多的文本。要让正则表达式采用非贪心方式匹配,可以在量词后面加上问号(?)。
import re
haRegex = re.compile(r'(Ha){3,5}?') #
mo5 = haRegex.search('HaHaHaHaHa') # search()方法查找字符串中的模式
print(mo5.group()) # mo5.group()返回匹配的文本 匹配五次
HaHaHa
import re
haRegex = re.compile(r'(Ha){3,5}') #
mo5 = haRegex.search('HaHaHaHaHa') # search()方法查找字符串中的模式
print(mo5.group()) # mo5.group()返回匹配的文本 匹配五次
HaHaHaHaHa
非贪心匹配和贪心匹配的区别
贪心():尽可能多的匹配 非贪心(花括号加?)表示尽可能匹配最短的字符串
5.findall()方法
secarch()方法只返回字符串中的一个匹配项。要在字符串中找到所有的匹配项,可以使用findall()方法。调用findall()方法时,它返回一个字符串列表,每个字符串都是匹配的文本。findall()方法在字符串中查找所有非重叠的模式匹配项,然后返回一个包含所有匹配项的列表。
findall()方法返回一个字符串列表,每个字符串都是匹配的文本。在正则表达式中,字符串列表与使用for循环遍历匹配对象所得到的结果相同。
phoneNumRegex = re.compile(r'\d{3}-\d{3}-\d{4}')
mo = phoneNumRegex.search('Cell: 415-555-9999 Work: 212-555-0000')
mo.group()
输出:'415-555-9999'
phoneNumRegex = re.compile(r'\d{3}-\d{3}-\d{4}')
mo = phoneNumRegex.findall('Cell: 415-555-9999 Work: 212-555-0000')
mo
输出:['415-555-9999', '212-555-0000']
如果正则表达式中有分组 那么 findall()方法将返回一个元组列表,每个元组表示一个匹配项。
phoneNumRegex = re.compile(r'(\d{3})-(\d{3})-(\d{4})')
mo = phoneNumRegex.findall('Cell: 415-555-9999 Work: 212-555-0000')
print(mo)
输出:[('415', '555', '9999'), ('212', '555', '0000')]
作为 findall()方法的返回结果的总结,请记住下面两点:
- 如果调用在一个没有分组的正则表达式上,例如\d\d\d-\d\d\d-\d\d\d\d,方法findall()将返回一个匹配字符串的列表,例如['415-555-9999', '212-555-0000']。
- 如果调用在一个有分组的正则表达式上,例如(\d\d\d)-(\d\d\d)-(\d\d\d\d),方法 findall()将返回一个字符串的元组的列表(每个分组对应一个字符串),例如[('415',
'555', '1122'), ('212', '555', '0000')]。
6.字符分类
\d 表示0-9的任何数字
\D 表示任何非数字字符
\w 表示任何字母、数字或下划线字符(可以认为是匹配“单词”字符)。等价于[A-Za-z0-9_]。
\W 表示任何非单词字符。
\s 表示任何空格字符,包括空格、制表符、换页符等。等价于[\t\n\r\f\v]。
\S 表示任何非空白字符。
xmasRegex = re.compile(r'\d+\s\w+')
mo = xmasRegex.findall('12 drummers, 11 pipers, 10 lords, 9 ladies, 8 maids, 7\
swans, 6 geese, 5 rings, 4 birds, 3 hens, 2 doves, 1 partridge')
print(mo)
输出:['12 drummers', '11 pipers', '10 lords', '9 ladies', '8 maids', '7 swans', '6 geese', '5 rings', '4 birds', '3 hens', '2 doves', '1 partridge']
7.建立自己的字符分类
有时候你想匹配一组字符,但是\d、\w和\s等字符分类太宽泛了。在这种情况下,你可以使用方括号定义自己的字符分类。例如,\d表示数字0~9,但如果你只想匹配数字2、3、4、5、6、7、8、9,你可以使用[23456789]。
import re
vowelRegex = re.compile(r'[aeiouAEIOU]')
mo = vowelRegex.findall('Robocop eats baby food. BABY FOOD.')
print(mo)
输出:['o', 'o', 'o', 'e', 'a', 'o', 'o', 'o', 'A', 'O', 'O', 'O', 'O', 'O']
方括号内的字符分类是区分大小写的。因此,[AEIOU]只匹配大写元音,而[aeiou]只匹配小写元音。如果你想匹配的大小写元音,可以同时列出这两个分类,如[AaEeIiOoUu]。
8.取反字符分类
在方括号内的字符分类前面加上一个脱字符(^),就可以创建“取反”或“否定”字符分类。即,匹配任何不在方括号内的字符。
import re
vowelRegex = re.compile(r'[^aeiouAEIOU]')
mo = vowelRegex.findall('Robocop eats baby food. BABY FOOD.')
print(mo)
也可以使用短横线表示字母或数字的范围。例如,[a-z0-9]匹配所有小写字母和数字,[A-Z0-9]匹配所有大写字母和数字。
9.插入字符和美元字符
插入字符()匹配字符串的开始,美元字符($)匹配字符串的结尾。例如,Hello匹配以Hello开头的字符串,Hello$匹配以Hello结尾的字符串。
import re
beginsWithHello = re.compile(r'^Hello')
mo1 = beginsWithHello.search('Hello world!')
print(mo1.group())
mo2 = beginsWithHello.search('He said hello.')
print(mo2 == None)
输出:
Hello
True
import re
endsWithNumber = re.compile(r'\d$')
mo1 = endsWithNumber.search('Your number is 42') # search()方法查找字符串中的模式
print(mo1.group())
print(endsWithNumber.findall('Your number is 42'))
输出:
2
['2']
注意 r'\d$'用户匹配单个数字结尾的字符串 而不是整个字符串所以会输出2
import re
endsWithNumber = re.compile(r'\d+$')
mo1 = endsWithNumber.search('Your number is 42')
print(mo1.group(0)) # 输出整个匹配项
print(endsWithNumber.findall('Your number is 42'))
输出:
42
['4', '2']
10.通配字符
句点字符(.)匹配除换行符\n之外的任何字符。句点字符不是正则表达式中的特殊字符,所以你不需要对其进行转义。例如,r.b.匹配任何以字母b开头、后面跟着任意字符、然后是字母b的字符串(注意,这个匹配b字符的任意实例,而不仅仅是下一个字符)。
import re
atRegex = re.compile(r'.at')
mo = atRegex.findall('The cat in the hat sat on the flat mat.')
print(mo)
输出:['cat', 'hat', 'sat', 'lat', 'mat']
如果你想匹配包括换行符\n在内的任何字符,你可以向re.compile()方法传入re.DOTALL作为第二个参数。例如:
import re
noNewlineRegex = re.compile('.*', re.DOTALL)
mo = noNewlineRegex.search('Serve the public trust.\nProtect the innocent.\nUphold the law.')
print(mo.group())
.*匹配字符串中的所有字符,re.DOTALL让句点字符(.)匹配所有字符,包括换行符。
输出:
Serve the public trust.
Protect the innocent.
Uphold the law.
11.正则表达式符号复习
? 匹配前面的字符0次或1次
- 匹配前面的字符0次或多次
- 匹配前面的字符1次或多次
{n} 匹配前面的字符n次
{n,} 匹配前面的字符n次或多次
{n,m} 匹配前面的字符n到m次
^ 匹配字符串的开头 ^spam 意味着字符串必须以spam开头
$ 匹配字符串的结尾 ^spam 意味着字符串必须以spam结尾
. 匹配任何字符(除了\n)
[...] 匹配字符分类中的任意一个字符
[^...] 匹配不在字符分类中的任意一个字符
\d \w \s 匹配数字、单词字符、空白字符
\D \W \S 匹配非数字、非单词字符、非空白字符
12.不区分大小写的匹配
如果你不关心正则表达式匹配的大小写,可以给re.compile()方法传入re.IGNORECASE(或re.I)作为第二个参数,像这样:
import re
robocop = re.compile(r'robocop', re.I)
mo1 = robocop.search('Robocop is part man, part machine, all cop.')
print(mo1.group())
mo2 = robocop.search('ROBOCOP always gets to the chase.')
print(mo2.group())
输出:
Robocop
ROBOCOP
一个能让你靠自学快速学会任何东西的顶级思维:先做个垃圾出来,然后看看别人是怎么做的,最后再模仿着做出来。
矛盾 主要矛盾
13.用sub()方法替换字符串
将字符串中所有以"Agent "开头的名字替换为只显示首字母和四个星号的形式。
agentNamesRegex = re.compile(r'Agent (\w)\w*')
m1 = agentNamesRegex.sub(r'\1****', 'Agent Alice told Agent Carol that Agent Eve knew Agent Bob was a double agent.')
print(m1)
输出:
Agent A**** told Agent C**** that Agent E**** knew Agent B**** was a double agent.
必须搞明白下面这个例子
re.sub(pattern, repl, string, count=0, flags=0)
import re
# 基本替换示例
text = "Hello, World!"
new_text = re.sub("World", "Python", text)
print(new_text) # 输出: Hello, Python!
# 使用分组引用进行替换
text = "Agent Alice"
new_text = re.sub(r"Agent (\w+)", r"Agent \1****", text)
print(new_text) # 输出: Agent A****
# 使用函数进行替换
def replace_with_upper(match):
return match.group(1).upper()
text = "abc123"
new_text = re.sub(r'[a-zA-Z+]', replace_with_upper, text)
print(new_text) # 输出: ABC123
'''
在这段代码中,re.sub函数的第二个参数是replace_with_upper函数。re.sub在执行时会遍历字符串text,寻找所有与正则表达式r'[a-zA-Z]'匹配的子串。每当它找到一个匹配项时,就会调用replace_with_upper函数,并将一个代表这次匹配的re.Match对象传递给replace_with_upper函数。
在replace_with_upper函数内部,你可以通过match.group()获取匹配的文本,然后对这个文本进行处理。在这个例子中,我们将匹配的文本转换为大写。
'''
# 限制替换次数
text = "one1 two2 three3 four4"
new_text = re.sub(r"\d", "0", text, count=2)
print(new_text) # 输出: one0 two0 three3 four4
14.管理复杂的正则表达式
可以这样写,增加代码可读性
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)
如果需要正则表达式不区分大小写并且.可以匹配换行符,可以给re.compile()传入re.IGNORECASE和re.DOTALL作为第二个参数。
用|分隔
someRegexValue = re.compile('foo', re.IGNORECASE | re.DOTALL)
项目电话号码和 E-mail 地址提取程序
假设你有一个无聊的任务,要在一篇长的网页或文章中,找出所有电话号码和
邮件地址。如果手动翻页,可能需要查找很长时间。如果有一个程序,可以在剪贴
板的文本中查找电话号码和 E-mail 地址,那你就只要按一下 Ctrl-A 选择所有文本,
按下 Ctrl-C 将它复制到剪贴板,然后运行你的程序。它会用找到的电话号码和 E-mail
地址,替换掉剪贴板中的文本
当你开始接手一个新项目时,很容易想要直接开始写代码。但更多的时候,最
好是后退一步,考虑更大的图景。我建议先草拟高层次的计划,弄清楚程序需要做
什么。暂时不要思考真正的代码,稍后再来考虑。现在,先关注大框架。
不错 这点对于一个程序员来说 很重要
- 使用 pyperclip 模块复制和粘贴字符串。
- 创建两个正则表达式,一个匹配电话号码,另一个匹配 E-mail 地址。对两个正则表达式,找到所有的匹配,而不只是第一次匹配。
- 将匹配的字符串整理好格式,放在一个字符串中,用于粘贴。
- 如果文本中没有找到匹配,显示某种消息。
为电话号码创建一个正则表达式
phoneRegex = re.compile(r'\d{11}')
为 E-mail 地址创建一个正则表达式
emailRegex = re.compile(r'\w+@\w+.\w+')
找到所有匹配的字符串
text = '12345678901@qq.com'
mo = phoneRegex.findall(text)
print(mo)
mo = emailRegex.findall(text)
print(mo)
#! python3
# phoneAndEmail.py - Finds phone numbers and email addresses on the clipboard.
import pyperclip, re
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)
# Create email regex.
emailRegex = re.compile(r'''(
[a-zA-Z0-9._%+-]+ # username
@ # @ symbol
[a-zA-Z0-9.-]+ # domain name
(\.[a-zA-Z]{2,4}){1,2} # dot-something
)''', re.VERBOSE)
# Find matches in clipboard text.
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])
# Copy results to the clipboard.
if len(matches) > 0:
pyperclip.copy('\n'.join(matches))
print('Copied to clipboard:')
print('\n'.join(matches))
else:
print('No phone numbers or email addresses found.')
1.创建 Regex 对象的函数是什么?
re.compile() 这个函数接受一个正则表达式模式作为参数,并返回一个Regex对象
import re
# 创建一个 Regex 对象
pattern = re.compile(r'\d+') # 匹配一个或多个数字
# 使用 Regex 对象进行匹配
match = pattern.match('123abc')
if match:
print("匹配成功:", match.group())
else:
print("匹配失败")
2.在创建 Regex 对象时,为什么常用原始字符串?
转义字符:正则表达式中经常使用反斜杠 \ 来表示特殊字符,例如 \d 表示数字,\w 表示字母或数字。在普通的字符串中,反斜杠也是一个转义字符,例如 \n 表示换行,\t 表示制表符。这会导致在正则表达式中使用普通字符串时,需要写成 \d、\w 等来表示真正的正则表达式。而使用原始字符串,反斜杠 \ 不会被当作转义字符,这样就可以直接写 \d、\w。
可读性:使用原始字符串可以使正则表达式更加简洁和易读,不需要写那么多反斜杠。
避免错误:由于正则表达式中反斜杠的频繁使用,如果不使用原始字符串,很容易出现由于转义字符导致的错误
3.search()方法返回什么?
返回对象
import re
pattern = r'\d+' # 匹配一个或多个数字
text = 'abc123def456'
match = re.search(pattern, text)
if match:
print("匹配成功:", match.group())
else:
print("匹配失败")
4.如果 search()方法没有找到匹配,它将返回什么?
None
5.findall()方法返回什么?
返回一个字符串列表
import re
pattern = r'\d+' # 匹配一个或多个数字
text = 'abc123def456'
matches = re.findall(pattern, text)
if matches:
print("匹配成功:", matches)
else:
print("匹配失败")
6.如果 Regex 对象在创建时没有使用 re.DOTALL 标志,那么句点 . 在正则表达式中有什么含义?
在正则表达式中,句点 . 通常表示匹配除换行符 \n 之外的任何单个字符。这意味着 . 可以匹配字母、数字、空格、标点符号等任何字符,但不会匹配换行符。
例如,正则表达式 a.b 可以匹配字符串 a1b、a*b、a b 等,但不能匹配 a\nb。
如果你想让 . 匹配包括换行符在内的任何字符,可以使用 re.DOTALL 标志。这个标志会使 . 匹配任何字符,包括换行符。
4.通过 Match 对象,如何得到匹配该模式的实际字符串?
group() 方法
5.用 r'(\d\d\d)-(\d\d\d-\d\d\d\d)'创建的正则表达式中,分组 0 表示什么?分组 1 呢?分组 2 呢?
import re
Rre = re.compile(r'(\d\d\d)-(\d\d\d-\d\d\d\d)')
text = '123-456-7890ddddd'
mo = Rre.search(text)
print(mo.group(0))
print(mo.group(1))
print(mo.group(2))
-
分组 0:表示整个匹配的字符串。例如,对于输入字符串 '123-456-7890',分组 0 将匹配整个字符串 '123-456-7890'。
-
分组 1:表示第一个捕获组 (\d\d\d) 匹配的子字符串。这个捕获组匹配三个数字。例如,对于输入字符串 '123-456-7890',分组 1 将匹配 '123'。
-
分组 2:表示第二个捕获组 (\d\d\d-\d\d\d\d) 匹配的子字符串。这个捕获组匹配三个数字,一个连字 符 -,再跟着四个数字。例如,对于输入字符串 '123-456-7890',分组 2 将匹配
6.括号和句点在正则表达式语法中有特殊的含义。如何指定正则表达式匹配真正的括号和句点字符?
使用反斜杠 \ 对括号和句点进行转义,例如 . 和 \
import re
# 匹配真正的括号和句点
pattern = r'\(\.\)' # 匹配一个左括号,一个句点,一个右括号
text = '(.)'
match = re.search(pattern, text)
if match:
print("匹配成功:", match.group())
else:
print("匹配失败")
7.findall()方法返回一个字符串的列表,或字符串元组的列表。是什么决定它提供哪种返回?
findall()方法返回的列表类型取决于正则表达式中是否使用了括号。如果没有使用括号,findall()方法返回一个字符串列表,每个字符串都是匹配整个正则表达式的子字符串。如果使用了括号,findall()方法返回一个字符串元组的列表,每个元组包含正则表达式中每个括号捕获组的匹配结果。
8.在正则表达式中,|字符表示什么意思?
竖线 | 是一个逻辑“或”运算符。它用于匹配两个或多个模式中的任意一个。
9.在正则表达式中,?字符有哪两种含义?
非贪婪匹配和可选匹配。
10.在正则表达式中,+和*字符之间的区别是什么?
- 表示匹配前面的字符一次或多次,* 表示匹配前面的字符零次或多次。
11.在正则表达式中,{3}和{3,5}之间的区别是什么?
{3} 表示匹配前面的字符恰好 3 次,{3,5} 表示匹配前面的字符至少 3 次,最多 5 次。
12.在正则表达式中,\d、\w 和\s 缩写字符类是什么意思?
- \d:匹配任何数字字符,相当于 [0-9]。
- \w:匹配任何字母数字字符,相当于 [a-zA-Z0-9_]。
- \s:匹配任何空白字符,相当于 [ \t\n\r\f\v]。
13.在正则表达式中,\D、\W 和\S 缩写字符类是什么意思? - \D:匹配任何非数字字符,相当于 [^0-9]。
- \W:匹配任何非字母数字字符,相当于 [^a-zA-Z0-9_]。
- \S:匹配任何非空白字符,相当于 [^ \t\n\r\f\v]。
14.如何让正则表达式不区分大小写?
使用 re.IGNORECASE 标志。例如:
import re
pattern = re.compile(r'hello', re.IGNORECASE)
text = 'Hello, world!'
match = pattern.search(text)
if match:
print("匹配成功:", match.group())
else:
print("匹配失败")
15.字符.通常匹配什么?如果 re.DOTALL 作为第二个参数传递给 re.compile(),它会匹配什么?
字符.通常匹配除换行符之外的任何单个字符。如果 re.DOTALL 作为第二个参数传递给 re.compile(),那么.将匹配任何字符,包括换行符。
16..和?之间的区别是什么?
- 表示匹配前面的字符零次或多次,.* 表示匹配前面的字符零次或多次,尽可能多地匹配字符。*? 表示匹配前面的字符零次或多次,但尽可能少地匹配字符,即非贪婪匹配。
17.匹配所有数字和小写字母的字符分类语法是什么?
[0-9a-z]
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'
16..和?之间的区别是什么?
17.匹配所有数字和小写字母的字符分类语法是什么?
18.如果 numRegex = re.compile(r'\d+'),那么 numRegex.sub('X', '12 drummers, 11 pipers, five rings, 3 hens')返回什么?
numRegex = re.compile(r'\d+')
numRegex.sub('X', '12 drummers, 11 pipers, five rings, 3 hens')
'X drummers, X pipers, five rings, X hens'
# 这里需要注意sub()函数参数 用regex对象调用的时候,第一个参数是替换字符串,第二个参数是要进行替换的字符串
# 从模块调用的时候:第一个参数:正则表达式模式,用于匹配需要替换的字符串部分。第二个参数 repl:用于替换匹配到的字符串部分。可以是字符串或者一个函数。第三个参数 string:要进行替换操作的原始字符串。第四个参数 count:可选参数,表示替换的次数,默认为-1,表示替换所有匹配的部分。
numRegex = re.compile(r'\d+')
re.sub(numRegex,'X', '12 drummers, 11 pipers, five rings, 3 hens')
19.将 re.VERBOSE 作为第二个参数传递给 re.compile(),让你能做什么?
import re
# 使用 re.VERBOSE 标志,使正则表达式更易读
pattern = re.compile(r"""
\b # 单词边界
\w+ # 匹配一个或多个字母、数字或下划线
\s # 匹配一个空白字符
\w+ # 匹配一个或多个字母、数字或下划线
\b # 单词边界
""", re.VERBOSE)
text = "hello world this is a testrrrr"
# 使用编译后的正则表达式进行匹配
matches = pattern.findall(text)
print(matches)
# 输出: ['hello world', 'this is', 'a testrrrr']
20.如何写一个正则表达式,匹配每 3 位就有一个逗号的数字?它必须匹配以下数字:
'42''1,234''6,368,745'
但不会匹配:
'12,34,567' (逗号之间只有两位数字)'1234' (缺少逗号)
import re
# 正则表达式
pattern = r'^\d{1,3}(?:,\d{3})*$' # ^\d{1,3} 匹配1到3位的数字 (?:,\d{3})*$ 匹配0个或多个逗号和3位的数字
# ?:,\d{3} 是一个非捕获组,用于匹配逗号和3位的数字,但不捕获匹配的内容
# 测试字符串
test_strings = [
"42",
"1,234",
"6,368,745",
"12,34,567",
"1234"
]
# 查找并打印每个字符串中匹配正则表达式的部分
for s in test_strings:
for match in re.finditer(pattern, s):# 这里用finditer()函数,它会返回一个迭代器,每次迭代返回一个Match对象,而不是字符串本身。
print(f"在字符串中找到匹配的部分:'{match.group()}'")
else:
print(f"在字符串 '{s}' 中没有找到匹配的部分。")
21.如何写一个正则表达式,匹配姓 Nakamoto 的完整姓名?你可以假定名字总是出现在姓前面,是一个大写字母开头的单词。该正则表达式必须匹配:
'Satoshi Nakamoto'
'Alice Nakamoto'
'RoboCop Nakamoto'
但不匹配:
'satoshi Nakamoto'(名字没有大写首字母)
'Mr. Nakamoto'(前面的单词包含非字母字符)
'Nakamoto' (没有名字)
'Satoshi nakamoto'(姓没有首字母大写)
r'[A-Z]{1}\w+ Nakamoto'
pattern = rr'[A-Z]{1}\w+\s*Nakamoto'# \s* 表示匹配零个或多个空格
text = ['Satoshi Nakamoto', 'Alice Nakamoto', 'RoboCop Nakamoto', 'Mr. Nakamoto']
for name in text:
match = re.findall(pattern, name)
if match:
print(match)
else:
print('No match')
# 输出:
# ['Satoshi']
# ['Alice']
# ['RoboCop']
# No match
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.'
r'Alice|Bob|Carol\s(eats|pets|throws)\s(apples|cats|baseballs).'
import re
# 正则表达式
pattern = r'^(Alice|Bob|Carol)\s+(eats|pets|throws)\s+(apples|cats|baseballs)\.$'
# 测试字符串
test_strings = [
"Alice eats apples.",
"Bob pets cats.",
"Carol throws baseballs.",
"Alice pets dogs.",
"Bob eats apples, cats, and baseballs."
]
# 查找并打印每个字符串中匹配正则表达式的部分
for s in test_strings:
if re.match(pattern, s):
print(f"匹配的句子:'{s}'")
else:
print(f"不匹配的句子:'{s}'")
在正则表达式中,括号 ()
具有分组和捕获的双重功能。具体是分组还是捕获,取决于你是否关心括号内匹配的文本。以下是一些使用括号的场景:
分组(Grouping)
当你想要将表达式的一部分组合在一起,以便应用量词或者创建更复杂的模式时,你使用括号进行分组。在这种情况下,括号不捕获匹配的文本。
例子:
a(b|c)d
匹配 "abd" 或 "acd"。括号内的b|c
被分组在一起,表示 "b" 或 "c" 中的一个。(ab)+
匹配一个或多个 "ab"。括号将 "ab" 分组在一起,使得+
可以应用于整个 "ab"。
捕获(Capturing)
当你想要获取括号内匹配的文本时,括号执行捕获功能。捕获的文本被存储在匹配对象的组中,可以通过组号或组名来引用。
例子:
re.search(r'(ab)+', 'ababab').group(1)
匹配 "ababab" 中的 "ab"。group(1)
返回第一个捕获组匹配的内容 "ab"。re.search(r'(?P<word>\w+)', 'Hello World').group('word')
匹配 "Hello World" 中的 "Hello"。(?P<word>\w+)
创建了一个命名的捕获组 "word",group('word')
返回该捕获组匹配的内容 "Hello"。
非捕获组(Non-capturing groups)
如果你只需要分组,而不需要捕获匹配的文本,可以使用非捕获组。非捕获组由 ?:
标志表示,放在括号内部。
例子:
(?:ab)+
匹配一个或多个 "ab",但不会捕获 "ab"。re.search(r'(?:ab)+', 'ababab').groups()
返回一个空元组,因为没有任何捕获组。
总结
- 分组:使用括号
()
来创建一个分组,以便应用量词或构建更复杂的模式。 - 捕获:使用括号
()
来捕获匹配的文本,可以通过组号或组名来引用。 - 非捕获组:使用
?:
标志在括号内部(?...)
来创建一个非捕获组,这样分组内的文本不会被捕获。
括号何时分组,何时捕获,完全取决于你的需求。如果你不需要括号内匹配的文本,可以使用非捕获组来提高正则表达式的效率,因为非捕获组不会存储匹配的文本。
实践项目
写一个函数,它使用正则表达式,确保传入的口令字符串是强口令。强口令的定义是:长度不少于 8 个字符,同时包含大写和小写字符,至少有一位数字。你可能需要用多个正则表达式来测试该字符串,以保证它的强度。
import re # 导入正则表达式模块
def strong_password(password):
# 检查密码长度是否小于8,如果是则返回False,表示密码强度不足
if len(password) < 8:
return False
# 使用正则表达式查找密码中是否包含大写字母,如果没有则返回False
if not re.findall(r'[A-Z]',password):
return False
# 使用正则表达式查找密码中是否包含小写字母,如果没有则返回False
if not re.findall(r'[a-z]',password):
return False
# 使用正则表达式查找密码中是否包含数字,如果没有则返回False
if not re.findall(r'\d',password):
return False
# 如果以上条件都满足,则返回True,表示密码强度足够
else:
return True
print(strong_password('Aa123456S'))
习题
23.编写一个函数,它使用正则表达式,从字符串中移除所有 HTML 标签,并返回清理后的字符串。
import re
def remove_html_tags(text):
# 使用正则表达式匹配并替换所有的 HTML 标签
cleaned_text = re.sub(r'<[^>]+>', '', text)
return cleaned_text
# 测试函数
html_text = '<p>This is a <a href="http://example.com">link</a>.</p>'
cleaned_text = remove_html_tags(html_text)
strip()的正则表达式版本
写一个函数,它接受一个字符串,做的事情和 strip()字符串方法一样。如果只
传入了要去除的字符串,没有其他参数,那么就从该字符串首尾去除空白字符。否
则,函数第二个参数指定的字符将从该字符串中去除。
import re
def strip(text, chars=None):
# 如果没有传入 chars 参数,则默认去除
if chars is None:
#使用正则表达式匹配并替换收尾字符
cleaned_text = re.sub(r'^\s+|\s+$', '', text)
else:
# 使用正则表达式匹配并替换指定的字符
cleaned_text = re.sub(r'^[' + chars + ']+|[' + chars + ']+$', '', text)
return cleaned_text
# 测试函数
text1 = ' Hello, World! '
text2 = '***Hello, World!***'
text3 = '---Hello, World!---'
text4 = '+++Hello, World!+++'
print(strip(text1)) # 输出: 'Hello, World!'
print(strip(text2,'*')) # 输出: 'Hello, World!'
还是得多练习啊 有些知识不熟练根本就不会用
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战