> 文章列表 > 从零学习python - 14正则表达式的始末

从零学习python - 14正则表达式的始末

从零学习python - 14正则表达式的始末

熟悉正则表达式

# 正则表达式是对字符串操作的一种逻辑公式,使用事先定义好的一些'特定字符'及特定字符的'组合',组成一个'规则字符串'
# 这个'规则字符串'表示对字符串(普通字符和特殊字符)操作的一种逻辑公式.
# 作用: 1-给定字符串是否匹配正则表达式的过滤逻辑   2-通过正则表达式从字符串中获取我们想要的特定部分
# 特点: 1-灵活,逻辑性和功能性强 2-用简单的方式达到字符串的复杂控制
# 使用场景: 判断手机号是否符合 判断填写的邮箱是否符合格式# re模块: 提供正则相关的操作
# 使用试例
msg1 = '杰伦德华赵本山'
# re.compile(pattern) pattern正则表达式 编译一个正则表达式模式,返回一个pattern(正则表达式模式)对象
pattern1 = re.compile('赵本山')
# pattern.match(AnyStr) -> Match[AnyStr] | None (match:使匹配)
print(pattern1.match(msg1))  # None    match()方法会从前往后进行匹配,如果不同则直接返回None
# 交换一下msg中要匹配的str的位置,就会改变输出结果
msg2 = '赵本山杰伦德华'
pattern2 = re.compile('赵本山')
print(pattern2.match(msg2))  # <re.Match object; span=(0, 3), match='赵本山'> 匹配成功返回match对象
# 使用re模块内封装的match()方法
# re.match(pattern, String):尝试在字符串开头匹配正则表达式,匹配到则返回Match对象,匹配不到则返回None
print(re.match('赵本山', msg1))  # None
print(re.match('赵本山', msg2))  # <re.Match object; span=(0, 3), match='赵本山'># re.search(pattern, String):扫描字符串,如果找到与正则表达式匹配则返回Match对象,如果没有找到则返回None。
print(re.search('赵本山', msg1))  # <re.Match object; span=(4, 7), match='赵本山'>
print(re.search('赵本山', msg2))  # <re.Match object; span=(0, 3), match='赵本山'># 我们也可以接收这个Match对象,通过这个对象来操作里面的内容
search_match = re.search('赵本山', msg1)
print(search_match.span())  # 返回与其相匹配的字符串位置  (4, 7)
print(search_match.group())  # 返回与其相匹配的字符串内容  赵本山

正则表达式的基本操作

# 在熟悉了正则表达式的使用场景与用法,我们接下来就需要更灵活的去学习它.
'''
# '.': 用于匹配除换行符外(\\n)的所有字符(写在正则表达式中表示取反)
# '^': 用于匹配字符串的开始,即首行 如果没有^则只从符合匹配条件的字符开始匹配
# '$': 用于匹配到字符串的结尾,(末尾如果有换行符\\n,就匹配\\n前面的那个字符),即行尾 如果没有$则只匹配到符合需求的就不会匹配后面的字符
# 定义正则表达式验证次数的符号:
# '*': 用于将前面的正则模式匹配0次或多次(贪婪模式:尽可能多的匹配) >=0
# '+': 用于将前面的正则模式匹配一次或多次(贪婪模式:尽可能多的匹配) >=1
# '?': 用于将前面的正则模式匹配0次或1次(贪婪模式:尽可能多的匹配) [0, 1]次
# '*?' '+?' '??': 表示上面三种字符的非贪婪模式:尽可能少的匹配
# '{m}': 用于验证将前面的正则模式匹配m次
# '{m,}': 用于验证将前面的正则模式匹配m次或多次 >=m
# '{m,n}': 用于将前面正则模式匹配m次到n次(贪婪模式),最小匹配m次,最大匹配n次
# '{m,n}?': 用于将前面正则模式匹配m次到n次(非贪婪模式),最小匹配m次,最大匹配n次
'''
# 正则表达式中的符号:
# '[]': 用于标识一组字符,如果^是第一个字符,则表示的是一个补集.比如[0-9]表示所有数字,[^0-9]表示除数字外的字符
# 提取到符合 - '字母,数字,字母' - 这种规则的字符串出来
msg3 = 'a2fh23gw3eq8asf1h'
search_match = re.search('[a-z][0-9][a-z]', msg3)
print(search_match.group())  # a2f  re.search()方法特点:只要找到第一个匹配正则的字符串,就停止匹配.(只能找到第一个符合正则的字符串)
# 如果需要找到所有符合条件的方法则使用re.findall(pattern, string)方法
# 将字符串中所有匹配项以列表形式返回
re_findall1 = re.findall('[a-z][0-9][a-z]', msg3)
print(re_findall1)  # ['a2f', 'w3e', 'q8a', 'f1h']
# 如果不管字母中间有几个数字的这种字符串都找到需要怎么办呢?
re_findall2 = re.findall('[a-z][0-9]+[a-z]', msg3)
print(re_findall2)  # 使用符号便可以完成我们的需求了 ['a2f', 'h23g', 'w3e', 'q8a', 'f1h']
# 通过几个例子来了解上面符号的用法:
# QQ号码验证:5-11位,首位不能为0   '^[1-9][0-9]{4,10}$'    经测试符合条件
QQ_num1 = '12345'
QQ_num2 = '1234'
QQ_num3 = '12345678901'
QQ_num4 = '123456789012'
result1 = re.match('^[1-9][0-9]{4,10}$', QQ_num1)
result2 = re.match('^[1-9][0-9]{4,10}$', QQ_num2)
result3 = re.match('^[1-9][0-9]{4,10}$', QQ_num3)
result4 = re.match('^[1-9][0-9]{4,10}$', QQ_num4)
print(result1)  # <re.Match object; span=(0, 5), match='12345'>
print(result2)  # None
print(result3)  # <re.Match object; span=(0, 10), match='12345678901'>
print(result4)  # None
# 用户名可以是字母或数字,不能以数字开头,用户名必须6位以上 [0-9a-zA-Z]
username1 = '00admin'  # 数字开头
username2 = 'admin&*'  # 加特殊符号
username3 = 'admin'  # 正常用户名
print(re.match('[a-zA-Z][0-9a-zA-Z]', username1))  # None
print(re.match('[a-zA-Z][0-9a-zA-Z]', username2))  # <re.Match object; span=(0, 2), match='ad'>
print(re.match('[a-zA-Z][0-9a-zA-Z]', username3))  # <re.Match object; span=(0, 2), match='ad'>
# 如果我们不使用$符号的话,可以看到不符合规则的username2也匹配成功了,这时候我们在末尾加上$测试
print(re.match('[a-zA-Z][0-9a-zA-Z]$', username2))  # None 当加上$表示要一直匹配到字符串结尾,这样加上特殊符号的username2就不符合条件了
# 由于match()函数是从头开始匹配字符串的,所以我们不需要使用^符号,那么我们使用search函数测试一些username1的效果是什么呢?
print(re.search('[a-zA-Z][0-9a-zA-Z]', username1))  # <re.Match object; span=(2, 4), match='ad'> 数字开头也显示匹配成功
# 原因是没有^符号,search函数只要在字符串中匹配到符合正则的字符就会返回,所以我们加上^符号,要求从头匹配.
print(re.search('^[a-zA-Z][0-9a-zA-Z]', username1))  # None
# 通过上面两个例子我们应该理解了符号'^'和'$'的使用方法了: '^...$'表示从头到尾,拿整个字符串进行匹配!
# 那有没有什么能代替上面比较麻烦的书写呢?还真有:
'''
# \\A: 表示从字符串的开始处匹配
# \\Z: 表示从字符串的结束处匹配,如果存在换行,只匹配到换行前的结束字符串
# \\b: 匹配一个单词边界,指单词和空格间的位置,例:'py\\b'可以匹配'python'中的py,但不能匹配'openpyxxm'中的py
# \\B: 匹配一个非单词边界,'py\\B'可以匹配'openpyxxm'中的py,但不能匹配'python'中的py
# \\d: 匹配任意数字,等价于[0-9]
# \\D: 匹配任意非数字,等价于[^\\d]
# \\s: 匹配任意空白字符,等价于[\\t\\n\\r\\f]
# \\S: 匹配任意非空白字符,等价于[^\\s]
# \\w: 匹配任意字母数字及下划线,等价于[0-9a-zA-Z_]
# \\W: 匹配任意非字母数字及下划线,等价于[^\\w]
# \\\\: 匹配反斜杠'\\'
'''
# 那使用上面学到的重新写一个正则表达式看看效果
# 用户名符合字母数字下划线,不能以数字开头且用户名长度大于6:
# 如果要使用整个字符串进行比较:由于match是从头开始的,所以只需要在末尾加$,search函数的话需要开头加^末尾加$
username4 = '00admin'
username5 = 'admin00'
print(re.match('[a-zA-Z]\\w{5,}$', username4))  # None
print(re.match('[a-zA-Z]\\w{5,}$', username5))  # <re.Match object; span=(0, 7), match='admin00'>
# 从一列字符串中找到.py文件:
str1 = 'aa.py bb.py cc.py apy.txt'
print(re.findall(r'py\\b', str1))    # ['py', 'py', 'py', 'py'] 需要加上文件名
print(re.findall(r'\\w\\.py\\b', str1))   # ['a.py', 'b.py', 'c.py'] 文件全名没匹配到
print(re.findall(r'\\w*\\.py\\b', str1))   # ['aa.py', 'bb.py', 'cc.py'] 匹配成功
# 手机号码验证:
phone_num = '15233779988'
print(re.match(r'1[35789]\\d{9}$', phone_num))   # <re.Match object; span=(0, 11), match='15233779988'>

正则表达式的分组

# 分组
'''
'|': 表示或者功能
'[adc]': 表示元素范围内单个的或者(a or b or c)
'(word1 | word2)': 表示整体范围内的或者(word1 or word2) 可以通过group(第几组)方法得到内容
'''
# 验证输入的邮箱格式 @163.com @qq.com @126.com
email_num1 = 'otto666@qq.com'
email_num2 = '666otto@163.com'
email_num3 = '66otto6@126.com'print(re.match(r'\\w{6,20}@(163|QQ|qq|126)\\.(com|cn)$',email_num1))  # <re.Match object; span=(0, 14), match='otto666@qq.com'>
print(re.match(r'\\w{6,20}@(163|QQ|qq|126)\\.(com|cn)$',email_num2))  # <re.Match object; span=(0, 15), match='666otto@163.com'>
print(re.match(r'\\w{6,20}@(163|QQ|qq|126)\\.(com|cn)$',email_num3))  # <re.Match object; span=(0, 15), match='66otto6@126.com'>
# 不以7结尾的手机号码
phone1 = '15233448877'
phone2 = '15233448888'
print(re.match(r'1\\d{9}[1-689]$', phone1))  # None
print(re.match(r'1\\d{9}[1-689]$', phone2))  # <re.Match object; span=(0, 11), match='15233448888'>
# match.group(): group方法可以获得不同组的信息# 通过数字分组引用:
# 将电话的区号和号码分别提取出来
phone3 = '010-12644448888'
re_match = re.match(r'(\\d{3}|\\d{4})-(\\d{8,12})$', phone3)
print(re_match)  # <re.Match object; span=(0, 15), match='010-12644448888'>
print(re_match.group())  # 010-12644448888
# ()表示分组,group(1)表示提取出第一组的内容
print(re_match.group(1))  # 010
print(re_match.group(2))  # 12644448888
# 从标签页中提取出信息 '.'表示所有字符 </1>表示匹配第一组的内容 如第一组内容位<html>则<1> = </html>
msg1 = '<h1>hello world</h1>'
re_match1 = re.match(r'<([0-9a-zA-Z]+)>(.+)</\\1>$', msg1)
print(re_match1)  # <re.Match object; span=(0, 20), match='<h1>hello world</h1>'>
print(re_match1.group())  # <h1>hello world</h1>
print(re_match1.group(1))  # h1
print(re_match1.group(2))  # hello worldmsg2 = '<h1>hello world</h2>'
re_match2 = re.match(r'<([0-9a-zA-Z]+)>(.+)</\\1>$', msg2)
print(re_match2)  # None 因为</\\1>的判定前后标签不一致,匹配不成功返回Nonemsg3 = '<h1><h2>hello world</h2></h1>'
re_match3 = re.match(r'<([0-9a-zA-Z]+)>(.+)</\\1>$', msg3)
print(re_match3)
print(re_match3.group(2))  # <h2>hello world</h2>
# 如果是多个嵌套的标签体,我们怎么得到中间的信息呢?
# 方法是我们也多嵌套一层正则表达式
re_match3 = re.match(r'<([0-9a-zA-Z]+)><([0-9a-zA-Z]+)>(.+)</\\2></\\1>$', msg3)
print(re_match3.group(1))  # h1
print(re_match3.group(2))  # h2
print(re_match3.group(3))  # hello world 成功拿到!
# 但当上面嵌套过于复杂时,很容易出现看串的情况,这里需要怎么优化一下呢?
# 起名的方式解决: (?P<name>正则) (?P=name)
msg4 = '<h1><h2>hello world</h2></h1>'
re_match4 = re.match(r'<(?P<name1>[0-9a-zA-Z]+)><(?P<name2>[0-9a-zA-Z]+)>(.+)</(?P=name2)></(?P=name1)>', msg4)
print(re_match4)  # <re.Match object; span=(0, 29), match='<h1><h2>hello world</h2></h1>'>
print(re_match4.group(3))  # hello world
# 其余re方法
# re.sub(正则表达式, 新内容, string):将正则表达式匹配到的内容替换为新内容
re_sub1 = re.sub(r'\\d+', '99', '数学:59 英语:59')
print(re_sub1)  # 替换结果: 数学:99 英语:99# 新内容部分也可以放函数进行运算
def numplus(temp):# temp.group()相当于匹配正则取出的结果num = temp.group()num1 = int(num)num1 += 1return str(num1)re_sub2 = re.sub(r'\\d+', numplus, '数学:59 英语:59')
print(re_sub2)  # 数学:60 英语:60 执行函数后的运行结果!# re.split(正则, string)
re_split = re.split(r'[,:]', '数学:59 英语:59')
print(re_split)  # ['数学', '59 英语', '59']
# 贪婪模式: 总是尽可能多的去匹配
msg5 = 'abc123abc'
print(re.match(r'abc(\\d+)', msg5))  # <re.Match object; span=(0, 6), match='abc123'>
print(re.match(r'abc(\\d*)', msg5))  # <re.Match object; span=(0, 6), match='abc123'>
print(re.match(r'abc(\\d?)', msg5))  # <re.Match object; span=(0, 4), match='abc1'>
# re中方法总结
match(pattern, string)   从开头开始匹配,匹配一次
search(pattern, string)   整个字符串匹配一次就完事
findall(pattern, string)    整个字符串全部匹配
sub(pattern, new, str)     替换
split(pattern, str)   切割