day18:正则表达式和re模块

时间:2023-12-24 09:46:49

1,复习递归:返回值,不要只看到return就认为已经返回了,要看返回操作是在递归的第几层发生的,然后返回给了谁,如果不是返回给最外层函数,调用者就接收不到,需要再分析,看如何把结果返回回来,超过最大递归限制的报错,只要写递归,必须要有结束条件

2,几乎所有的递归问题都可以用循环来解决问题,只不过有时候,循环考虑的事情更多更复杂,递归会相对少一些,循环写好了,效率有时候会比递归好一点,有时候需要想太多问题,所以就用递归,比如快排,递归和算法联系很紧密,会把问题变简单,效率高。平时递归用的不多,除非,算法分析什么的,如果未来不做这些,工作的很长一段时间都是用不到的。面试有时候会考,递归是非常经典的计算机算法和思想,能看懂,能掌握几个经典的数学算法和概念就可以了

3,作业题一,斐波那契数列递归实现,一定要注意结束条件和传递,递归一般都是结果往前推的

# 1,1,2,3,5,8,13,21,34,55
# 第一种,树性结构
def fib(n):
if n == 1 or n ==2:
return 1 # 结束条件
else:
return fib(n-1) +fib(n-2)
print(fib(10)) # 循环实现
def fib(n):
num1, num2 = 1,1
num = 0
if n==1 or n==2:
return 1
else:
for i in range(n-2):
num = num1 + num2
num1 = num2
num2 = num
return num
print(fib(8)) # 第三种
# 一次调用,那就返回两个值
def fib(n,l=[0]): # 默认参数的陷阱问题,无论发生多少次调用,这个地方都只是用这一个值,利用了这个陷阱问题,在不同的函数调用之间传递值
l[0] +=1
print(l[0])
if n == 1:
return 1
if n ==2 :
l[0]-=1
return 1,1
else:
a,b = fib(n-1)
l[0]-=1
print(l[0]) # 这只是个计数器,计算调用了多少次,最后减完了就可以返回结果了
# a,b = b,a+b
if l[0] == 0:
return a+b
return b,a+b print(fib(11))

4,发生一次调用和两次调用的会非常慢,因为是树形调用,2的n次方和n次的区别

5,作业题二,阶乘,注意fac1001会超过最大递归限制

# 作业题二:阶乘factorial  1 1*2

# 循环实现
def fac(n):
if n == 1:
return 1
else:
b = 1
for i in range(1,n+1):
b = i * b
return b
print(fac(4)) # 递归实现
def fac(n):
if n == 1:
return 1
else:
return n*fac(n-1) print(fac(3))

6,re模块和正则表达式,对比time模块和时间,时间是现实存在的,re模块背后已经存在的就是正则表达式,首先学习正则表达式,然后学习正则表达式操作正则,正则表达式是用来做字符串匹配的,从一大段文章中匹配首先要学习规则,想要Python来做这件事,就要用到re模块。

老师的博客地址:https://www.cnblogs.com/Eva-J/articles/7228075.html#_label10

7,比方说你在注册一个网站,他会检测你输入的格式是不是有问题,这些都是正则来实现的

8,正则规则是客观存在的,世界上任何一种语言都可以有正则的,正则规则在处理字符串上有非常大的优势,正则里面所有的东西,Python里面翻译过来都是字符串,看一个简单的例子

# 判断手机号码是否合法
# 如果是从整个文件里面找出所有符合条件的手机号码,显然这样处理会很麻烦,而正则表达式却可以轻松的处理掉,他有先天的优势
while True:
phone = input("please input you phone number:")
if len(phone) == 11 \
and phone.isdigit() \
and (phone.startswith('')\
or phone.startswith('') \
or phone.startswith('') \
or phone.startswith('')):
print("合法的手机号")
else:
print('不合法') # 正则表达式实现
import re
phone = input("please input you phone number:")
if re.match('^(13|14|15|18)[0-9]{9}$',phone): # 正则规则
print("合法的手机号")
else:
print('不合法')

9,在同一个位置可能出现的字符范围的时候,正则表达式的用武之地就来了

10,用Python来处理正则表达式用到了re模块,三个函数用的最多查找,findall,search,match,

import re

s = 'jasdhfieafy73o84&……%o734yfuye*bfjb98u833242jjxb%'
ret = re.findall('a', s)
# ['a', 'a'],返回所有匹配到的结果放在列表中。
print(ret) ret = re.findall('[a-z]+',s)
print(ret)
# ['jasdhfieafy', 'o', 'o', 'yfuye', 'bfjb', 'u', 'jjxb']

11,search函数返回第一个找到的匹配值

s = 'jasdhfieafy73o84&……%o734yfuye*bfjb98u833242jjxb%'
ret = re.search('a',s)
# <re.Match object; span=(1, 2), match='a'>
print(ret) # 返回一个结果的对象
# 从前往后找到一个就返回需要调用group来拿结果,找不到会返回None,不可以用group来取值,会报错。
print(ret.group())
# a
# 一般情况下我们会,找到值再调用group函数
if ret:
print(ret.group())

12,match函数,必须从头开始匹配

s = 'jasdhfieafy73o84&……%o734yfuye*bfjb98u833242jjxb%'
ret = re.match('a',s) #从头开始匹配,如果正则规则从头开始可以匹配上,就返回一个变量,匹配的内容需要调用group才能显示,
# 如果没有匹配上,就返回None,调用group会报错
print(ret)
# None
ret = re.match('j',s).group()
print(ret)
# j
ret = re.match('ja',s).group()
print(ret)
# ja
if ret:
print(ret.group())

13,split函数,除了查找之外其他的函数

ret = re.split('[ab]','abcd')
print(ret)
# ['', '', 'cd'] # 先按照a分割得到''和'bcd' 再按照b在split ret = re.split('ab','abcd')
print(ret)
# ['', 'cd'] # 按照ab来整体分割

14,sub函数

s = 'fkdjh9845tbfdog7720834y83hdiub'
ret = re.sub('\d','$',s) # 和replace很像,但是replace不能运用正则
print(ret)
print(s) ret = re.sub('\d','$',s,3) # 默认的是全部替换,如果我只想替换指定次数
print(ret)
print(s)

15,subn函数

s = 'fkdjh9845tbfdog7720834y83hdiub'
ret = re.subn('\d','$',s)
print(ret)
# 返回元祖,替换的结果,替换了多少次。

16,compile函数,一次编译,多次运用。将正则表达式编译成为一个正则表达式对象,内置函数也有compile,对一个字符串类型的代码提前进行编译,就可以用eval或者exec来运行了。

正则也是字符串,所以这里的compile和字符串是很像的。

s1 = 'fkdjh9845tbfdog7720834y83hdiub'
s2 = '7720834y83hdiub'
obj = re.compile('\d{3}') #匹配三个数字
ret = obj.search(s1)
if ret:
print(ret.group())
ret = obj.search(s2)
if ret:
print(ret.group()) # 运行结果:
984
772

17,finditer方法,迭代器里面的每一项都需要用i.group来查结果

s = 'fkdjh9845tbfdog7720834y83hdiub'
ret = re.finditer('\d',s) # 返回一个存放匹配结果的迭代器
print(ret) #<callable_iterator object at 0x101eb2278>
print(next(ret).group()) #查看第一个结果
print(next(ret).group()) # 查看第二个结果
print([i.group() for i in ret]) # 查看剩余的所有结果
print('----------------') for i in ret:
print(i.group()) # 迭代器没有强转的过程,上面取过一次了,这儿不再取了

18,group取分组内的内容,用序号来取,这个取分组的机制是由Python决定的,和正则表达式没有关系的,这样的话就感觉比findall好了

import re
ret = re.search('^[1-9]\d{14}(\d{2}[0-9x])?$','')
print(ret.group())
print(ret.group(1)) ret = re.search('^[1-9](\d{14})(\d{2}[0-9x])?$','')
print(ret.group())
print(ret.group(1))
print(ret.group(2)) # 取分组里面的内容 运行结果:
331182199400400500
500
331182199400400500
31182199400400
500

19,findall分组优先和取消分组优先

ret = re.findall('www.(baidu|lisa).com','www.lisa.com')
print(ret) #['lisa']
# 这是因为findall会优先把匹配结果组里的内容返回,如想要要取消分组优先看下面
# 这个规则专门为取消分组优先做的
ret = re.findall('www.(?:baidu|lisa).com','www.lisa.com') # ?:
print(ret) #['www.lisa.com']

20,split函数的分组优先

# 只要有分组,就会特殊对待分组里面的内容
s = 'sdjhfsdig123dfbirisb5df,jbfhj888dnf'
ret = re.split('\d+',s)
print(ret) # ['sdjhfsdig', 'dfbirisb', 'df,jbfhj', 'dnf'] ret = re.split('(\d+)',s) # 加到一个分组里面,会保留切掉的内容
print(ret) # ['sdjhfsdig', '123', 'dfbirisb', '5', 'df,jbfhj', '888', 'dnf']

21,爬虫小练习

# 简单版本
import re
from urllib.request import urlopen def get_page(url):
response = urlopen(url)
return response.read().decode('utf-8') def parse_page(s):
# 这个正则可以对照着html源码来看,re.S匹配空格
ret = re.findall(
'<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>'
'.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>',s,re.S)
return ret def main(num):
url = 'https://movie.douban.com/top250?start=%s&filter=' % num # 这儿是由网址决定的
response_html = get_page(url)
ret = parse_page(response_html)
print(ret) count = 0
for i in range(10): # 10页
main(count)
count += 25

22,爬虫复杂版本,链接,分析图片,登录,这些都是后话,以后再说,宗旨就是爬取下来,分析

# 复杂版本
import re
from urllib.request import urlopen
import json def get_page(url):
response = urlopen(url)
return response.read().decode('utf-8') # 拿到字节码,编码 def parse_page(s):
# 这个正则可以对照着html源码来看,re.S匹配空格,联系上下文的匹配,只要分组里面的内容,利用分组优先,只提取出来,非贪婪匹配,
# 匹配到什么时候为止,分组名指代分组内匹配到的内容,?P就代表给分组取名字
com = re.compile(
'<div class="item">.*?<div class="pic">.*?<em .*?>(?P<id>\d+).*?<span class="title">(?P<title>.*?)</span>'
'.*?<span class="rating_num" .*?>(?P<rating_num>.*?)</span>.*?<span>(?P<comment_num>.*?)评价</span>') ret = com.finditer(s,re.S) # 返回迭代器,循环迭代器
for i in ret:
yield {
"id":i.group("id"),
"title":i.group("title"),
"rating_num":i.group("rating_num"),
"comment_num":i.group("comment_num")
} def main(num):
url = 'https://movie.douban.com/top250?start=%s&filter=' % num # 这儿是由网址决定的
response_html = get_page(url)
ret = parse_page(response_html) # 返回的是一个生成器
print(ret)
f = open('move_info7','a',encoding='utf8')
for obj in ret:
print(obj)
data = json.dumps(obj,ensure_ascii=False)
f.write(data+'\n') # json序列化是day20的内容,到时候会有详细的讲解 if __name__ == '__main__':
count = 0
for i in range(10): # 10页
main(count)
count += 25 # url 从网页上把代码爬取下来
# bytes -utf8,网页内容就是待匹配的内容
# ret = re.findall(正则,待匹配的字符串),ret 所有匹配到内容的列表
# 持续爬,过一段时间爬取一次

23,计算器大作业,老师讲解后在更新

# 基本思路:
# 首先得到一个字符串
# 去空格
# 没有空格的字符串
# 先算最里层括号里面的:找括号,且括号里没有括号
# 得到了一个没有括号的表达式:只有加减乘除,从左到右先找到一个乘除法 ---重复
# 所有的乘除法都做完了
# 计算加减 --加减法
# 只有一个数了,就可以结束了

24,计算机大作业,跟着老师讲解自己写的代码

import re

# 计算乘除
def cal_ret_son_1(ret):
if '/' in ret:
new_ret = ret.split('/')
s = str(float(new_ret[0]) / float(new_ret[1]))
return s
elif '*' in ret:
new_ret = ret.split('*')
s = str(float(new_ret[0]) * float(new_ret[1]))
return s # 计算加减 # 这个函数可以简化的其实
def cal_ret_son_2(ret):
if '+' in ret:
new_ret = ret.split('+')
s = str(float(new_ret[0]) + float(new_ret[1]))
return s
elif '-' in ret:
new_ret = ret.split('-')
s = str(float(new_ret[0]) - float(new_ret[1]))
return s # 第三步,计算没有括号的表达式
def cal_express_no_bracket(exp):
exp = exp.strip('()') # 去掉两边的括号 # 先乘除后加减
while True: ret = re.search('\d+\.?\d*[*/]-?\d+\.?\d*', exp)
if ret:
ret_son = ret.group()
s = cal_ret_son_1(ret_son)
exp = exp.replace(ret_son,s)
dealwith_minus(exp)
else:break while True:
exp = dealwith_minus(exp)
ret = re.search('\d+\.?\d*[+\-]-?\d+\.?\d*', exp)
if ret:
ret_son = ret.group()
s = cal_ret_son_2(ret_son)
exp = exp.replace(ret_son, s)
dealwith_minus(exp)
else:break # ret = re.findall('-?\d+\.\d*',exp)
# sum = 0
# for i in ret:
# sum += float(i) return exp # 第三步骤,处理+-连着的这种情况
def dealwith_minus(exp):
if "+-" in exp:
exp = exp.replace('+-','-')
elif "--" in exp:
exp = exp.replace('--','+')
return exp # 第二步 提取括号里面没有其他括号的表达式
# findall search match 三种方法供选择
# findall没有search好,为何呢?比如说3*5*6+2*3 他只能匹配到3*5和2*3,match只能匹配从头开始的
def remove_bracket(new_express):
while True:
express_no_brackets = re.search('\([^()]+\)',new_express) # 中间可以匹配任意多个不是括号的
if express_no_brackets:
express_no_brackets = express_no_brackets.group()
exp = cal_express_no_bracket(express_no_brackets)
new_express = new_express.replace(express_no_brackets,exp)
new_express = dealwith_minus(new_express)
else:
break
ret = cal_express_no_bracket(new_express)
return ret
express = '1 - 2 * ( (60-30 +(-40/5*8) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )'
print(eval(express)) # 用来验证结果是否正确,会有一点点的误差
# 第一步 去空格
new_express = express.replace(' ','')
print(remove_bracket(new_express)) # 用函数封装以后,代码明显清晰了很多
import re
express = '1 - 2 * ( (60-30 +(-40/5*8) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )' print(eval(express)) # 用来验证结果是否正确,会有一点点的误差
# 第一步 去空格
new_express = express.replace(' ','') # 计算乘除
def cal_ret_son_1(ret):
if '/' in ret:
new_ret = ret.split('/')
s = str(float(new_ret[0]) / float(new_ret[1]))
return s
elif '*' in ret:
new_ret = ret.split('*')
s = str(float(new_ret[0]) * float(new_ret[1]))
return s # 计算加减
def cal_ret_son_2(ret):
if '+' in ret:
new_ret = ret.split('+')
s = str(float(new_ret[0]) + float(new_ret[1]))
return s
elif '-' in ret:
new_ret = ret.split('-')
s = str(float(new_ret[0]) - float(new_ret[1]))
return s # 第三步,计算没有括号的表达式
def cal_express_no_bracket(exp):
exp = exp.strip('()') # 去掉两边的括号 # 先乘除后加减
while True:
dealwith_minus(exp)
ret = re.search('\d+\.?\d*[*/]-?\d+\.?\d*', exp)
if ret:
ret_son = ret.group()
s = cal_ret_son_1(ret_son)
exp = exp.replace(ret_son,s)
else:break while True:
exp = dealwith_minus(exp)
ret = re.search('\d+\.?\d*[+\-]-?\d+\.?\d*', exp)
if ret:
ret_son = ret.group()
s = cal_ret_son_2(ret_son)
exp = exp.replace(ret_son, s)
else:break
return exp # 第三步骤,处理+-连着的这种情况
def dealwith_minus(exp):
if "+-" in exp:
exp = exp.replace('+-','-')
elif "--" in exp:
exp = exp.replace('--','+')
return exp # 第二步 提取括号里面没有其他括号的表达式
# findall search match 三种方法供选择
# findall没有search好,为何呢?比如说3*5*6+2*3 他只能匹配到3*5和2*3,match只能匹配从头开始的 while True:
express_no_brackets = re.search('\([^()]+\)',new_express) # 中间可以匹配任意多个不是括号的
if express_no_brackets:
express_no_brackets = express_no_brackets.group()
exp = cal_express_no_bracket(express_no_brackets)
new_express = new_express.replace(express_no_brackets,exp)
new_express = dealwith_minus(new_express)
else:
break ret = cal_express_no_bracket(new_express)
print(ret)