day18:正则表达式和re模块

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('13')\
        or phone.startswith('14') \
        or phone.startswith('15') \
        or phone.startswith('18')):
        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])?$','331182199400400500')
print(ret.group())
print(ret.group(1))

ret = re.search('^[1-9](\d{14})(\d{2}[0-9x])?$','331182199400400500')
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)

 

posted on 2018-12-16 11:43  Lisa_Guo  阅读(203)  评论(0编辑  收藏  举报