一 time & datetime
在Python中,通常有如下几种方式来表示时间:
- 时间戳(timestamp): 通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量。我们运行"type(time.time())",返回的是float类型。
- 格式化的时间字符串(Format String)
- 结构化的时间(struct_time): struct_time元祖共有9个元素(年,月,日,时,分,秒,一年中第几周,一年中第几天,夏令时)
>>> import time#-------------------------以下分别是三种形式的时间-----------------------#>>> print(time.time()) #时间戳1509802396.6130548>>> print(time.strftime('%Y-%m-%d %X')) #格式化的时间字符串,默认按当前时间(time.localtime())进行格式化2017-11-04 21:34:03>>> print (time.strftime('%a %b %d %H:%M:%S %Y')) #格式化的时间字符串,同上一个Sat Nov 04 21:35:40 2017>>> print(time.localtime()) #本地时区的struct_timetime.struct_time(tm_year=2017, tm_mon=11, tm_mday=4, tm_hour=21, tm_min=34, tm_sec=40, tm_wday=5, tm_yday=308, tm_isdst=0)>>> print(time.gmtime()) #UTC时区的struct_timetime.struct_time(tm_year=2017, tm_mon=11, tm_mday=4, tm_hour=13, tm_min=34, tm_sec=57, tm_wday=5, tm_yday=308, tm_isdst=0)
%y 去掉世纪的年份表示(00 - 99)%Y 完整的年份表示(0000 - 9999)%m 月份(01 - 12)%d 一个月中的第几天(01 - 31)%H 24小时制小时数(00 - 23)%I 12小时制小时数(01 - 12)%M 分钟数(00 - 59)%S 秒(00 - 59)%a 本地(locale)简化星期名称%A 本地完整星期名称%b 本地简化的月份名称%B 本地完整的月份名称%c 本地相应的日期表示和时间表示%j 一年中的第几天(001 - 366)%p 本地A.M.或P.M.的等价符%U 一年中的星期数(00 - 53)星期天为星期的开始%w 一个星期中的第几天(0 - 6),星期天为星期的开始%W 和%U基本相同,不同的是%W是以星期一为一个星期的开始%x 本地相应的日期表示%X 本地相应的时间表示%Z 当前时区的名称(如果不存在为空字符)%% %号本身
其中计算机认识的时间只能是'时间戳'格式,而程序员可处理的或者说人类能看懂的时间有: '格式化的时间字符串','结构化的时间' ,于是有了下图的转换关系
>>> import time#-------------------------------------按图1转换时间------------------------------------------## localtime([secs]):将一个时间戳转换为当前时区(UTC+8)的struct_time。secs参数未提供,则以当前时间为准。>>> time.localtime()time.struct_time(tm_year=2017, tm_mon=11, tm_mday=4, tm_hour=21, tm_min=57, tm_sec=41, tm_wday=5, tm_yday=308, tm_isdst=0)>>> x = time.localtime(1509802396.6130548)>>> print(x)time.struct_time(tm_year=2017, tm_mon=11, tm_mday=4, tm_hour=21, tm_min=33, tm_sec=16, tm_wday=5, tm_yday=308, tm_isdst=0)>>> print(x.tm_year)2017>>> print(x.tm_yday)308# gmtime([secs]) 和localtime()方法类似,gmtime()方法是将一个时间戳转换为UTC时区(0时区)的struct_time。# mktime(t) : 将一个struct_time转化为时间戳。>>> print(time.mktime(time.localtime()))1509803970.0# strftime(format[, t]) : 把一个代表时间的元组或者struct_time(如由time.localtime()和time.gmtime()返回)转化为格式化的时间字符串。 如果t未指定,将传入time.localtime()。如果元组中任何一个元素越界,ValueError的错误将会被抛出。>>> print(time.strftime("%Y-%m-%d %X", time.localtime()))2017-11-04 22:00:32>>> print(time.strftime("%X %Y-%m-%d", time.localtime()))22:00:32 2017-11-04# time.strptime(string[, format]):把一个格式化时间字符串转化为struct_time。实际上它和strftime()是逆操作。>>> print(time.strptime('2017-11-04 22:00:32', '%Y-%m-%d %X'))time.struct_time(tm_year=2017, tm_mon=11, tm_mday=4, tm_hour=22, tm_min=0, tm_sec=32, tm_wday=5, tm_yday=308, tm_isdst=-1)#在这个函数中,format默认为:"%a %b %d %H:%M:%S %Y"。
>>> import time#将格式字符串转换为时间戳>>> string_to_struct = time.strptime("2017-10-30 14:48:44","%Y-%m-%d %H:%M:%S") #将 日期字符串 转成 struct时间对象格式>>> print(string_to_struct)time.struct_time(tm_year=2017, tm_mon=10, tm_mday=30, tm_hour=14, tm_min=48, tm_sec=44, tm_wday=0, tm_yday=303, tm_isdst=-1)>>> struct_to_stamp = time.mktime(string_to_struct) #将struct时间对象格式转成时间戳>>> print(struct_to_stamp)1509346124.0或者一步到位>>> print(time.mktime(time.strptime("2017-10-30 14:48:44","%Y-%m-%d %H:%M:%S")) )1509346124.0#将时间戳转换为格式字符串>>> print(time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(1509346124.0)) )2017-10-30 14:48:44
>>> import time#-----------------------------------------按图2转换时间-------------------------------------------## asctime([t]) : 把一个表示时间的元组或者struct_time表示为这种形式:'Sun Jun 20 23:21:05 1993'。如果没有参数,将会将time.localtime()作为参数传入。>>> print(time.asctime())Sat Nov 4 22:19:37 2017# ctime([secs]) : 把一个时间戳(按秒计算的浮点数)转化为time.asctime()的形式。如果参数未给或者为None的时候,将会默认time.time()为参数。 它的作用相当于time.asctime(time.localtime(secs))。>>> print(time.ctime())Sat Nov 4 22:19:56 2017>>> print(time.ctime(time.time()))Sat Nov 4 22:20:35 2017#---------------------------------------其它用法---------------------------## sleep(secs):线程推迟指定的时间运行,单位为秒。
#时间加减>>> import datetime>>> print(datetime.datetime.now()) #返回当前时间2017-11-04 22:28:16.976757>>> print(datetime.date.fromtimestamp(time.time()) ) # 时间戳直接转成日期格式2017-11-04>>> print(datetime.datetime.now() )2017-11-04 22:29:12.067908>>> print(datetime.datetime.now() + datetime.timedelta(3)) #当前时间+3天2017-11-07 22:29:24.544621>>> print(datetime.datetime.now() + datetime.timedelta(-3)) #当前时间-3天 2017-11-01 22:29:38.412414>>> print(datetime.datetime.now() + datetime.timedelta(hours=3)) #当前时间+3小时2017-11-05 01:29:50.667115>>> print(datetime.datetime.now() + datetime.timedelta(minutes=30)) #当前时间+30分2017-11-04 23:00:03.260836>>> c_time = datetime.datetime.now()>>> print(c_time.replace(minute=3,hour=2)) #时间替换2017-11-04 02:03:37.749808
二 random
随机数
>>> import random>>> print(random.random()) #(0,1)----float 大于0且小于1之间的小数。也可以指定区间,看下一个0.4389206383156363>>> print(random.uniform(1,3)) #大于1小于3的小数1.8623602068930234>>> print(random.randint(1,3)) #[1,3] 大于等于1且小于等于3之间的整数1>>> print(random.randrange(1,3)) #[1,3) 大于等于1且小于3之间的整数2>>> print(random.choice([1,'23',[4,5]])) #1或者23或者[4,5]23>>> print(random.sample([1,'23',[4,5]],2)) #列表元素任意2个组合['23', 1]>>> item=[1,3,5,7,9]>>> random.shuffle(item) #打乱item的顺序,相当于"洗牌">>> print(item)[7, 3, 1, 5, 9]
>>> import random>>> def make_code(n): res = '' for i in range(n): s1 = chr(random.randint(65,90)) s2 = str(random.randint(0,9)) res += random.choice([s1,s2]) return res>>> print(make_code(9))5Z126T88M
三 OS模块
OS模块是于操作系统交互的一个接口
import osos.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cdos.curdir 返回当前目录: ('.')os.pardir 获取当前目录的父目录字符串名:('..')os.makedirs('dirname1/dirname2') 可生成多层递归目录os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推os.mkdir('dirname') 生成单级目录;相当于shell中mkdir dirnameos.rmdir('dirname') 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirnameos.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印os.remove() 删除一个文件os.rename("oldname","newname") 重命名文件/目录os.stat('path/filename') 获取文件/目录信息os.sep 输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"os.linesep 输出当前平台使用的行终止符,win下为"\r\n",Linux下为"\n"os.pathsep 输出用于分割文件路径的字符串 win下为;,Linux下为:os.name 输出字符串指示当前使用平台。win->'nt'; Linux->'posix'os.system("bash command") 运行shell命令,直接显示os.environ 获取系统环境变量os.path.abspath(path) 返回path规范化的绝对路径os.path.split(path) 将path分割成目录和文件名二元组返回os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素os.path.basename(path) 返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素os.path.exists(path) 如果path存在,返回True;如果path不存在,返回Falseos.path.isabs(path) 如果path是绝对路径,返回Trueos.path.isfile(path) 如果path是一个存在的文件,返回True。否则返回Falseos.path.isdir(path) 如果path是一个存在的目录,则返回True。否则返回Falseos.path.join(path1[, path2[, ...]]) 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略os.path.getatime(path) 返回path所指向的文件或者目录的最后存取时间os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间os.path.getsize(path) 返回path的大小
更多
>>> import os# 在Linux和Mac平台上,该函数会原样返回path,在windows平台上会将路径中所有字符转换为小写,并将所有斜杠转换为饭斜杠。>>> os.path.normcase('c:/windows\\system32\\')'c:\\windows\\system32\\'# 规范化路径,如..和/>>> os.path.normpath('c://windows\\System32\\../Temp/')'c:\\windows\\Temp'>>> a='/Users/jieli/test1/\\\a1/\\\\aa.py/../..'>>> print(os.path.normpath(a))\Users\jieli\test1
os路径处理#方式一:推荐使用import os,sys>>> possible_topdir = os.path.normpath(os.path.join( os.path.abspath(os.__file__), os.pardir, os.pardir, os.pardir))>>> sys.path.insert(0,possible_topdir)#方式二:不推荐使用import os>>> os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(os.__file__))))'C:\\Users\\Administrator\\AppData\\Local\\Programs\\Python'
四 sys模块
1 sys.argv # 命令行参数List,第一个元素是程序本身路径2 sys.exit(n) # 退出程序,正常退出时exit(0)3 sys.version # 获取Python解释程序的版本信息4 sys.maxint # 最大的Int值5 sys.path # 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值6 sys.platform # 返回操作系统平台名称7 sys.stdout.write('please:')8 val = sys.stdin.readline()[:-1]
#=========知识储备==========#进度条的效果[# ][## ][### ][#### ]#指定宽度print('[%-15s]' %'#')print('[%-15s]' %'##')print('[%-15s]' %'###')print('[%-15s]' %'####')#打印%print('%s%%' %(100)) #第二个%号代表取消第一个%的特殊意义#可传参来控制宽度print('[%%-%ds]' %50) #[%-50s]print(('[%%-%ds]' %50) %'#')print(('[%%-%ds]' %50) %'##')print(('[%%-%ds]' %50) %'###')#=========实现打印进度条函数==========import sysimport timedef progress(percent,width=50): if percent >= 1: percent=1 show_str=('[%%-%ds]' %width) %(int(width*percent)*'#') print('\r%s %d%%' %(show_str,int(100*percent)),file=sys.stdout,flush=True,end='')#=========应用==========data_size=1025recv_size=0while recv_size < data_size: time.sleep(0.1) #模拟数据的传输延迟 recv_size+=1024 #每次收1024 percent=recv_size/data_size #接收的比例 progress(percent,width=70) #进度条的宽度70
五 shutil模块
高级的文件、文件夹、压缩包处理模块。包括复制、移动、重命名、删除等等。
shutil.copyfileobj(fsrc, fdst[, length])
将文件内容拷贝到另一个文件中
>>> import shutil>>> shutil.copyfileobj(open('old.xml','r'), open('new.xml', 'w'))
shutil.copyfile(src, dst)
拷贝文件
>>> shutil.copyfile('f1.log', 'f2.log') #目标文件无需存在,一般不用
shutil.copymode(src, dst)
仅拷贝权限。内容、组、用户均不变。
>>> shutil.copymode('f1.log', 'f2.log') #目标文件必须存在,一般不用
shutil.copystat(src, dst)
仅拷贝状态的信息,包括:mode bits, atime, mtime, flags>>> shutil.copystat('f1.log', 'f2.log') #目标文件必须存在,一般不用
shutil.copy(src, dst)
拷贝文件和权限>>> import shutil>>> shutil.copy('f1.log', 'f2.log')
shutil.copy2(src, dst)
拷贝文件和状态信息>>> import shutil>>> shutil.copy2('f1.log', 'f2.log')
shutil.ignore_patterns(*patterns)
shutil.copytree(src, dst, symlinks=False, ignore=None)递归的去拷贝文件夹>>> import shutil>>> shutil.copytree('folder1', 'folder2', ignore=shutil.ignore_patterns('*.pyc', 'tmp*')) #目标目录不能存在, 注意对folder2目录父级目录要有可写权限,ignore的意思是排除
>>> import shutil>>> shutil.copytree('f1', 'f2', symlinks=True, ignore=shutil.ignore_patterns('*.pyc', 'tmp*'))'''通常的拷贝都把软连接拷贝成硬链接,即对待软连接来说,创建新的文件'''
shutil.rmtree(path[, ignore_errors[, onerror]])
递归的去删除文件>>> import shutil>>> shutil.rmtree('folder1')
shutil.move(src, dst)
递归的去移动文件,它类似mv命令,其实就是重命名。>>> import shutil>>> shutil.move('folder1', 'folder3')
shutil.make_archive(base_name,format,...)
创建压缩包并返回文件路径,例如:zip、tar
- base_name: 压缩包的文件名,也可以是压缩包的路径。只是文件名时,则保存至当前目录,否则保存至指定路径,
如:data_bak =>保存至当前路径
如:/tmp/data_bak =>保存至/tmp/test/- format: 压缩包种类,“zip”, “tar”, “bztar”,“gztar”
- root_dir: 要压缩的文件夹路径(默认当前目录)
- owner: 用户,默认当前用户
- group: 组,默认当前组
- logger: 用于记录日志,通常是logging.Logger对象
#将 /data 下的文件打包放置当前程序目录>>> import shutil>>> shutil.make_archive('data_bak','tar',root_dir='/data')'/root/data_bak.tar'#将 /data下的文件打包放置 /tmp/ 目录>>> shutil.make_archive('/tmp/data_bak','tar',root_dir='/data')'/tmp/data_bak.tar'
shutil 对压缩包的处理是调用 ZipFile 和 TarFile 两个模块来进行的,详细:
import zipfile# 压缩z = zipfile.ZipFile('laxi.zip', 'w')z.write('a.log')z.write('data.data')z.close()# 解压z = zipfile.ZipFile('laxi.zip', 'r')z.extractall(path='.')z.close()
import tarfile# 压缩>>> t=tarfile.open('/tmp/egon.tar','w')>>> t.add('/test1/a.py',arcname='a.bak')>>> t.add('/test1/b.py',arcname='b.bak')>>> t.close()# 解压>>> t=tarfile.open('/tmp/egon.tar','r')>>> t.extractall('/egon')>>> t.close()
六 json & pickle模块
用于序列化的两个模块
- json,用于字符串和python数据类型间进行转换
- pickle,用于python特有的类型和python的数据类型间进行转换
json模块提供了四个功能:dumps、dump、loads、load
pickle模块提供了四个功能:dumps、dump、loads、load
>>> import pickle >>> data = { 'k1':123,'k2':'Hello'} # pickle.dumps 将数据通过特殊的形式装换为只有python语言认识的字符串>>> p_str = pickle.dumps(data)>>> print(p_str)b'\x80\x03}q\x00(X\x02\x00\x00\x00k1q\x01K{X\x02\x00\x00\x00k2q\x02X\x05\x00\x00\x00Helloq\x03u.' # pickle.dump 将数据通过特殊的形式装换为只有python语言认识的字符串,并写入文件>>> with open(r'D:\result.pk','wb') as fp: pickle.dump(data,fp) #读取pickle文件>>> with open(r'D:\result.pk','rb') as fp: pickle.load(fp) { 'k2': 'Hello', 'k1': 123} >>> import json# json.dumps 将数据通过特殊的形式转换为所有程序语言都认识的字符串>>> j_str = json.dumps(data)>>> print(j_str){ "k2": "Hello", "k1": 123} # json.dumps 将数据通过特殊的形式转换为所有程序语言都认识的字符串,并写入文件>>> with open(r'D:\result.json','w') as fp: json.dump(data,fp) #读取json文件 >>> with open(r'D:\result.json','r') as fp: json.load(fp) { 'k2': 'Hello', 'k1': 123}
七 shelve模块
shelve模块是一个简单的k,v将内存数据通过文件持久化的模块,可以持久化任何pickle可支持的python数据格式,并且可以多次 dump 和 load
>>> import shelve>>> import datetime #持久化数据>>> info = { 'name': 'bigberg', 'age': 22}>>> name = ['Apoll', 'Zous', 'Luna']>>> t = datetime.datetime.now()>>> with shelve.open('/tmp/shelve.txt') as f: #执行代码后会生成3个文件:shelve.txt.bak、shelve.txt.dat、shelve.txt.dir。... f['name'] = name #持久化列表... f['info'] = info #持久化字典... f['time'] = t #持久化时间类型... #读取数据>>> with shelve.open('shelve.txt') as f: #打开文件... print(f.get('name')) #使用get获取数据... print(f.get('info'))... print(f.get('time'))...['Apoll', 'Zous', 'Luna']{ 'name': 'bigberg', 'age': 22}2017-11-01 16:07:29.261414
八 xml模块
xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单,不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀,至今很多传统公司如金融行业的很多系统的接口还主要是xml。
xml的格式如下,就是通过<>节点来区别数据结构的:
2 2008 141100 5 2011 59900 69 2011 13600
xml协议在各个语言里的都 是支持的,在python中可以用以下模块操作xml:
print(root.iter('year')) #全文搜索print(root.find('country')) #在root的子节点找,只找一个print(root.findall('country')) #在root的子节点找,找所有
import xml.etree.ElementTree as ET tree = ET.parse("xmltest.xml")root = tree.getroot()print(root.tag) #遍历xml文档for child in root: print('========>',child.tag,child.attrib,child.attrib['name']) for i in child: print(i.tag,i.attrib,i.text) #只遍历year 节点for node in root.iter('year'): print(node.tag,node.text)#---------------------------------------import xml.etree.ElementTree as ET tree = ET.parse("xmltest.xml")root = tree.getroot() #修改for node in root.iter('year'): new_year=int(node.text)+1 node.text=str(new_year) node.set('updated','yes') node.set('version','1.0')tree.write('test.xml') #删除nodefor country in root.findall('country'): rank = int(country.find('rank').text) if rank > 50: root.remove(country) tree.write('output.xml')
#在country内添加(append)节点year2import xml.etree.ElementTree as ETtree = ET.parse("a.xml")root=tree.getroot()for country in root.findall('country'): for year in country.findall('year'): if int(year.text) > 2000: year2=ET.Element('year2') year2.text='新年' year2.attrib={ 'update':'yes'} country.append(year2) #往country节点下添加子节点tree.write('a.xml.swap')
自己创建xml文档:
import xml.etree.ElementTree as ET new_xml = ET.Element("namelist")name = ET.SubElement(new_xml,"name",attrib={ "enrolled":"yes"})age = ET.SubElement(name,"age",attrib={ "checked":"no"})sex = ET.SubElement(name,"sex")sex.text = '33'name2 = ET.SubElement(new_xml,"name",attrib={ "enrolled":"no"})age = ET.SubElement(name2,"age")age.text = '19' et = ET.ElementTree(new_xml) #生成文档对象et.write("test.xml", encoding="utf-8",xml_declaration=True) ET.dump(new_xml) #打印生成的格式
九 configparser模块
用于生成和修改常见配置文档。
一个典型的Mysql配置文件如下:
[mysqld]port = 3306socket = /var/lib/mysql/mysql.sockkey_buffer = 384Mmyisam_sort_buffer_size = 64Mthread_cache_size = 8query_cache_size = 32Mmax_connections=500thread_concurrency = 8[client]user = rootpassword = 123456port = 3306socket = /var/lib/mysql/mysql.sock
读取
>>> import configparser>>> config=configparser.ConfigParser()>>> config.read('my.cnf')['my.cnf']#查看所有的标题>>> res=config.sections()>>> print(res)['mysqld', 'client']#判断是否存在某个标题>>> 'mysqld' in configTrue>>> print(config.has_section('mysqld')) #同上一个True#判断标题mysqld下是否有user>>> print(config.has_option('mysqld','user'))False#查看标题client下所有key=value的key>>> options=config.options('client')>>> print(options)['user', 'password', 'port', 'socket']>>> for key in config['client']:print(key) #同上一个... userpasswordportsocket#查看标题client下所有key=value的(key,value)格式>>> item_list=config.items('client')>>> print(item_list)[('user', 'root'), ('password', '123456'), ('port', '3306'), ('socket', '/var/lib/mysql/mysql.sock')]#查看标题client下user的值=>字符串格式>>> val=config.get('client','user')>>> print(val)root>>> config['client']['user'] #同上一个'root'#查看标题client下port的值=>整数格式>>> val1=config.getint('client','port')>>> print(val1)3306#查看标题mysqld下mysql_update的值=>布尔值格式val2=config.getboolean('mysqld','mysql_update')print(val2)#查看标题mysqld下max_connections的值=>浮点型格式val3=config.getfloat('mysqld','max_connections')print(val3)
改写
>>> import configparser>>> config=configparser.ConfigParser()>>> config.read('my.cnf',encoding='utf-8')['my.cnf']#删除整个标题client>>> config.remove_section('client')True删除标题mysqld下的key_buffer和thread_cache_size>>> config.remove_option('mysqld','key_buffer')True>>> config.remove_option('mysqld','thread_cache_size')True#添加一个标题>>> config.add_section('mysql')#在标题mysqld下添加read_buffer_size=2M,thread_concurrency=8的配置>>> config.set('mysqld','read_buffer_size','2M')>>> config.set('mysqld','thread_concurrency','8')#最后将修改的内容写入文件,完成最终的修改>>> config.write(open('my.cnf','w'))
>>> import configparser>>> config = configparser.ConfigParser()>>> config["DEFAULT"] = { 'ServerAliveInterval': '45', ... 'Compression': 'yes',... 'CompressionLevel': '9'}>>> >>> config['bitbucket.org'] = {}>>> config['bitbucket.org']['User'] = 'hg'>>> config['topsecret.server.com'] = {}>>> topsecret = config['topsecret.server.com']>>> topsecret['Host Port'] = '50022' # mutates the parser>>> topsecret['ForwardX11'] = 'no' # same here>>> config['DEFAULT']['ForwardX11'] = 'yes'>>> with open('example.ini', 'w') as configfile:... config.write(configfile)...
十 hashlib模块
hash:一种算法 ,3.x里代替了md5模块和sha模块,主要提供 SHA1, SHA224, SHA256, SHA384, SHA512 ,MD5 算法
三个特点:
- 内容相同则hash运算结果相同,内容稍微改变则hash值则变
- 不可逆推
- 相同算法:无论校验多长的数据,得到的哈希值长度固定。
>>> import hashlib>>> m = hashlib.md5() # m = hashlib.sha256()>>> m.update('hello'.encode('utf-8'))>>> print(m.digest()) #2进制格式hashb']A@*\xbcK*v\xb9q\x9d\x91\x10\x17\xc5\x92'>>> print(m.hexdigest()) #16进制格式hash5d41402abc4b2a76b9719d911017c592>>> m.update('alvin'.encode('utf8'))>>> print(m.hexdigest())92a7e713c30abbb0319fa07da2a5c4af>>> m2=hashlib.md5()>>> m2.update('helloalvin'.encode('utf8'))>>> print(m2.hexdigest())92a7e713c30abbb0319fa07da2a5c4af'''注意:把一段很长的数据update多次,与一次update这段长数据,得到的结果一样,但是update多次为校验大文件提供了可能。'''
以上加密算法虽然依然非常厉害,但时候存在缺陷,即:通过撞库可以反解。所以,有必要对加密算法中添加自定义key再来做加密。
>>> import hashlib######## 256 #########>>> hash = hashlib.sha256('898oaFs09f'.encode('utf8'))>>> hash.update('alvin'.encode('utf8'))>>> print (hash.hexdigest())e79e68f070cdedcfe63eaf1a2e92c83b4cfb1b5c6bc452d214c1b7e77cdfd1c7
import hashlibpasswds=[ 'alex3714', 'alex1313', 'alex94139413', 'alex123456', '123456alex', 'a123lex', ]def make_passwd_dic(passwds): dic={} for passwd in passwds: m=hashlib.md5() m.update(passwd.encode('utf-8')) dic[passwd]=m.hexdigest() return dicdef break_code(cryptograph,passwd_dic): for k,v in passwd_dic.items(): if v == cryptograph: print('密码是===>\033[46m%s\033[0m' %k)cryptograph='aee949757a2e698417463d47acac93df'break_code(cryptograph,make_passwd_dic(passwds))
python 还有一个 hmac 模块,它内部对我们创建 key 和 内容 进行进一步的处理然后再加密
>>> import hmac>>> h = hmac.new(b'898oaFs09f','alvin'.encode('utf8'))>>> h.update('hello'.encode('utf8'))>>> print (h.hexdigest())6b1bc37749ebe91b01e07685221d38ef
#要想保证hmac最终结果一致,必须保证:#1:hmac.new括号内指定的初始key一样#2:无论update多少次,校验的内容累加到一起是一样的内容>>> import hmac>>> h1=hmac.new(b'egon')>>> h1.update(b'hello')>>> h1.update(b'world')>>> print(h1.hexdigest())f1bf38d054691688f89dcd34ac3c27f2>>> h2=hmac.new(b'egon')>>> h2.update(b'helloworld')>>> print(h2.hexdigest())f1bf38d054691688f89dcd34ac3c27f2>>> h3=hmac.new(b'egonhelloworld')>>> print(h3.hexdigest())bcca84edd9eeb86f30539922b28f3981
十一 subprocess模块
常用subprocess方法示例
>>> import subprocess#执行命令,返回命令执行状态 , 0 or 非0>>> retcode = subprocess.call('ls -l',shell=True) #shell = True ,允许 shell 命令是字符串形式total 20-rw-r--r-- 1 root root 20480 Nov 1 14:13 tmp_file.tar>>> print(retcode)0>>> retcode = subprocess.call('aaa',shell=True) #如果输入一个错误的命令,Python解释器不会提示任何信息/bin/sh: aaa: command not found #这里是系统执行命令返回的系统报错>>> print(retcode)127#执行命令,如果命令结果为0,就正常返回,否则抛异常>>> subprocess.check_call('ls -l',shell=True)total 20-rw-r--r-- 1 root root 20480 Nov 1 14:13 tmp_file.tar0>>> subprocess.check_call('aaa',shell=True)/bin/sh: aaa: command not found #这里是系统执行命令返回的系统报错Traceback (most recent call last): #这里是Python解释器返回的报错 File "", line 1, in File "/usr/local/python3/lib/python3.6/subprocess.py", line 291, in check_call raise CalledProcessError(retcode, cmd)subprocess.CalledProcessError: Command 'aaa' returned non-zero exit status 127.#接收字符串格式命令,返回元组形式,第1个元素是执行状态,第2个是命令结果>>> subprocess.getstatusoutput('ls /bin/ls')(0, '/bin/ls')#接收字符串格式命令,并返回结果>>> subprocess.getoutput('ls /bin/ls')'/bin/ls'#执行命令,并返回结果,注意是返回结果,不是打印,下例结果返回给res>>> res=subprocess.check_output('ls -l',shell=True)>>> resb'total 20\n-rw-r--r-- 1 root root 20480 Nov 1 14:13 tmp_file.tar\n'##上面那些方法,底层都是封装的subprocess.Popenpoll()Check if child process has terminated. Returns returncodewait()Wait for child process to terminate. Returns returncode attribute.terminate() 杀掉所启动进程communicate() 等待任务结束stdin 标准输入stdout 标准输出stderr 标准错误pidThe process ID of the child process.#例子>>> p = subprocess.Popen('df -h | grep sdb1',stdin=subprocess.PIPE,stdout=subprocess.PIPE,shell=True)>>> p.stdout.read()b'/dev/sdb1 4.8G 20M 4.6G 1% /usr/local/mysql/data\n'
subprocess.run(...)
>>> subprocess.run('ls -l',shell=True)total 20-rw-r--r-- 1 root root 20480 Nov 1 14:13 tmp_file.tarCompletedProcess(args='ls -l', returncode=0)>>> subprocess.run('exit 1', shell=True, check=True)Traceback (most recent call last): File "", line 1, in File "/usr/local/python3/lib/python3.6/subprocess.py", line 418, in run output=stdout, stderr=stderr)subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1.>>> subprocess.run('ls -l /dev/null',shell=True,stdout=subprocess.PIPE)CompletedProcess(args='ls -l /dev/null', returncode=0, stdout=b'crw-rw-rw- 1 root root 1, 3 Nov 2 09:48 /dev/null\n')
调用subprocess.run(...)是推荐的常用方法,在大多数情况下能满足需求,但如果你可能需要进行一些复杂的与系统的交互的话,你还可以用subprocess.Popen(),语法如下:
>>> p = subprocess.Popen('ping -c5 127.0.0.1',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)>>> print(p.stdout.read())
可用参数:
- args:shell命令,可以是字符串或者序列类型(如:list,元组)
- bufsize:指定缓冲。0 无缓冲,1 行缓冲,其他 缓冲区大小,负值 系统缓冲
- stdin, stdout, stderr:分别表示程序的标准输入、输出、错误句柄
- preexec_fn:只在Unix平台下有效,用于指定一个可执行对象(callable object),它将在子进程运行之前被调用
- close_sfs:在windows平台下,如果close_fds被设置为True,则新创建的子进程将不会继承父进程的输入、输出、错误管道。
所以不能将close_fds设置为True同时重定向子进程的标准输入、输出与错误(stdin, stdout, stderr)。
- shell:同上
- cwd:用于设置子进程的当前目录
- env:用于指定子进程的环境变量。如果env = None,子进程的环境变量将从父进程中继承。
- universal_newlines:不同系统的换行符不同,True -> 同意使用 \n
- startupinfo与createionflags只在windows下有效
将被传递给底层的CreateProcess()函数,用于设置子进程的一些属性,如:主窗口的外观,进程的优先级等等
终端输入的命令分为两种:
- 输入即可得到输出,如:ifconfig
- 输入进行某环境,依赖再输入,如:python
需要交互的命令示例
十二 logging模块
很多程序都有记录日志的需求,并且日志中包含的信息即有正常的程序访问日志,还可能有错误、警告等信息输出。Python的logging模块提供了标准的日志接口,可以通过它存储各种格式的日志,logging的日志可以分为5个级别:
- DEBUG 最详细的日志信息,用于问题诊断
- INFO 信息详细程度仅次于DEBUG,通常只记录关键节点信息,用于确认一切都是按照我们预期的那样进行工作
- WARNING 当某些不期望的事情发生时记录的信息(如,磁盘可用空间较低),但是此时应用程序还是正常运行的
- ERROR 由于一个更严重的问题导致某些功能不能正常运行时记录的信息
- CRITICAL 当发生严重错误,导致应用程序不能继续运行时记录的信息
说明:
- 上面列表中的日志等级是从上到下依次升高的,即:DEBUG < INFO < WARNING < ERROR < CRITICAL,而日志的信息量是依次减少的;
- 当为某个应用程序指定一个日志级别后,应用程序会记录所有日志级别大于或等于指定日志级别的日志信息,而不是仅仅记录指定级别的日志信息,同样,logging模块也可以指定日志记录器的日志级别,只有级别大于或等于该指定日志级别的日志记录才会被输出,小于该等级的日志记录将会被丢弃。
最简单的日志输出,默认打印到终端
>>> import logging>>> logging.debug('This is a debug log.')>>> logging.info('This is a info log.')>>> logging.warning('This is a warning log.')WARNING:root:This is a warning log.>>> logging.error('This is a error log.')ERROR:root:This is a error log.>>> logging.critical('This is a critical log.')CRITICAL:root:This is a critical log.'''为什么前面两条日志没有被打印出来???这是因为logging模块提供的日志记录函数所使用的日志器设置的日志级别是WARNING,因此只有WARNING级别的日志记录以及大于它的ERROR和CRITICAL级别的日志记录被输出了,而小于它的DEBUG和INFO级别的日志记录被丢弃了。'''
把日志写到文件里
>>> import logging>>> logging.basicConfig(filename=r'd:\access.log', format="%(asctime)s [%(filename)s:%(lineno)d] %(levelname)s %(message)s", datefmt='%Y-%m-%d %H:%M:%S %p', level=logging.INFO)>>> logging.debug('调试debug')>>> logging.info('消息info')>>> logging.warning('警告warn')>>> logging.error('错误error')>>> logging.critical('严重critical')#access.log内容:2017-11-15 14:15:09 [:2] INFO 消息info2017-11-15 14:15:32 [ :2] WARNING 警告warn2017-11-15 14:16:15 [ :2] ERROR 错误error2017-11-15 14:16:55 [ :2] CRITICAL 严重critical
可在logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有:
- filename:用指定的文件名创建FiledHandler(后边会具体讲解handler的概念),这样日志会被存储在指定的文件中。
- filemode:文件打开方式,在指定了filename时使用这个参数,默认值为“a”还可指定为“w”。
- format:指定handler使用的日志显示格式。
- datefmt:指定日期时间格式。
- level:设置rootlogger(后边会讲解具体概念)的日志级别
- stream:用指定的stream创建StreamHandler。可以指定输出到sys.stderr,sys.stdout或者文件,默认为sys.stderr。若同时列出了filename和stream两个参数,则stream参数会被忽略。
format参数中可能用到的格式化串
%(asctime)s 日志事件发生的时间--人类可读时间,默认格式是 “2003-07-08 16:49:45,896”。逗号后面的是毫秒%(created)f 日志事件发生的时间--时间戳,就是当时调用time.time()函数返回的值%(relativeCreated)d 日志事件发生的时间相对于logging模块加载时间的相对毫秒数(一般不用)%(msecs)d 日志事件发生事件的毫秒部分%(levelname)s 该日志记录的文字形式的日志级别('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')%(levelno)s 该日志记录的数字形式的日志级别(10, 20, 30, 40, 50)%(name)s 所使用的日志器名称,默认是'root',因为默认使用的是 rootLogger%(message)s 用户输出的消息%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有%(filename)s 调用日志输出函数的模块的文件名,包含文件后缀%(module)s 调用日志输出函数的模块名,不包含后缀%(lineno)d 调用日志输出函数的语句所在的代码行%(funcName)s 调用日志输出函数的函数名%(process)d 进程ID。可能没有%(processName)s 进程名称,Python 3.1新增%(thread)d 线程ID。可能没有%(threadName)s 线程名。可能没有
如果想同时把LOG打印到屏幕和文件日志里,可以使用logging模块提供的四个主要类来实现:
- logger 提供了应用程序可以直接使用的接口;
- handler 将(logger创建的)日志记录发送到合适的目的输出;
- filter 提供了细度设备来决定输出哪条日志记录;
- formatter 决定日志记录的最终输出格式。
Logger类每个程序在输出信息之前都要获得一个Logger。Logger通常对应了程序的模块名,比如聊天工具的图形界面模块可以这样获得它的Logger:LOG=logging.getLogger(”chat.gui”)而核心模块可以这样:LOG=logging.getLogger(”chat.kernel”)Logger.setLevel(lel):指定最低的日志级别,低于lel的级别将被忽略。debug是最低的内置级别,critical为最高Logger.addFilter(filt)、Logger.removeFilter(filt):添加或删除指定的filterLogger.addHandler(hdlr)、Logger.removeHandler(hdlr):增加或删除指定的handlerLogger.debug()、Logger.info()、Logger.warning()、Logger.error()、Logger.critical():可以设置的日志级别Handler类handler对象负责发送相关的信息到指定目的地。Python的日志系统有多种Handler可以使用。有些Handler可以把信息输出到控制台,有些Logger可以把信息输出到文件,还有些 Handler可以把信息发送到网络上。如果觉得不够用,还可以编写自己的Handler。可以通过addHandler()方法添加多个多handlerHandler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略Handler.setFormatter():给这个handler选择一个格式Handler.addFilter(filt)、Handler.removeFilter(filt):新增或删除一个filter对象每个Logger可以附加多个Handler。接下来我们就来介绍一些常用的Handler:1) logging.StreamHandler使用这个Handler可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息。它的构造函数是:StreamHandler([strm])其中strm参数是一个文件对象。默认是sys.stderr2) logging.FileHandler和StreamHandler类似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件。它的构造函数是:FileHandler(filename[,mode])filename是文件名,必须指定一个文件名。mode是文件的打开方式。参见Python内置函数open()的用法。默认是’a',即添加到文件末尾。3) logging.handlers.RotatingFileHandler这个Handler类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建 一个新的同名日志文件继续输出。比如日志文件是chat.log。当chat.log达到指定的大小之后,RotatingFileHandler自动把 文件改名为chat.log.1。不过,如果chat.log.1已经存在,会先把chat.log.1重命名为chat.log.2。。。最后重新创建 chat.log,继续输出日志信息。它的构造函数是:RotatingFileHandler( filename[, mode[, maxBytes[, backupCount]]])其中filename和mode两个参数和FileHandler一样。maxBytes用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生。backupCount用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除。4) logging.handlers.TimedRotatingFileHandler这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就 自动创建新的日志文件。重命名的过程与RotatingFileHandler类似,不过新的文件不是附加数字,而是当前时间。它的构造函数是:TimedRotatingFileHandler( filename [,when [,interval [,backupCount]]])其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义。interval是时间间隔。when参数是一个字符串。表示时间间隔的单位,不区分大小写。它有以下取值:S 秒M 分H 小时D 天W 每星期(interval==0时代表星期一)midnight 每天凌晨5) logging.handlers.HTTPHandler将日志消息以GET或POST的方式发送给一个HTTP服务器6) logging.handlers.SMTPHandler将日志消息发送给一个指定的email地址7) logging.NullHandler该Handler实例会忽略error messages,通常被想使用logging的library开发者使用来避免'No handlers could be found for logger XXX'信息的出现。Filter类Filter可以被Handler和Logger用来做比level更细粒度的、更复杂的过滤功能。Filter是一个过滤器基类,它只允许某个logger层级下的日志事件通过过滤。该类定义如下:class logging.Filter(name='') filter(record)比如,一个filter实例化时传递的name参数值为'A.B',那么该filter实例将只允许名称为类似如下规则的loggers产生的日志记录通过过滤:'A.B','A.B,C','A.B.C.D','A.B.D',而名称为'A.BB', 'B.A.B'的loggers产生的日志则会被过滤掉。如果name的值为空字符串,则允许所有的日志事件通过过滤。filter方法用于具体控制传递的record记录是否能通过过滤,如果该方法返回值为0表示不能通过过滤,返回值为非0表示可以通过过滤。Formater类Formater对象用于配置日志信息的最终顺序、结构和内容。与logging.Handler基类不同的是,应用代码可以直接实例化Formatter类。另外,如果你的应用程序需要一些特殊的处理行为,也可以实现一个Formatter的子类来完成。Formatter类的构造方法定义如下:logging.Formatter.__init__(fmt=None, datefmt=None, style='%')
logging模块就是通过这些组件来完成日志处理的,上面所使用的logging模块级别的函数也是通过这些组件对应的类来实现的
这些组件之间的关系描述:
- 日志器(logger)需要通过处理器(handler)将日志信息输出到目标位置,如:文件、sys.stdout、网络等;
- 不同的处理器(handler)可以将日志输出到不同的位置;
- 日志器(logger)可以设置多个处理器(handler)将同一条日志记录输出到不同的位置;
- 每个处理器(handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志;
- 每个处理器(handler)都可以设置自己的格式器(formatter)实现同一条日志以不同的格式输出到不同的地方。
简单点说就是:日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还可以通过过滤器(filter)和格式器(formatter)对要输出的日志内容做过滤和格式化等处理操作。
>>> import logging>>> from logging.handlers import RotatingFileHandler# 创建一个日志器logger并设置其日志级别为WARNING>>> logger = logging.getLogger('LOG')>>> logger.setLevel(logging.WARNING)# 创建一个流处理器ch_handler并设置其日志级别为DEBUG,还创建一个格式器>>> ch_handler = logging.StreamHandler()>>> ch_handler.setLevel(logging.DEBUG)>>> ch_formatter = logging.Formatter("%(asctime)s [%(filename)s:%(lineno)d] %(levelname)s %(message)s", datefmt='%Y-%m-%d %H:%M:%S %p')# 创建一个文件处理器fh_handler并设置其日志级别为INFO,还创建一个格式器>>> fh_handler = RotatingFileHandler(r'd:\log_test.log',maxBytes=20*1024*1024,backupCount=10)>>> fh_handler.setLevel(logging.INFO)>>> fh_formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")# 将流格式器添加到处理器ch_handler>>> ch_handler.setFormatter(ch_formatter)# 将文件格式器添加到处理器fh_handler>>> fh_handler.setFormatter(fh_formatter)#添加ch_handler和fh_handler处理器到日志器logger>>> logger.addHandler(ch_handler)>>> logger.addHandler(fh_handler)#屏幕输出>>> logger.debug('debug message')>>> logger.info('info message')>>> logger.warn('warn message')2017-11-16 15:37:03 PM [:2] WARNING warn message>>> logger.error('error message')2017-11-16 15:37:20 PM [ :2] ERROR error message>>> logger.critical('critical message')2017-11-16 15:37:27 PM [ :2] CRITICAL critical message# log_test.log文件内容:2017-11-16 15:37:03,055 - LOG - WARNING - warn message2017-11-16 15:37:20,433 - LOG - ERROR - error message2017-11-16 15:37:27,371 - LOG - CRITICAL - critical message
十三 re模块
常用匹配模式(元字符)
'\w' 匹配字母数字及下划线'\W' 匹配非字母数字下划线'\s' 匹配任意空白字符,等价于 [\t\n\r\f]'\S' 匹配任意非空字符'\d' 匹配任意数字,等价于 [0-9]'\D' 匹配任意非数字'\A' 匹配字符串开始'\Z' 匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串'\z' 匹配字符串结束'\G' 匹配最后匹配完成的位置'\n' 匹配一个换行符'\t' 匹配一个制表符'^' 匹配字符串的开头'$' 匹配字符串的末尾'.' 默认匹配除换行符(\n)以外的任意字符,若指定flag DOTALL,则可以匹配包括换行符的任意字符'[...]' 用来表示一组字符,单独列出:[amk]匹配'a','m'或'k''[^...]' 不在[]中的字符:[^abc]匹配除了a,b,c之外的字符。'*' 匹配前一个字符或表达式0次或多次'+' 匹配前一个字符或表达式1次或多次'?' 匹配前一个字符或表达式1次或0次,非贪婪方式'{n}' 精确匹配前一个字符或表达式n次'{n,m}' 匹配前一个字符或表达式n次到m次,贪婪模式'a|b' 匹配a或者b'()' 匹配括号内的表达式,也表示一个
re.search()
扫描整个字符串,只到找到第一个匹配然后返回一个包含匹配信息的对象,该对象可以通过调用group()方法得到匹配的字符串,如果字符串没有匹配,则返回None。
语法格式:
re.search(pattern,string,flags=0)
>>> import re #常规匹配>>> result = re.search('he','hi! hello world,good')>>> print(result)<_sre.SRE_Match object at 0x020E6058>>>> print(result.group()) #获取匹配的结果he>>> print(result.span()) #获取匹配字符串的长度范围(4, 6) # ()匹配目标>>> import re >>> result = re.search('\w+\s(\w*)','hi! hello world,good') #为了匹配字符串中具体的目标,则需要通过()括起来>>> result.group()'hello world'>>> result.group(1) #获取第一个括号中匹配的结果'world'>>> result.groups() #返回的是一个元组形式('world',) # .*贪婪匹配>>> import re >>> result = re.search('he.*(\d+).*','hi! hello 123456 world')>>> result.group(1)'6' '''从结果中可以看出只匹配到了6,并没有匹配到123456,出现这种情况的原因是前面的.* 给匹配掉了, .*在这里会尽可能的匹配多的内容,也就是我们所说的贪婪匹配'''#如果想要匹配到123456,可以把正则表达式改为:>>> result = re.search('he.*?(\d+).*','hi! hello 123456 world') # re.S匹配模式>>> content = """hi! hello 123456 worldmy name is tbb""">>> result = re.search('he.*?(\d+).*?tbb$',content,re.S) #用匹配模式re.S来匹配换行的内容>>> result.group()'hello 123456 world\nmy name is tbb'>>> print(result.group())hello 123456 worldmy name is tbb>>> print(result.group(1))123456 # \转义>>> import re >>> content = 'hello! price is $5.00'>>> result = re.search('price is \$5\.00',content)>>> print(result.group())price is $5.00 #如果要匹配\,可以使用如下方法:>>> re.search(r'a\\c','a\c').group() #r代表告诉解释器使用rawstring,即原生字符串,把我们正则内的所有符号都当普通字符处理,不要转义'a\\c'或>>> re.search('a\\\\c','a\c').group()'a\\c' '''当我们要匹配的内容中存在特殊字符的时候,就需要用到转移符号\'''
re.match()
尝试从字符串的起始位置匹配一个模式,如果不是起始位置匹配的话,match()就会返回None
同search,不过在字符串开始处进行匹配,完全可以用search+^代替match,如下所示:
>>> result = re.match('world','hello world')>>> print(result)None>>> result = re.search('world','hello world')>>> print(result)<_sre.SRE_Match object; span=(6, 11), match='world'>>>> result = re.search('^world','hello world')>>> print(result)None
re.findall()
搜索字符串,以列表的形式返回全部能匹配的子串
>>> re.findall('a[+*(]b','a*b a+b a-b a=b')['a*b', 'a+b']
re.split()
>>> re.split('[ab]','abcd') #先按'a'分割得到''和'bcd',再对''和'bcd'分别按'b'分割['', '', 'cd']
re.sub()
替换字符串中每一个匹配的子串后返回替换后的字符串
re.sub(正则表达式,替换成的字符串,原字符串)
>>> re.sub('l','L','hello world') #不指定n,默认替换所有'heLLo worLd'>>> re.sub('l','L','hello world',1)'heLlo world'>>> re.sub('l','L','hello world',2)'heLLo world'>>> re.sub('(\d+)',r'\1 7890','hello 123456 world') #获取匹配的字符串后面添加些内容,\1是获取第一个匹配的结果。为了防止转义字符的问题,需要在前面加上r'hello 123456 7890 world'>>> re.sub('(\w*)(\s*)(\d+)(\s*)(\w*)',r'\5\4\3\2\1','hello 123456 world') #把匹配到的结果按照顺序对原有字符串进行组合,返回新的字符串'world 123456 hello'>>> re.subn('l','L','hello world') #结果带有总共替换的个数('heLLo worLd', 3)
re.compile()
将正则表达式编译成正则表达式对象,方便复用该正则表达式
>>> content = 'hello 123456 world'>>> obj = re.compile('\d{3}')>>> obj.search(content).group()'123'>>> obj.findall(content) #重用了obj['123', '456']
对上面的一个小结:
尽量使用泛匹配,使用括号得到匹配目标,尽量使用非贪婪模式,有换行符就用re.S强调re.match是从字符串的起始位置匹配一个模式