2021.03.24
正则表达式
from re import fullmatch
正则是一种用来处理文本数据的一种工具。(一般用于处理复杂的文本问题)
1)检测输入的数据是否是手机号?
2)检查输入的ip地址是否合法?
3)获取字符串中所有的数字数据:‘sjhfj23世纪东方和79sk试试928sjd’
正则的语法 - 用不同的符号来描述字符串规则
1) 匹配类符号
2) 检测类符号
3) 控制次数的符号
4) 分组和分支
5) 其他常用参数
fullmatch(正则表达式, 字符串) - 检测字符串是否符合正则表达式描述的规则,如果不符合返回None
‘正则表达式’ - Python “正则表达式” - OC/python /正则表达式/ - js
匹配类符号 - 一个符号对应字符串的一个字符
1)普通字符 - 特殊符号以外的字符就是普通字符
普通字符在正则中表示这个符号本身
re_str = 'abc' # 需要一个长度是3的字符串,并且第一个字符是a, 第二个字符是b, 第三个字符是c
print(fullmatch(re_str, 'abc'))
. - 匹配一个任意字符
re_str = 'a.c' # 需要一个长度是3的字符串, 并且第一个字符是a, 最后一个字符是c,中间是任意字符
print(fullmatch(re_str, 'a胡c'))
re_str = '..ab' # 一个长度是4的字符串,ab前面是任意两个字符
print(fullmatch(re_str, 'o9ab'))
\d - 匹配任意一个数字字符
re_str = r'ab\d' # 一个长度是3的字符串,ab后面是一个任意数字
print(fullmatch(re_str, 'ab3'))
\w - 匹配任意一个数字、字母或者下划线(基于ASCII码)
re_str = r'\w123'
print(fullmatch(re_str, '好123'))
\s - 匹配任意一个空白字符
空白字符 - 空格、\t、\n
re_str = r'123\sabc'
print(fullmatch(re_str, '123 abc'))
\D、\S、\W
# \D - 匹配任意一个非数字字符
# \S - 匹配任意一个非空白字符
print(fullmatch(r'abc\S123\D', 'abcM123='))
[字符集] - 匹配字符集中的任意一个字符
[ab12] - 匹配a、b、1或者2中任意一个字符
[ab\d]、[\dab]、[a\db]、[ab0123456789] - 匹配a、b、或者0到9中任意一个字符
[0-9] - 匹配0到9中任意一个字符
[56789]、[5-9] - 匹配5到9中任意一个字符
[a-z] - 匹配任意一个小写字母
[A-Z] - 匹配任意一个大写字母
[a-zA-Z] - 匹配任意一个字母
[a-zA-Z\d_] - 匹配任意一个字母、数字、下划线
[\u4e00-\u9fa5] - 匹配任意一个中文
注意:如果[]中-在两个字符之间表示范围,如果-不在两个字符之间,它不具备特殊功能表示这个符号本身, -在[]外面也是普通符号
re_str = r'a[xyz]b'
print(fullmatch(re_str, 'ayb'))
print(fullmatch(r'abc[2-5]', 'abc4'))
print(fullmatch(r'[\u4e00-\u9fa5]abc', '好abc'))
# print(fullmatch(r'[z-a]abc', 'wabc')) # 报错
print(fullmatch(r'[ac-]123', '-123'))
[^字符集] - 匹配除了字符集以外的任意一个字符
print(fullmatch(r'[^axy]123', 'k123'))
print(fullmatch(r'[^a-zA-Z]123', '-123'))
注意:[]里面,只有放在最前面才有特殊意义,在其他位置表示本身
print(fullmatch(r'[a-z^]123', '^123'))
检测类符号
在正则中不会影响字符串长度,它们的作用是在匹配成功以后检测检测类符号所在的位置是否符号要求
- \b - 检测是否是单词边界
单词边界 - 凡是能够将两个单词区分开的所有的符号,主要有:空白、标点符号、字符串开头和字符结尾
re_str = r'abc \b123'
print(fullmatch(re_str, 'abc 123'))
re_str = r'abc\b.123'
print(fullmatch(re_str, 'abc,123'))
str1 = '数据集,890,shs323节省时间34jsj 922 失敬失8敬283'
result = findall(r'\b\d+\b', str1)
print(result)
\B - 检测是否不是单词边界
result = findall(r'\B\d+\B', str1)
print(result)
^ - 检测是否是字符串开头([]外面)
$ - 检测是否是字符串结尾([]外面)
re_str = r'\dabc$'
print(findall(re_str, '7abc啥时间2abc数据9abc是6abc'))
匹配次数
*- 匹配0次或多次
a* - 匹配0个或者多个a
.* - 匹配0个或者多个任意字符
\d* - 相当于任意多个\d
[xyz]* - 相当于任意多个[xyz]
re_str = r'a*123'
print(fullmatch(re_str, 'aaaaaa123'))
re_str = r'.*123'
print(fullmatch(re_str, 'asc123'))
re_str = r'\d*abc'
print(fullmatch(re_str, '78908887abc'))
re_str = r'[xyz]*123'
print(fullmatch(re_str, 'xxyz123'))
± 1次或者多次
? - 0次或者1次
{N} - 匹配N次
{M,N} - 匹配M~N次
{,N} - 匹配0~N次(最多N次)
{M,} - 匹配至少M次
print(fullmatch(r'\d{3}abc{5}', '722abccccc'))
print(fullmatch(r'\d{3,5}abc', '72126abc'))
print(fullmatch(r'\d{,2}abc', '23abc'))
print(fullmatch(r'\d{2,}abc', '2231abc'))
练习:写程序判断输入手机号是否合法:1)长度11位 2)都是数字 3)第一位必须是1,第二位是3~9
def is_tel(tel: str):
result = fullmatch(r'1[3-9]\d{9}', tel)
if result:
print(f'{tel}是合法的手机号')
else:
print(f'{tel}不是合法的手机号')
is_tel('17865754346')
贪婪和非贪婪
贪婪和非贪婪模式是在匹配次数不确定的时候才会出现:*、+、?、{M,N}、{,N}、{M,}
贪婪和非贪婪:在能够匹配成功的情况下,如果对应的匹配次数有多种,贪婪模式选最多的那个次数,非贪婪选最少的那个次数。
贪婪:、+、?、{M,N}、{,N}、{M,}
非贪婪:?、+?、??、{M,N}?、{,N}?、{M,}?
print(search(r'\d{3}', 'shsj234色即是空889====902')) # <re.Match object; span=(4, 7), match='234'>
re_str = r'a.+b'
print(search(re_str, '试试amnbxyzb是计算机数据')) # <re.Match object; span=(2, 10), match='amnbxyzb'>
re_str = r'a.+?b'
print(search(re_str, '试试amnbxyzb是计算机数据')) # <re.Match object; span=(2, 6), match='amnb'>
re_str = r'a.+b'
print(search(re_str, '试试amnbxyz是计算机数据')) # <re.Match object; span=(2, 6), match='amnb'>
response = requests.get('http://api.tianapi.com/txapi/ncovabroad/index?key=c9d408fefd8ed4081a9079d0d6165d43')
result = findall(r'"provinceName":"([\u4e00-\u9fa5]+?)"', response.text)
print(result)
分组和分支
分组
应用一:将正则表达式中的一部分用()括起来表示一个整体,然后进行整体相关操作
应用二:通过\M来重复它前面第M个分组匹配到的内容
应用三:捕获(获取结果的时候只取某个分组对应的内容)
# hj78hu23kk90
re_str = r'([a-zA-Z]{2}\d\d){3}'
print(fullmatch(re_str, 'hj78hu23kk90'))
# mn123mn、xy123xy
re_str = r'([a-z]{2})123\1'
print(fullmatch(re_str, 'mn123mn'))
re_str = r'(\d{2})([A-Z]{3})\1\2'
print(fullmatch(re_str, '12MNA12MNA'))
re_str = r'(\d{2})([A-Z]{3})\1{2}\2'
print(fullmatch(re_str, '12KAM1212KAM'))
re_str = r'[a-z](\d{3})'
result = findall(re_str, 'hsjs234失敬失敬你a8393sjjk9901啥时间---234看看嘛238')
print(result)
分支 - |
正则1|正则2
|相当于或者
# abc123、abcMN
re_str = r'abc\d{3}|abc[A-Z]{2}'
print(fullmatch(re_str, 'abcMN'))
re_str = r'abc\d{3}|[A-Z]{2}'
print(fullmatch(re_str, 'MK'))
re_str = r'abc(\d{3}|[A-Z]{2})'
print(fullmatch(re_str, 'abc234'))
re模块
from re import *
compile(正则表达式) - 创建正则表达式对象
re_obj = compile(r'\d{3}')
print(fullmatch(r'\d{3}', '234'))
print(re_obj.fullmatch('384'))
匹配相关方法
1)fullmatch(正则表达式, 字符串) - 完全匹配,判断整个字符串是否符合正则表达式描述的规则。如果不符合返回None,如果符合返回匹配对象
2)match(正则表达式, 字符串) - 匹配开头,判断字符串开头是否符合正则表达式描述的规则。如果不符合返回None,如果符合返回匹配对象
re_str = r'[a-z]{3}'
print(fullmatch(re_str, 'hsm'))
print(match(re_str, 'mks结束的发就好为34'))
3)匹配对象
re_str = r'((\d{2})([A-Z]{3}))'
result = match(re_str, '23KLA失敬失敬')
print(result)
匹配对象.group() / 匹配对象.group(0) - 获取整个正则匹配到的内容
匹配对象.group(N) - 获取正则表达式第N个分组匹配到的内容
print(result.group()) # '23KLA'
print(result.group(1)) # '23KLA'
print(result.group(2)) # '23'
print(result.group(3)) # 'KLA'
匹配对象.span() / 匹配对象.span(0) - 获取整个正则匹配到的内容在原字符串中的位置信息(开始下标和结束下标)
匹配对象.span(N) - 获取正则表达式第N个分组匹配到的内容在原字符串中的位置信息
print(result.span())
print(result.span(3))
匹配对象.string - 获取原字符串
print(result.string) # '23KLA失敬失敬'
查找相关方法
- search(正则表达式, 字符串) - 在字符串中搜索第一个满足正则表达式的字符串。如果找不到返回None,找到了返回匹配对象
print(search(r'\d{3}', '试试234节省时间988开始时间210')) # <re.Match object; span=(2, 5), match='234'>
- findall(正则表达式, 字符串) - 获取字符串中所有满足正则表达式的子串。返回值是列表,如果找不到返回空列表
print(findall(r'\d{3}', '试试234节省时间988开始时间210')) # ['234', '988', '210']
print(findall(r'\d{3}[\u4e00-\u9fa5]', '试试234节省时间988开始时间210mn')) # ['234节', '988开']
print(findall(r'(\d{3})[\u4e00-\u9fa5]', '试试234节省时间988开始时间210mn')) # ['234', '988']
print(findall(r'\d{3}-[a-z]{2}', '啥时间233-ks试试,892-mk啥,sjs2912-op')) # ['233-ks', '892-mk', '912-op']
print(findall(r'(\d{3})-([a-z]{2})', '啥时间233-ks试试,892-mk啥,sjs2912-op')) # [('233', 'ks'), ('892', 'mk'), ('912', 'op')]
print(findall(r'(\d[a-z]){3}', '是计算机上8m9l0b,s数据是8a0p1k')) # ['0b', '1k']
- finditer(正则表达式, 字符串) - 获取字符串中所有满足正则表达式的子串。返回一个迭代器,迭代器中的元素是匹配对象
result = finditer(r'(\d{3})-([a-z]{2})', '啥时间233-ks试试,892-mk啥,sjs2912-op')
# print([x.group() for x in result]) # ['233-ks', '892-mk', '912-op']
# print([x.group(1) for x in result]) # ['233', '892', '912']
print([x.group(2) for x in result]) # ['ks', 'mk', 'op']
- sub(正则表达式, 字符串1, 字符串2) - 将字符串2中所有满足正则的子串全部替换成字符串
str1 = 'how are you? I am fine, Thank you!'
result = sub(r'you', 'me', str1)
print(result) # how are me? I am fine, Thank me!
str1 = 'how are you? I am fine, Thank you!'
result = sub(r'you|I', 'me', str1)
print(result) # how are me? me am fine, Thank me!
result = sub(r'傻[\s/,.\\]*[b瓜]|f\s*u\s*c\s*k|操', '*', r'我操, 你是傻 /b吗?F u c k')
print(result)
- split(正则表达式, 字符串) - 将字符串中所有满足正则表达式的子串作为切割点对字符串进行切割
result = split(r'[-+]', '试试-j234ss+skskj-健康是390福+世纪东2方看019计算机')
print(result)
result = split(r'\d+', '试试-j234ss+skskj-健康是390福+世纪东2方看019计算机')
print(result)
转义符号
from re import fullmatch, I, S
转义符号 - 将在正则中有特殊功能或者意义的符号变成一个普通字符
\具有特殊意义的符号: .、+、*、…
re_str = r'\d{2}\.\d{2}'
print(fullmatch(re_str, '23.34'))
re_str = r'\\dabc\+'
print(fullmatch(re_str, '\dabc+'))
re_str = r'\(\d{2}\)'
print(fullmatch(re_str, '(23)'))
将独立存在具备特殊功能的符号放在[]里面,这个符号的功能会自动消失
特别注意符号在[]里面也有特殊功能的情况,比如:^、-、[、]
re_str = r'\d{2}[.+?*(\-)|$^\]]\d{2}'
print(fullmatch(re_str, '23]34'))
常用参数
1)忽略大小写
默认不忽略大小写
print(fullmatch(re_str, '123k')) # <re.Match object; span=(0, 4), match='123k'>
print(fullmatch(re_str, '123M')) # None
用法一: r’(?i)正则表达式’
re_str = r'(?i)123[a-z]'
print(fullmatch(re_str, '123k')) # <re.Match object; span=(0, 4), match='123k'>
print(fullmatch(re_str, '123M')) # <re.Match object; span=(0, 4), match='123M'>
re_str = r'123[a-z]'
print(fullmatch(re_str, '123M', flags=I)) # <re.Match object; span=(0, 4), match='123M'>
单行匹配
多行匹配(默认) - 表示任意字符的.不能和\n(换行符)进行匹配
单行匹配 - 表示任意字符的.可以和\n(换行符)进行匹配
re_str = r'abc.123'
print(fullmatch(re_str, 'abc\n123')) # None
用法一
re_str = r'(?s)abc.123'
print(fullmatch(re_str, 'abc\n123')) # <re.Match object; span=(0, 7), match='abc\n123'>
re_str = r'abc.123'
print(fullmatch(re_str, 'abc\n123', flags=S)) # <re.Match object; span=(0, 7), match='abc\n123'>
re_str = r'(?is)abc.123'
print(fullmatch(re_str, 'ABC\n123')) # <re.Match object; span=(0, 7), match='ABC\n123'>
re_str = r'abc.123'
print(fullmatch(re_str, 'ABC\n123', flags=I | S)) # <re.Match object; span=(0, 7), match='ABC\n123'>
作业
一、不定项选择题
- 能够完全匹配字符串
"(010)-62661617"
和字符串"01062661617"
的正则表达式包括(c )
A.r"\(?\d{3}\)?-?\d{8}"
B. r"[0-9()-]+"
C.r"[0-9(-)]*\d*"
D.r"[(]?\d*[)-]*\d*"
- 能够完全匹配字符串“c:\rapidminer\lib\plugs”的正则表达式包括( )
A. “c:\rapidminer\lib\plugs”
B. “c:\rapidminer\lib\plugs”
C. “(?i)C:\RapidMiner\Lib\Plugs” ?i:将后面的内容的大写变成小写
D. “(?s)C:\RapidMiner\Lib\Plugs” ?s:单行匹配 - 能够完全匹配字符串“back”和“back-end”的正则表达式包括( A BCD)
A. “\w{4}-\w{3}|\w{4}”
B. “\w{4}|\w{4}-\w{3}”
C. “\S±\S+|\S+”
D. “\w*\b-\b\w*|\w*” - 能够完全匹配字符串“go go”和“kitty kitty”,但不能完全匹配“go kitty”的正则表达式包括(AD)
A. “\b(\w+)\b\s+\1\b”
B. “\w{2,5}\s*\1”
C. “(\S+) \s+\1”
D. “(\S{2,5})\s{1,}\1” - 能够在字符串中匹配“aab”,而不能匹配“aaab”和“aaaab”的正则表达式包括(B C)
A. “a*?b”
B. “a{,2}b”
C. “aa??b”
D. “aaa??b”
二、编程题
1.用户名匹配
要求: 1.用户名只能包含数字 字母 下划线
2.不能以数字开头
3.⻓度在 6 到 16 位范围内
from re import *
def new_names(str1):
result=fullmatch(r'[^0-9]\w.{6,16}',str1)
if result:
print(f'正确')
else:
print(f'错误')
new_names('1amsdka;ls')
- 密码匹配
要求: 1.不能包含!@#¥%^&*这些特殊符号
2.必须以字母开头
3.⻓度在 6 到 12 位范围内
def new_pw(str1):
result=fullmatch(r'[a-zA-Z]([0-9]|[a-zA-Z]|[\u4e00-\u9fa5]){6,12}',str1)
if result:
print(f'正确')
else:
print(f'错误')
new_pw('aslas好uja')
- ipv4 格式的 ip 地址匹配
提示: IP地址的范围是 0.0.0.0 - 255.255.255.255
- 提取用户输入数据中的数值 (数值包括正负数 还包括整数和小数在内) 并求和
例如:“-3.14good87nice19bye” =====> -3.14 + 87 + 19 = 102.86
def re_data(str1:str):
result=findall(r'[-]?\d[.]?\d+',str1)
s=[]
for x in result:
s.append(float(x))
return s
s=re_data('-3.14good87nice19bye')
print(sum(s))
-
验证输入内容只能是汉字
def names(str1): result=fullmatch(r'[\u4e00-\u9fa5]+',str1) if result: print(f'输入正确') else: print(f'输入错误') names('哈斯卡')
-
匹配整数或者小数(包括正数和负数)
def nums(str1): result=fullmatch(r'-?\d+(\.\d+)?',str1) if result: print(f'正确') else: print(f'错误') nums('-3.89')
-
验证输入用户名和QQ号是否有效并给出对应的提示信息
要求:
用户名必须由字母、数字或下划线构成且长度在6~20个字符之间
QQ号是5~12的数字且首位不能为0def names_pw(str1,str2): result1=fullmatch(r'\w{6,20}',str1) result2=fullmatch(r'[^0]\d{5,12}',str2) if result1 and result2: print(f'输入正确') else: print(f'输入错误') names_pw('jasd221','0178237')
-
拆分长字符串:将一首诗的中的每一句话分别取出来
poem = ‘窗前明月光,疑是地上霜。举头望明月,低头思故乡。’
def extract(str1:str):
result=findall(r'[\u4e00-\u9fa5]{5}',str1)
return result
s=extract('窗前明月光,疑是地上霜。举头望明月,低头思故乡。')
print(s)