python全栈开发中级班全程笔记(第二模块、第四章(三、re 正则表达式))
python全栈开发笔记第二模块
第四章 :常用模块(第三部分)
一、正则表达式的作用与方法
正则表达式是什么呢?一个问题带来正则表达式的重要性和作用
有一个需求 : 从文件中读取所有联系人的电话
1、用传统方式
f = open("re_file", "r") list_f = [] for date in f.readlines(): name, age, phone, time = date.split(",") if phone.isdigit(): list_f.append(phone) print(list_f) # 用这种方式,也取得了其中的电话号码,但是,如果我要取其他东西,就需要重新再写,有点麻烦,
2、一种新的方法,既简单,又有效,就是标准库的模块(re正则表达式)
import re f = open("re_file", "r") date = f.read() n = re.search("1[0-9]{10}", date) #找到单条符合规则的数据("以几开头[取值范围]{获取数据总长度}", 获取文件内容)
n1 = re.findall("1[0-9]{10}", date) # 找到所有符合条件的数据
print(n, n1)
二、**重点**常用正则表达式规则
'.' 默认匹配除\n以外的任意一个字符,若指定flag DOTALL,则匹配任意字符包括换行 '^' 匹配字符开头,若指定flags MULTILINE , 这种也可以匹配上(r"^a","\n abc \n asd") '$' 匹配字符结尾,若指定flags MULTILINE ,re.search('foo.$', 'foo1 \n foo2 \n ) '*' 匹配 * 号前的字符 0 次或多次,re.search('a*','aaavvc') 输出结果为 aaa '+' 匹配 + 前一个字符一次或多次,re.findall('ab+', "ab+cd+abb+bba") 输出结果为['ab','abba'] '?' 匹配前一个字符 1 次或 0 次,re.search('b?','alex').group() 匹配 0 次 '{m}' 匹配当前一个字符长度为 m 次,re.search('b{3}', 'alexbbbs').group() 匹配到 'bbb' '{n,m}' 匹配前一个字符长度为 n 到 m 次,re.findall('ab{1,3}','abb abc abbcbbb') 结果‘abb','ab','abb' '[0-9]' 匹配指定范围 0 到 9 的所有数字范围,也可是 0-5 匹配 5 以内的数字范围 '[a-z]' 匹配指定范围 a 到 z 的所有小写字符,也可为 a-g 匹配 abcdefg 内范围 '|' 匹配 | 左或 | 右的字符,re.search('abc|ABC','ABCBabcCD').group() 结果 'ABC' '(...)' 分组匹配, re.search("(abc){2}a(123|45)","abcabca456c").groups() 结果 ('abc', '45')
实际举例操作 v = "asde12vv33abbaff98aaccd"re.search("...", v) # 默认匹配除\n以外的任意一个或多个字符,(几个点,匹配几个字符) re.search("^as", v) # 匹配字符开头,如果不设置,会返回空,("^*****")等同于 re.match("as",'asd') re.search('d$', v) # 匹配字符结尾,如果用在match上面,可以实现精确匹配 match('d$','d')返回d(必须只匹配一个字符) re.search('ab*', 'aa')# '匹配 * 号前的字符 0 次或多次( 0 次匹配不到也会返回'')(匹配 * 号前首次查找字符的最后为止) re.findall('a+', 'aaaaab+cd+abb+bba') # 匹配 + 前一个字符一次或多次,('aaaaabbbbbb') 会返回['abbbbbbb'] re.findall('.+', v) # 或和, 联合使用匹配所有字符 re.search("b?", v) # 匹配 ? 前一个字符 1 次或 0 次(最多只能匹配 ? 前字符 1 次) re.search('b{2}', v) # {指定匹配字符长度},多余只取指定数量,条件不足会返回空(None) re.findall('[a-c]{1,3}', 'abb abc abbbbab abcdefg') # 匹配前一个字符长度范围{最短字符串n, m最长字符串} re.search('Will|will', 'will') # (匹配 Will 或者 | 匹配小写 will) re.search('[w|W]ill', 'Will') # 也可简写成这样 把区分的值写进列表内 [W|w] re.search('([a-z]+)([0-9]+)', "will678").groups() # 注意语法,分组匹配和 groups() 配合使用才能起效, re.search("(abc){2}a(123|45)","abcabca456c").groups() # 注意语法和 groups() 的用法
三、常用表达式(二)
"\A" 只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的,相当于re.match('abc', 'willabc') "\Z" 匹配字符结尾,同 $ "\d" 匹配数字 0-9 (0到9)'\d' = [0-9], '\d+' = 匹配任意长度的字符,至少1个(常用重点方法) "\D" 匹配非数字(除数字之外的字符) "\w" 匹配[A-Za-z0-9] [大写 A 到 Z 所有字符 小写 a 到 z 所有字符 数字 0 到 9 所有数字](常用重点方法) "\W" 匹配非[A-Za-z0-9] (匹配特殊字符)(除[A-Za-z0-9]外的特殊字符) "\s" 匹配空白字符( \n \t \r)包括空格,re.search("\s+","ab\t c1\n 3").group() 返回 "\t" "(?P<name>...)" 分组匹配 re.search('^ab','abd') == re.match('ab', abd) == re.search('\Aab', 'willab')
实际举例操作 v = "asde12vv33abbAAa\nf98aa#$ccd"re.search('\d+', '1455222d8852s')# '\d+' = 匹配任意长度的字符,遇见不符合条件字符终止,
至少能匹配一个(没有数字除外) re.search('\D+', v) # 匹配非数字(除数字之外的字符)和 \d 相反,遇见不符合条件字符终止 re.search('\w+', v) # 匹配所有 [A-Za-z0-9] 遇见不符合条件字符终止 re.search('\W+', v) # 匹配非[A-Za-z0-9] (特殊字符包含空格)遇见不符合条件字符终止 vr1 = "asde12vv3\t3abbAAa\nf98aa#\r$ccd"re.search('\s+', vr1) # 匹配空白字符( \n \t \r)包括空格,遇见不符合条件字符终止 re.findall('\s+', vr1) # 用 re.findall()语法,相同的方式可匹配所有符合条件的字符 vr2 = '410573199906067745' # 身份证号码 re.search("(?P<province>\d{3})(?P<city>\d{3})(?P<birth>\d{8})(?P<end>\d{4})", vr2).groups()
# 以元祖形式返回(没有命名)目的是把身份证号的地区和生日区分,注意:只要涉及到分组必须用 groups() 语法 re.search("(?P<province>\d{3})(?P<city>\d{3})(?P<birth>\d{8})(?P<end>\d{4})", vr2).groupdict()
# 以字典形式返回并命名
四、re匹配语法
1、re.match # 从头开始匹配(只取第一个(或设定)值,如果没有就会返回 None (空)匹配单个) 2、re.search # 匹配包含全局内搜索,匹配到值并返回(只匹配单个值) 3、re.findall # 以列表形式,返回符合条件的所有值 4、re.split # 把匹配到的字符当做列表分隔符 5、re.sub # 匹配字符并替换 6、re.fullmatch # 全部匹配(规则,符合规则的字符) 7、re.compile(pattern,flags=0) # 负责定义规则 re.fullmatch() 的拆解部分,可以配合使用
1、re.match
re.match() # 从头开始匹配(只取第一个(或设定)值,如果没有就会返回 None (空)匹配单个) v = "asde12vv33bff98ccd" va = re.match("[0-9]", v)
2、re.search
re.search() # 全局内搜索,匹配到值并返回(只匹配单个值) vb = re.search("[0-9]", v) # 发现返回的是一个对象 if vb: # 先判断返回值不为空(否则会报错) vr = vb.group() # 再用语法得到返回值 print(vr) <_sre.SRE_Match object; span=(4, 5), match='1'>(span=(值的片位置),match=搜索的值) print(vb)
3、re.findall
re.findall() # 把所有匹配的字符放入列表并返回 vc = re.findall("[0-9]", v) print(vc)
4、re.split
vs = re.split() # 把匹配好的字符当做列表分割符 vs1 = "will25ale#22ja-ck28" vsa = re.split('\d', vs1) # 发现分开了,却有很多空格的字符串,因为\d 只取一个值 vsb = re.split('\d+', vs1) # \d+ 取多个值,最后一个空字符串因为最后有一个数字被代替 # 语法反转 vsc = re.findall('\d+', vs1) # 发现此语法正好相反,把数字之外的字符当做列表分割符并返回列表内数字值 # 特殊符号分割 vsd = re.split('\d+|#|-', vs1) # 如果字符串内包含多个字符,可以这样分割,空字符串代表数字和分隔符冲突 # 转义和分割结合讲解 vs1 = "will25a|l\e#22|ja-ck28" # 加 2 个 |(管道符)和一个 \(斜杠) vse = re.split('\|', vs1) # 以管道符分割,需在管道符前添加一个斜杠('\|') vsf = re.split('\\\\', vs1) # 以斜杠分割,需写 4 个斜杠,("\\\\") vsfa = re.split('\d+', vs1, maxsplit=2) # maxsplit=要替换几个
5、re.sub
re.sub() # 匹配字符并替换, # 语法:re.sub(pattern,repl,string,count=0,flags=0) vs1 = "will25al#22ja-ck28" vsg = re.sub('\d+', '=', vs1, count=2) # (匹配的值,替换的值,字符串, 要替换几个)
6、re.fullmatch
re.fullmatch(pattern, string, flags=0) # 全部匹配返回结果,否则返回 None vsh = re.fullmatch('\w+@\w+\.(com|cn|edu)', "will@yilong.cn") # 匹配邮件为例
7、re.compile
re.compile() # 只负责定义规则,re.fullmatch() 的拆解部分,可以配合使用,一个定义一个判断 vsk = re.compile('\w+@\w+\.(com|cn|edu)') # 定义规则 vsm = vsk.fullmatch("will@yilong.cn").group() # 匹配规则 # 和 fullmatch 效果相同(如果一个规则需处理多个内容,用此方法,效率高,减少代码重复)
五、Flags标志符语法
re.I(re.IGNORECASE) : 忽略大小写(括号内为完整写法,以下相同) re.M(re.MULTILINE) : 多行模式,改变开头 ^ 和 结尾 $ 的行为,自动识别 \n(换行符)并以行为查找范围 re.S(re.DOTALL) :改变 "." 的行为,不加 S ,换行符不匹配,加了 S ,换行符在内所有都匹配 re.X(re.VERBOSE) : 可以给你的表达式写注释,使其更可读
# 代码实际举例操作 vsn = re.search("a",'Alex', re.I) # 正常情况匹配不上,这样就可以匹配 vso = re.search("wto.$", "wto1\n wto5", re.M)
# 正常情况是匹配后者,但加上 re.M 自动识别 \n(换行符)并以行为查找范围查找前者 vsp = re.search(".", "\n", re.S) # 原本 不匹配换行符,加了re.S ,就可以匹配换行符在内的所有 vsr = re.match("a # 匹配A", "alex", re.X) # 可以在匹配字符内设置注释 vss = re.search("a # 匹配A", "alex") # 如果不加 re.X 会误认为是语法
练习题:
1、验证手机号是否合法
2、验证邮箱是否合法
3、开发一种简单的 python 计算器,实现加减乘除及括号优先级解析
题为 :js = 1-2*((60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))
解题思路:
1、先计算小括号最内部的结果 (用正则式把小括号内的字符串取到)
2、再计算最后结果,把所有计算的值再用替换的方式换到公式内
3、用找出标记符号的方法让符号两侧数字计算
# 先写好字体颜色搭配便于调用 def font_color(olo, judge="yes"): if judge == "yes": print("\033[33;1m%s\033[0m" % olo) elif judge == "error": print("\033[31;1m%s\033[0m" % olo)
# 1、验证手机号是否合法 def user_input(): while True: phone = input("input phone :") if phone.isdigit() and len(phone) == 11: structure = re.search("(?P<dead>\d)(?P<living>\d)(?P<number>\d{9})", phone).groupdict() if int(structure["dead"]) == 1: meet = [0, 1, 2, 4] if int(structure["living"]) in meet: font_color("is phone number start[13,15,16,17,18,19]", "error") else: font_color("very good!!!", "yes") else: font_color("phone number is 1 start", "error") else: font_color("input 11 digits,not str", "error") user_input()
# 2、验证邮箱是否合法 def mail_box(): definition = re.compile("\w+@\w+\.(com|cn|net)") while True: mail = input("Mailbox account:") if "@" and "." in mail: result = definition.fullmatch(mail) if result: font_color("very good", "yes") return font_color(result.group(), "yes") else: font_color("mailbox format is [login name@domain name.com \ cn \ net]", "error") else: font_color("mailbox format is [login name@domain name.com \ cn \ net]", "error") mail_box()
# 3、计算题 js = "1-2*((60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))" def addition(string): if "+" in string: _string = string.split("+") tote = int(_string[0]) + int(_string[1]) return tote def minus(group): if "-" in group: _group = group.split("-") result = int(_group[0]) - int(_group[1]) return result def ride(array): if "*" in array: _array = array.split("*") sum = int(_array[0]) * int(_array[1]) return sum def divided(block): if "/" in block: _block = block.split("/") gross = int(_block[0]) / int(_block[1]) return gross def inside(): inner = re.findall("\([^()]+\)", js) ago, big, middle, after = [i.strip("()") for i in inner] def within(): first = divided(ago) form_big = re.split("-|\+", big) one = re.split("/", form_big[1]) res_one = ride(one[0]) / int(one[1]) tow = re.findall("\d+", form_big[2]) tow_ = [int(i) for i in tow] res_tow = tow_[0] / tow_[1] * tow_[2] / tow_[3] * tow_[4] three = re.split("/", form_big[3]) res_three = ride(three[0]) / int(three[1]) res_big = first * (int(form_big[0]) - res_one + res_tow + res_three) return res_big RES_WIT = within() middle_res = ride(middle) after_, afterd = after.split("-") afterd = ride(afterd) after_ = int(after_) - afterd res_ = middle_res / after_ inner_ = re.split("\(|\)", js) inn_one = inner_[2].split("+") res_inn_one = minus(inn_one[0]) center = res_inn_one + RES_WIT - res_ rec, rec_ = re.findall("\d+", inner_[0]) res_rec = int(rec_) * center res_tote = int(rec) - res_rec print(res_tote) inside()