• Python全栈开发记录_第七篇(模块_time_datetime_random_os_sys_hashlib_logging_configparser_re)


    这一篇主要是学习python里面的模块,篇幅可能会比较长

    模块的概念:在Python中,一个.py文件就称之为一个模块(Module)。

    模块一共三种:

    • python标准库
    • 第三方模块
    • 应用程序自定义模块

    模块导入方法(重要):

    # 1、import 语句
    import sys
    import test # 导入其他模块时,解释器先通过sys.path搜索路径找到test.py,然后执行完test.py后将内容赋值给了test
    
    print(test.add(1,2))    # 调用test模块内的方法
    print(sys.path)

    如果说test里面我只需要导入其中一个方法,并不需要导入整个模块的方法咋搞呢?

    # 2、from…import 语句
    # from test import *      # 导入模块内的所有方法、变量。。。(一般不推荐使用)
    from test import add    # 从模块调用方法,只导入了具体方法,变量啥的都不会导入(推荐使用),但是会从上到下执行test.py文件
    print(add(2,3))
    from test import add as ad  # 给add方法起个别名
    print(ad(1,2))

    那如果后续模块很多了怎么办呢?此时就引入了包的概念,为了避免模块名冲突,Python又引入了按目录来组织模块的方法,称为包(Package)。

    注意,每一个包目录下面都会有一个__init__.py的文件,这个文件是必须存在的,否则,Python就把这个目录当成普通目录(文件夹),而不是一个包。__init__.py可以是空文件,也可以有Python代码,因为__init__.py本身就是一个模块,而它的模块名就是对应包的名字

    # 包内的模块调用
    from package1.package2 import module    # 从包中引入模块
    from package1.package2.module import function   # 从包下的模块引入方法
    import package1     # 执行了package1下的__init__.py文件

     

    import sys,os
    BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))      # 返回当前目录的上一级目录
    sys.path.append(BASE_DIR)   # 将总的大目录加进来,这样就可以直接通过大目录下的包进行调用
    
    from test01 import loggertest
    loggertest.timem()

    一、time模块

    # -*- coding:utf-8 -*-
    
    import time
    
    # print(help(time))
    # 1、time()  返回时间戳
    print(time.time())  # 返回当前时间的时间戳 1541819905.0988002
    
    # 2、clock() 返回CPU处理时间
    print(time.clock()) # 返回程序开始后cpu的处理时间  这里实际上cpu只处理了一个print所以是6.413790161951408e-07
    
    for i in range(100000000):
        i*i
    print(time.clock()) # 这里cpu处理花了7.8202384583044315
    
    # 3、sleep() 等待
    time.sleep(3)    # 等待3秒
    
    # 4、gmtime()    以元祖形式返回UTC世界标准时间
    # 5、localtime() 以元祖形式返回本地时间
    print(time.gmtime())    # time.struct_time(tm_year=2018, tm_mon=11, tm_mday=10, tm_hour=3, tm_min=27, tm_sec=9, tm_wday=5, tm_yday=314, tm_isdst=0)
    print(time.localtime()) # time.struct_time(tm_year=2018, tm_mon=11, tm_mday=10, tm_hour=11, tm_min=27, tm_sec=9, tm_wday=5, tm_yday=314, tm_isdst=0)
    # 从这两个结果来看我们知道相差了8小时,实际上localtime()是以这种元祖的形式返回本地时间,而gmtime()是返回UTC世界标准时间,也就是本初子午线那的格林尼治时间
    
    # 6、asctime()   将元祖格式的时间转换成字符串格式,如果不传参默认就是time.asctime(time.localtime())
    print(time.asctime(time.gmtime()))  # 返回UTC世界标准时间,不过是按照这种格式Sat Nov 10 03:34:26 2018
    
    # 7、ctime()  将数字格式的时间转换成字符串格式,不传参默认就是time.ctime(time.time())
    print(time.ctime(1))    # 代表从1970 1月1日 8点开始后1秒,所以返回Thu Jan  1 08:00:01 1970
    # 注意asctime()和ctime()只是传入的参数不同,实际上返回的格式是一样的
    
    # 8、mktime()   将元祖格式的时间转换成时间戳
    print(time.mktime(time.localtime()))    # 返回当前时间戳,精度没有time.time()高 1541821331.0
    
    # 9、strftime(format, tuple)  将元祖格式的时间转换成自定义格式的时间,这个还是比较实用的,不过似乎没看到毫秒的表达式
    print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))   #  以自定义字符串格式化返回当前时间,可以不写time.localtime(),默认就是
    # 下面是format中的含义
    """
    %y 两位数的年份表示(00-99)
    %Y 四位数的年份表示(000-9999)
    %m 月份(01-12)
    %d 月内中的一天(0-31)
    %H 24小时制小时数(0-23)
    %I 12小时制小时数(01-12)
    %M 分钟数(00=59)
    %S 秒(00-59)
    %a 本地简化星期名称
    %A 本地完整星期名称
    %b 本地简化的月份名称
    %B 本地完整的月份名称
    %c 本地相应的日期表示和时间表示
    %j 年内的一天(001-366)
    %p 本地A.M.或P.M.的等价符
    %U 一年中的星期数(00-53)星期天为星期的开始
    %w 星期(0-6),星期天为星期的开始
    %W 一年中的星期数(00-53)星期一为星期的开始
    %x 本地相应的日期表示
    %X 本地相应的时间表示
    %Z 当前时区的名称
    %% %号本身
    """
    
    # 10、time.strptime(string, format)  将字符串格式时间通过format解析成元祖格式时间
    print(time.strptime("2018-11-10 11:52:56", "%Y-%m-%d %H:%M:%S"))   # 所以这里返回time.struct_time(tm_year=2018, tm_mon=11, tm_mday=10, tm_hour=11, tm_min=52, tm_sec=56, tm_wday=5, tm_yday=314, tm_isdst=-1)
    
    # 11、tzset()    根据环境变量TZ重新初始化时间相关设置,使用很少,可百度查阅资料

    下面是时间关系的转换图: 

    二、datetime模块

    # -*- coding:utf-8 -*-
    
    import datetime,time
    
    print(datetime.datetime.now())   # 返回当前时间,精确度很高2018-11-10 14:54:39.476800
    print(datetime.date.fromtimestamp(time.time()))  # 转化成2018-11-10
    print(datetime.datetime.now() + datetime.timedelta(3))  # 当前时间+3天
    print(datetime.datetime.now() + datetime.timedelta(-3))  # 当前时间-3天
    print(datetime.datetime.now() + datetime.timedelta(hours=-3))  # 当前时间-3小时
    print(datetime.datetime.now() + datetime.timedelta(minutes=3))  # 当前时间+3分钟
    print(datetime.datetime.now() + datetime.timedelta(weeks=3))  # 当前时间+3星期
    
    print(datetime.datetime.now().replace(month=3, day=4, hour=5, minute=6))    # 将月日时分分别替换了,变成了2018-03-04 05:06:23.489800

    三、random模块 

    # -*- coding:utf-8 -*-
    
    import random
    
    #  print(help(random))
    # print(random.random())  # 随机生成0-1之间的一个数,比如0.2513054977155842
    # print(random.randint(1,3))  # 随机在1-3之间取一个数字,比如3,注意这个包括右边
    # print(random.randrange(1, 3))  # 随机在1-2直接取一个数字,比如2,注意这个不包括右边
    # print(random.sample([[1,2,3], "test", 2, 3],2)) # 随机从列表中取2个值,并以列表形式返回,比如['test', 3]
    # print(random.choice([[1,2,3], "test", 2]))  # 从列表[[1,2,3], "test", 2]中随机取一个,比如[1,2,3]
    # print(random.choices([[1,2,3], "test", 2])) # 跟上面类似,不过返回的是列表,比如[test]
    # print(random.choice("test"))    # 从字符串中取一个字符,比如e
    
    # 做一个简单的练习,生成一个4位的随机验证码,包含字母和数字
    def check_code(n):
        # ord()函数就是用来返回单个字符的ascii值(0-255)或者unicode数值(),chr()则相反
        random_str = chr(random.randint(ord("A"), ord("Z")))    # 生成随机一个字母
        id_code = ''
        for i in range(n):
            id_code+=str(random.choice([random.randint(0,9), random_str]))
        return id_code
    
    a = check_code(4)
    print(a)

     

    四、OS模块

    # -*- coding:utf-8 -*-
    
    import os
    
    print(os.getcwd())  # 获取当前的工作目录
    os.chdir(r"C:Users")   # 切换至C:Users目录,r这里表示后面的字符串就是原生字符串,直接读取字符串
    print(os.curdir)    # 返回当前目录"."
    print(os.pardir)    # 返回当前目录的父目录".."
    os.makedirs("liu\long\kang")    # 在当前目录下生成多层递归目录,如果存在了还创建则报错
    os.removedirs("liu\long\kang")    # 若目录为空则删除,并递归去判断上一级目录,如果为空也删除,一直递归到非空目录,
                                        # 举个例子如果long下面有其它文件,则只删除kang这个目录,如果都是空目录则全部删除
    os.mkdir("liu") # 生成单个文件夹(目录),如果存在了还创建则报错
    os.rmdir("liu") # 删除单个空目录
    os.listdir(dirname)  # 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印,注意层级目录下的不会列出
    os.remove("test.py") # 只能删除单个文件,不能删除目录
    os.rename("oldname", "newname") # 重命名文件/目录
    print(os.stat('path/filename'))   # 获取文件/目录信息
    dir_info = os.stat(os.curdir)
    print(dir_info) # os.stat_result(st_mode=16895, st_ino=16044073672586573, st_dev=699768, st_nlink=1, st_uid=0, st_gid=0, st_size=4096, st_atime=1541857420, st_mtime=1541857420, st_ctime=1541596941)
    print(dir_info.st_size) # 目录大小,其中st_atime=1541857420(文件中的数据最后被访问的时间), st_mtime=1541857420(文件内容被修改的最后时间), st_ctime=1541596941(显示的是文件的权限等改变时的时间)
    print(os.sep)   # 输出操作系统特定的路径分隔符,win下为"\",Linux下为"/"
    print(os.linesep)    # 输出当前平台使用的行终止符,win下为"
    ",Linux下为"
    "
    print(os.pathsep)    # 输出用于分割文件路径的字符串,win下为";",Linux下为":"
    print(os.name)    # 输出字符串指示当前使用平台。win->'nt'; Linux->'posix'
    os.system("bash command")  # 运行shell命令,直接显示
    os.system("dir")    # 相当于在cmd运行dir,打印当前目录在所有文件
    print(os.environ)   # 获取系统的环境变量,返回的是字典
    print(os.environ['APPDATA'])    # 取出相应的变量值
    os.path.abspath(path)  # 返回path规范化的绝对路径
    os.path.split(path) # 将path分割成目录和文件名二元组返回
    os.path.dirname(path)  # 返回path的目录。其实就是os.path.split(path)的第一个元素
    print(os.path.dirname(__file__))   # __file__是当前文件的绝对路径,所以返回当前文件的目录的绝对路径
    os.path.basename(path)  # 返回path最后的文件名。如何path以/或结尾,那么就会返回空值。即os.path.split(path)的第二个元素
    os.path.exists(path)  # 如果path存在,返回True;如果path不存在,返回False
    os.path.isabs(path)  # 如果path是绝对路径,返回True
    os.path.isfile(path)  # 如果path是一个存在的文件,返回True。否则返回False
    os.path.isdir(path)  # 如果path是一个存在的目录,则返回True。否则返回False
    os.path.join(path1[, path2[, ...]])  # 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
    print(os.path.join("c:\user", "liulongkang"))    # 打印c:userliulongkang
    os.path.getatime(path)  # 返回path所指向的文件或者目录的最后存取时间
    os.path.getmtime(path)  # 返回path所指向的文件或者目录的最后修改时间

     

    五、sys模块

    # -*- coding:utf-8 -*-
    import sys
    
    sys.argv    # 执行脚本时后面加的参数这个方法能接收,并以列表方式返回,第一个参数为文件本身,第二个开始才是用户填写的参数
                #比如,python xxx.py  test  liu ,这样sys.argv=['xxx.py', 'test', 'liu']
    sys.exit(n) # 退出程序,正常退出时exit(0),默认是正常退出
    sys.version     # 获取Python解释程序的版本信息
    sys.maxsize     # 最大的Int值
    sys.path         # 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值,是一个列表,可以通过append添加自定义的模块
    sys.platform     # 返回操作系统平台名称,可以根据不同的操作系统执行不同命令
    
    sys.stdout.write('please:') # 在python中调用print时,事实上调用了sys.stdout.write(obj+'
    ')
    sys.stdout.flush()  # python的stdout是有缓冲区的,如果把这句话sys.stdout.flush()注释的话,你就只能等到程序执行完毕,屏幕上会一次性输出
    val = sys.stdin.readline()[:-1]
    # sys.stdin.readline( )会将标准输入全部获取,包括末尾的'
    ',因此用len计算长度时是把换行符'
    '算进去了的,但是input( )获取输入时返回的结果是不包含末尾的换行符'
    '的。
    # 因此如果在平时使用sys.stdin.readline( )获取输入的话,不要忘了去掉末尾的换行符,可以用strip( )函数(sys.stdin.readline( ).strip('
    '))或sys.stdin.readline( )[:-1]这两种方法去掉换行
    print(val)

    六、hashlib加密模块 

    # -*- coding:utf-8 -*-
    import hashlib
    
    hash = hashlib.md5()    # md5算法的对象
    hash.update("hello world".encode("utf-8"))  # 将hello world加密,py3默认是unicode,加密前需要先转化成byte类型
    print(hash.hexdigest()) # 以十六进制返回,5eb63bbbe01eeed093cb22bb8f5acdc3
    
    hash.update("test".encode("utf-8"))
    print(hash.hexdigest()) # f208a28ca02d96e08bd162a0e3e00b7d
    # 其实上面最后加密的f208a28ca02d96e08bd162a0e3e00b7d就是"hello worldtest"的加密
    
    #除了md5加密还有sha加密,常用的是sha256,还有sha1、sha384。。。
    hash_sha = hashlib.sha256()
    hash_sha.update("hello world".encode("utf-8"))
    print(hash_sha.hexdigest()) # b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
    
    # 以上加密算法虽然依然非常厉害,但时候存在缺陷,即:通过撞库可以反解。所以,有必要对加密算法中添加自定义key再来做加密。
    hash_md5 = hashlib.md5('liu'.encode("utf-8"))
    hash_sha.update("hello world".encode("utf-8"))
    print(hash_sha.hexdigest())

     七、logging日志模块

    # -*- coding:utf-8 -*-
    import logging
    
    # 下面是简单的日志打印
    logging.debug("debug test")
    logging.info("info test")
    logging.warning("warn test")
    logging.error("error test")
    logging.critical("critical test")  # CRITICAL 表示产生了不可逆的错误,系统无法正常工作
    
    # 结果输出到控制台
    # WARNING:root:warn test
    # ERROR:root:error test
    # CRITICAL:root:critical test
    # 这里说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ERROR > WARNING > INFO > DEBUG > NOTSET)

      这里是直接输出到控制台,那如果我们想要保持到文本内呢?这里就需要修改配置了

    logging.basicConfig(level=logging.DEBUG,
                        format='%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s %(message)s',  # 指定日志输出的格式
                        datefmt='%a, %d %b %Y %H:%M:%S',  # 指定时间格式
                        filename='test.log',    # 日志路径,没有则输出到控制台
                        filemode='a')   # 日志写入方式,默认就是a追加
    
    logging.debug('debug message')  # 写入格式为 Sun, 11 Nov 2018 20:22:56 logging模块.py [line:23] DEBUG debug message
    logging.info('info message')
    logging.warning('warning message')
    logging.error('error message')
    logging.critical('critical message')
    # test.log就写入了上面5条,详细参数看下面
    """
    可见在logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有
    filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。
    filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
    format:指定handler使用的日志显示格式。 
    datefmt:指定日期时间格式。 
    level:设置rootlogger(后边会讲解具体概念)的日志级别 
    stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件(f=open('test.log','w')),默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。
    
    format参数中可能用到的格式化串:
    %(name)s Logger的名字
    %(levelno)s 数字形式的日志级别
    %(levelname)s 文本形式的日志级别
    %(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
    %(filename)s 调用日志输出函数的模块的文件名
    %(module)s 调用日志输出函数的模块名
    %(funcName)s 调用日志输出函数的函数名
    %(lineno)d 调用日志输出函数的语句所在的代码行
    %(created)f 当前时间,用UNIX标准的表示时间的浮 点数表示
    %(relativeCreated)d 输出日志信息时的,自Logger创建以 来的毫秒数
    %(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒
    %(thread)d 线程ID。可能没有
    %(threadName)s 线程名。可能没有
    %(process)d 进程ID。可能没有
    %(message)s用户输出的消息
    """

      上面我们就将日志写入到了日文件中,但是却发现日志没有在界面上打印,咦,那我想同时又打印又写入日志文件咋搞呢?

    # -*- coding:utf-8 -*-
    
    import logging
    
    logger = logging.getLogger("liu")   # 返回一个logger对象,如果没有指定名字将返回root logger
    
    fh = logging.FileHandler("test.log")  # 创建一个handler,用于写入日志文件
    
    ch = logging.StreamHandler()    # 再创建一个handler,用于输出到控制台
    
    # 创建格式对象,自定义日志输出格式
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    
    fh.setFormatter(formatter)  # 写入日志文件的格式就用formatter这种
    ch.setFormatter(formatter)  # 输入到控制台格式也用formatter
    
    logger.addHandler(fh)   # logger对象添加fh和ch对象
    logger.addHandler(ch)
    
    # 可以设置日志级别,不设置就打印warning及其以上的
    logger.setLevel(logging.DEBUG)
    
    logger.debug('logger debug msg')
    # logger.info('logger info msg')
    # logger.warning('logger warning msg')
    # logger.error('logger error msg')
    # logger.critical('logger critical msg')
    
    '''
    结果如下:
    2018-11-11 21:20:58,374 - liu - DEBUG - logger debug msg
    2018-11-11 21:20:58,374 - liu - INFO - logger info msg
    2018-11-11 21:20:58,374 - liu - WARNING - logger warning msg
    2018-11-11 21:20:58,374 - liu - ERROR - logger error msg
    2018-11-11 21:20:58,374 - liu - CRITICAL - logger critical msg
    '''

    八、ConfigParser配置文件模块

    配置文件如下,如果我们要用python写入咋么搞呢?

    [DEFAULT]
    name = liu
    age = 18
    job = tester
    
    [email]
    qq = 12345
    psw = 123
    
    [city]
    area = 深圳

      可以通过下面的代码生成上面的配置文件:

    # -*- coding:utf-8 -*-
    import configparser
    
    config = configparser.ConfigParser()    # 返回一个配置文件对象
    config['DEFAULT'] = {'name': 'liu',     # 添加一个默认模块,config是以键值对(字典)保存
                         'age': 18,
                         'job': 'tester'}
    config['email'] = {}    # 添加email及其具体内容
    email_c = config['email']
    email_c['qq'] = '12345'
    email_c['psw'] = '123'
    config['city'] = {'area': '深圳'}
    with open('config.ini', 'w') as file:
        config.write(file)

      那么我们生成了配置文件,我们该如何读取呢?

    import configparser
    
    config = configparser.ConfigParser()
    config.read('config.ini')   # 创建的config对象是没有任何内容的,所以要读取文件先
    print(config.sections())    # 读取除了default其它的一级栏目,这里返回['email', 'city']
    print(config['email']['qq'])    # 读取email下qq的值,其它读取类似
    # 有一个特殊的看下
    for i in config['email']:
        print(i)
    # 注意,这里除了返回了email下的key之外,还会返回default下的key,所以结果是qq、psw、name、age、job

      那么我们在使用的时候需要修改又怎么办呢?既然是字典当然可以通过字典的方式去修改。(之前说过文件不能修改只能通过覆盖同名文件的方式修改)

    import configparser
    
    config = configparser.ConfigParser()
    config.read('config.ini')   # 依旧先读取文件
    
    # 增加内容
    config['email']['msg'] = 'hello'
    config['new_section1'] = {}     # 新增一级栏目方法一
    config.add_section('new_section2')  # 新增一级栏目方法二
    config.write(open('config.ini', 'w'))   # 覆盖写将增加内容加入
    
    # 删除内容qq
    config.remove_option('email', 'qq')     # remove_option是删除非一级栏目的
    config.write(open('config.ini', 'w'))   # 覆盖写将增加内容加入
    # 删除email一级栏目
    config.remove_section('email')  # remove_section是删除一级栏目
    config.write(open('config.ini', 'w'))   # 覆盖写将增加内容加入
    
    # 改内容
    config.set('email', 'qq', 'abc')    # 修改qq为abc
    config.write(open('config.ini', 'w'))   # 覆盖写将增加内容加入

     

    九、re(正则表达式)

     python中通过re模块来实现正则的匹配。下面是11个元字符的讲解:

    # -*- coding:utf-8 -*-
    # @__author__ : Loris
    # @Time : 2018/11/8 20:26
    
    import re
    
    # findall方法是找到所有符合的匹配,并以列表形式返回
    ret = re.findall("world", "hello world,i love world")   # 这种属于完全匹配,结果是['world', 'world']
    
    # 元字符".",匹配任意除换行符"
    "外的字符(在DOTALL模式中也能匹配换行符
    ret = re.findall("w.r", "hello world,i love world")     # 结果是['wor', 'wor']
    
    # 元字符"^",匹配字符串开头。在多行模式中匹配每一行的开头
    ret = re.findall("^he", "hello world,i love world")     # 结果是['he']
    
    # 元字符"$",匹配字符串末尾,在多行模式中匹配每一行的末尾
    ret = re.findall(".d$", "hello world,i love world")      # 结果是['ld']
    
    # 元字符"*",匹配前一个字符0或多次,注意下,像*、+、?、{}都是代表数量
    ret = re.findall(".*", "hello world,i love world")      # 结果是['hello world,i love world', ''],由于*代表0到多个,所以空字符串也算
    
    # 元字符"+",匹配前一个字符1或多次
    ret = re.findall("ov+", "hello world,i love world")      # 结果是['ov']
    
    # 元字符"?",匹配前一个字符0或1次
    ret = re.findall("ov?", "hello world,i love world")      # 结果是['o', 'o', 'ov', 'o']
    
    # 元字符"{m,n}",{m}匹配前一个字符m次,{m,n}匹配前一个字符m至n次,若省略n,则匹配m至无限次
    ret = re.findall("ld{1,3}", "hello world,i love world")      # 这里是匹配ld,但是d可以是1-3次,所以匹配结果是['ld', 'ld']
    # 这里可以有疑问说为啥1-3次,为啥不是1次而是最大次数呢,这就是贪婪模式,默认就是出现最多的
    
    
    # 元字符"[]",这个是字符集,对应的位置可以是字符集中任意字符。字符集中的字符可以逐个列出,也可以给出范围,
    # 如[abc]或[a-c]。[^abc]表示取反,即非abc。所有特殊字符在字符集中都失去其原有的特殊含义,除了-、和^这三个
    ret = re.findall("w[o a]r", "hello wo rld,i love world w r")  # 匹配wor或w r或war,所以结果是['wor', 'w r']
    ret = re.findall("w[^ ,o]r", "hello warld,i love world w r")  # 匹配不是w r或wor或w,r之外的,返回['war']
    ret = re.findall("w[*,]r", "hello warld,i love world w r")   # 匹配w*r和w,r,这里*就表示普通字符
    ret = re.findall("w[w]{2}r", "hello waerld,i love world w r")  # 匹配wxxr,其中xx代表两个字符,所以结果是:['waer']
    
    
    # 元字符"",转义字符,使后一个字符改变原来的意思,原本元字符变成普通字符,部分普通字符变成能实现特殊功能
    # d    匹配任何十进制数字,等价于[0-9]
    # D    匹配任意非数字,等价于[^0-9]
    # s    匹配任意空白字符,等价于 [	
    
    fv]
    # S    匹配任意非空字符,等价于 [^	
    
    fv]
    # w    匹配任意字母数字字符,等价于[A-Za-z0-9_]
    # W    匹配非字母字符,即匹配特殊字符,等价于[^A-Za-z0-9_]
    #     匹配一个特殊字符的边界比如空格、@等特殊字符,这里举例说明下
    ret = re.findall(r"i", "hello world,i lo$ivi#e world")     # 这里是'i '和'i#'满足,所以结果是['i', 'i']
    
    # 下面是后面接元字符变成普通字符
    ret = re.findall(".", "hello .world,i lo$ivi#e world")     # 这里就是匹配"."所以结果就是['.']
    # 那如果是要匹配普通字符呢,要用\\,为啥呢,因为本身正则表达式语法中我们需要匹配就需要变成\,然后在python语法中表达\需要转义,所以是\\
    ret = re.findall("\\", "hello world,i lo$ivi#e world")
    
    
    # 元字符"()",配出的内容就表示一个分组。从正则表达式的左边开始看,看到的第一个左括号“(”表示第一个分组,第二个表示第二个分组,依次类推,
    # 需要注意的是,有一个隐含的全局分组(就是0),就是整个正则表达式通过groups()来全部访问匹配的元组,也可以通过group()函数来按分组方式来访问
    ret = re.findall("(ld)+", "hello world,i love world")  # 匹配到了['ld', 'ld']
    print(ret)
    ret = re.search("(ld)+", "hello world,i love world")    # search默认找第一个,返回的是一个对象<_sre.SRE_Match object; span=(9, 11), match='ld'>
    ret = re.search("(?P<name>w{3}):(?P<age>d{1,3})", "my msg is liu:18")
    print(ret.group())      # 需要用group()取值,默认返回所有,也就是group(0),结果就是liu:18
    print(ret.group('name'))    # 只取name分组,结果就是liu
    print(ret.group('age'))     # 只取age分组,结果就是18
    
    # 元字符"|"    或。匹配|左右表达式任意一个,从左到右匹配,如果|没有包括在()中,则它的范围是整个正则表达式
    ret = re.findall("(2|ld)+", "hello2 world2,i love wo2rld")   # 匹配2或者ld多次,结果是['2', '2', '2', 'ld']
    ret = re.findall("2|ld", "hello2 world,i love world")   # 匹配2或者ld,结果是['2', 'ld', 'ld']
    print(ret)

      除了11个元字符,还有几种正则表达式的方法:

    # 1、findall()  所有结果都返回到一个列表里
    # 2、search()   返回匹配到的第一个对象,对象可以调用group()返回结果
    
    # 3、match()    从字符串开始进行匹配,只返回匹配到的第一个对象,对象可以调用group()返回结果
    ret = re.match("w+llw+", "hello world")     # 必须从开头开始写,用的不多
    print(ret.group())              # 返回结果就是hello
    
    # 4、split()   切片函数。使用指定的正则规则在目标字符串中查找匹配的字符串,用它们作为分界,把字符串切片,可切多次
    ret = re.split('[o,h]', "helloworld")   # 先用o去分,得到'hell'、'w'和'rld',然后再通过h去分别分这三个
    print(ret)  #   结果如下:['', 'ell', 'w', 'rld']
    
    # 5、sub()   对字符串的替换和修改
    ret = re.sub('l.{2}', 'sb...', 'hello world')
    print(ret)  # 返回hesb... world
    
    # 6、    将正则规则编译成一个 Pattern 对象,以供接下来使用.
    pattern_obj = re.compile('.com')
    ret = pattern_obj.findall("www.baidu.com")
    print(ret)      # 结果是['.com']

       最后来一个题目,计算 "2 - 4 * ( (-20-60 +(4-5*5/3 + 9 /-3*90.2/4*2558 +10 * 5601/10) +(-40/5) * (9-2*5/3 + 7 /3*99/4*29.8 +11 * 568/14 )) - (-4*3)/ (16-3*2) )",不能用eval。

    # -*- coding:utf-8 -*-
    """
    需求:用户输入 2 - 4 * ( (-20-60 +(4-5*5/3 + 9 /-3*90.2/4*2558 +10 * 5601/10) +(-40/5) * (9-2*5/3 + 7 /3*99/4*29.8 +11 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式后,
    必须自己解析里面的(),+,-,*,/符号和公式(不能调用eval等类似功能偷懒实现),运算后得出结果,结果必须与真实的计算器所得出的结果一致
    
    分析:
        1、首先我们应该先检查一下输入是否合法;
        2、然后需要整理一下用户的输入,去除多余空格,将类似+-,--等替换一下
        3、判断是否有括号,有括号就按照括号的计算,无则不需要找括号,直接按照乘除优先算
        4、按照我们手算的思路来说,先算最里面括号的,所以找出最里面的括号
        5、先算最里面括号的计算,括号内乘除优先,先找乘除法,从左往右,算完后替换回去再算加减
        6、注意计算之前要先格式化一下,整理一下类似+-,--等
    """
    import re
    s_name = "-2 - 4 * ( (-20-60 +(-4-5*5/3  -9 /-3*90.2/4*2558 +10 * -5601/10) +(-40/5) * (9-2*5/3 + 7 /3*99/4*29.8 +11 * 568/14 )) - (-4*3)/ (16-3*2) )"
    print(eval(s_name))         # 正确结果是-599895.3904761905
    def check_string(s_name):
        flag = True     # 判断计算表达式是否合法标志位
        ret1 = re.search("[^d+-*/ .()]+", s_name)     # 找一下看是否有字母及特殊字符
        ret2 = re.search("[+-]+[*/]+", s_name)         # 找一下看是否有+*、-/等不合法表达
        if ret1 != None or ret2 != None:
            flag = False
            print("Invalid Input")
        return flag
    
    # 定义一个整理字符串格式的函数
    def format_str(s_name):
        s_name = s_name.replace(' ','')     # 去除字符串的空格
        s_name = s_name.replace('++', '+')
        s_name = s_name.replace('--', '+')
        s_name = s_name.replace('+-', '-')
        s_name = s_name.replace('-+', '-')
        s_name = s_name.replace('*+', '*')
        s_name = s_name.replace('/+', '/')
        return s_name
    
    # 计算加减乘除
    def calc_apmd(s_name):
        s_name = s_name.strip('()')         # 去除两边括号
        while True:
            # 乘除法计算
            if "*" in s_name or "/" in s_name:
                s_name = format_str(s_name)
                search_str = re.search("d+.?d*[*/][-]?d+.?d*", s_name).group()  # 从左往右找出乘除法
                if "*" in search_str:  # 判断乘法或除法是不是在找到的第一个表达式里面
                    s1, s2 = search_str.split('*')  # 通过*或/分割
                    cal_result = float(s1) * float(s2)  # 计算乘法结果
                else:
                    s1, s2 = search_str.split("/")
                    cal_result = float(s1) / float(s2)
                s_name = s_name.replace(search_str, str(cal_result))  # 将结果替换回去
    
            # 下面是加减法计算
            elif "+" in s_name.strip("-+") or "-" in s_name.strip("-+"):  # 加减法跟乘除法类似
                s_name = format_str(s_name)
                search_str = re.search("[-]?d+.?d*[+-]d+.?d*", s_name).group()  # 从左往右找出加减法
                if "+" in search_str:
                    s1, s2 = search_str.split('+')
                    cal_result = float(s1) + float(s2)
                else:
                    # 判断第一位是否是负数
                    if search_str[0] == "-":
                        s1, s2 = search_str.strip("-").split("-")
                        cal_result = -float(s1) - float(s2)
                    else:
                        s1, s2 = search_str.split("-")
                        cal_result = float(s1) - float(s2)
                s_name = s_name.replace(search_str, str(cal_result))  # 将结果替换回去
            else:
                break
        return s_name
    
    if __name__ == "__main__":
        if check_string(s_name):
            s_name = format_str(s_name)      # 格式化整理字符串
            while True:
                if re.search("(", s_name):  # 先判断是否有括号
                    inner_string = re.search("([^()]+)", s_name) .group() # 找到最里面的括号
                    calc_reslut = calc_apmd(inner_string)         # 将值传入计算后返回
                    s_name = s_name.replace(inner_string, calc_reslut)  # 将计算后的结果替换
                elif re.search("d+.?d*[+-*/]d+.?d*", s_name):    # 如果没括号看下有没有加减乘除,有的话就计算,没有就跳出循环
                    calc_reslut = calc_apmd(s_name)  # 将值传入计算后返回
                    s_name = s_name.replace(s_name, calc_reslut)  # 将计算后的结果替换
                else:
                    break
        print(s_name)       # -599895.3904761905,与上面得到的结果
  • 相关阅读:
    C#下编程完成IIS网络App的权限设置
    IIS6与IIS7在编程实现HTTPS绑定时的细微差别
    Android 对话框(Dialog)大全
    Android 开发中使用Intent传递数据的方法
    设计模式--模版设计模式
    android 布局页面文件出错故障排除Exception raised during rendering: java.lang.System.arraycopy([CI[CII)V
    viewPager的切换动画
    设计模式--状态模式
    git学习
    二〇一五年五月二十二日--bug--启动页面出现模糊的问题
  • 原文地址:https://www.cnblogs.com/leixiaobai/p/9934469.html
Copyright © 2020-2023  润新知