时间模块 time datatime
time.clock(2.7) time.process_time(3.3) |
测量处理器运算时间,不包括sleep时间 |
|
time.altzone |
返回与UTC时间的时间差,以秒计算 |
print(time.altzone) -32400 |
time.asctime() |
将struct时间格式转为可读的时间格式"Fri Aug 19 11:14:16 2016" |
print(time.asctime()) 输出: Mon Jan 2 15:59:48 2017 |
time.localtime() |
输入时间戳,转为本地时区的stuct时间格式 |
stuct时间可以被调用 t = time.localtime() print(t.tm_year) 输出: 2017 |
time.gmtime(time.time()-800000) |
输入时间戳,转为UTC时间的stuct时间格式
|
print(time.gmtime(time.time())) |
time.ctime() |
返回 "Fri Aug 19 11:14:16 2016" |
print((time.ctime())) |
日期字符串转时间戳
time.strptime("str","fomat") |
将字符串转为struct时间格式 |
s=time.strptime("2017-1-2","%Y-%m-%d") print(s)
time.struct_time(tm_year=2017, tm_mon=1, tm_mday=2, tm_hour=0, tm_min=0, tm_sec=0, tm_wday=0, tm_yday=2, tm_isdst=-1) |
time.mktime(struct) |
将struct时间格式转为时间戳 |
s=time.strptime("2017-1-2","%Y-%m-%d") # 将字符串转为struct时间 print(time.mktime(s))
输出
1483286400.0 |
time.strftime(format,struct) |
将struct时间格式转为字符串 |
t1=time.strftime("%Y-%m-%d%H:%M:%S.log",time.localtime(time.time())) print(t1)
2017-01-02 23:01:01.log |
time.time >> gmtime or localtime >> asctime
时间戳 时间戳转struct struct转可读
time.time >> gmtime or localtime >> strftime
时间戳 时间戳转struct struct转自定义可读
stf >> strptime >> mktime
字符串 字符串转struct struct转时间戳
struct_time时间格式
索引 |
属性 |
值 |
0 |
tm_year(年) |
比如2011 |
1 |
tm_mon(月) |
1 - 12 |
2 |
tm_mday(日) |
1 - 31 |
3 |
tm_hour(时) |
0 - 23 |
4 |
tm_min(分) |
0 - 59 |
5 |
tm_sec(秒) |
0 - 61 |
6 |
tm_wday(weekday) |
0 - 6(0表示周日) |
7 |
tm_yday(一年中的第几天) |
1 - 366 |
8 |
tm_isdst(是否是夏令时) |
默认为-1 |
源文档
格式 |
含义 |
备注 |
%a |
本地(locale)简化星期名称 |
|
%A |
本地完整星期名称 |
|
%b |
本地简化月份名称 |
|
%B |
本地完整月份名称 |
|
%c |
本地相应的日期和时间表示 |
|
%d |
一个月中的第几天(01 - 31) |
|
%H |
一天中的第几个小时(24小时制,00 - 23) |
|
%I |
第几个小时(12小时制,01 - 12) |
|
%j |
一年中的第几天(001 - 366) |
|
%m |
月份(01 - 12) |
|
%M |
分钟数(00 - 59) |
|
%p |
本地am或者pm的相应符 |
一 |
%S |
秒(01 - 61) |
二 |
%U |
一年中的星期数。(00 - 53星期天是一个星期的开始。)第一个星期天之前的所有天数都放在第0周。 |
三 |
%w |
一个星期中的第几天(0 - 6,0是星期天) |
三 |
%W |
和%U基本相同,不同的是%W以星期一为一个星期的开始。 |
|
%x |
本地相应日期 |
|
%X |
本地相应时间 |
|
%y |
去掉世纪的年份(00 - 99) |
|
%Y |
完整的年份 |
|
%Z |
时区的名字(如果不存在为空字符) |
|
%% |
‘%’字符 |
|
源文档
datetime
datetime.datetime.now() |
打印当前本地时间 |
t2=datetime.datetime.now() print(t2)
2017-01-02 23:08:52.325153 |
|
datetime.datetime.fromtimestamp() |
时间戳转成日期格式,因此可以在里面对时间戳进行时间运算 |
print(datetime.datetime.fromtimestamp(time.time())) 输出
2017-01-03 22:23:48.811628 |
|
datetime.timedelta() |
用来时间运算,括号里面可以指定多种参数
days=0,seconds=0,microseconds=0, milliseconds=0,minutes=0,hours=0,weeks=0
|
print(datetime.datetime.now()) print(datetime.datetime.now()+datetime.timedelta(days=10))
2017-01-03 22:29:24.129807 2017-01-13 22:29:24.129807
|
|
时间.replace() |
直接替换时间中的年月日等信息
year=None,month=None,day=None,hour=None, minute=None,second=None,microsecond=None,tzinfo=True
|
now=datetime.datetime.now() print(now) print(now.replace(year=2018,month=6,day=8))
2017-01-03 22:35:19.673142 2018-06-08 22:35:19.673142
|
|
随机数 模块 random
random.random() |
随机打印一个小数 |
|
random.randint(1,2) |
随机生成从1到2的数,即大于等于1小于等于2 |
|
random.randrange(1,10) |
随机生成从1到9的数,即不包含大于等于1小于10 |
|
random.sample(列表或字符串,数量) |
根据列表或字符串中的元素随机选择指定的数量,随机验证码可以使用这个方法。前面的取值范围可以使用range(100)取100以内的数字 |
|
String模块
string.ascii_lowercase |
打印全部ascii编码中的小写字母 |
print(string.ascii_lowercase) 输出 |
string.ascii_uppercase |
打印全部大写字母 |
|
string.ascii_letters |
打印全部大小写字母 |
print(string.ascii_letters) |
string.digits/hexdigits/octidgits |
打印全部十进制,十六进制,八进制数 |
print(string.digits) print(string.hexdigits) print(string.octdigits)
输出
0123456789 0123456789abcdefABCDEF 01234567
|
随机验证码
str_source = string.ascii_letters +string.digits
print("".join(random.sample(str_source,4)))
高级随机验证码
li = []
for i in range(6):
r = random.randrange(0,6)
if r == 3 or r == 2:
tem = random.randrange(0,10)
li.append(str(tem)) #因为在向列表中添加元素时,需要使用字符串类型,因此ASCII中0-10是数字因此需要将其转为字符串类型
else:
tem = random.randrange(65,91)
c = chr(tem)
li.append(c)
result = "".join(li) #将生成的列表形成字符串
print(result)
OS模块
提供对操作系统进行调用的接口
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径 os.chdir("dirname") 改变当前脚本工作目录;相当于shell下cd os.curdir 返回当前目录: ('.') os.pardir 获取当前目录的父目录字符串名:('..') os.makedirs('dirname1/dirname2') 可生成多层递归目录 os.removedirs('dirname1') 若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推 os.mkdir('dirname') 生成单级目录;相当于shell中mkdir dirname os.rmdir('dirname') 删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname os.listdir('dirname') 列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印 os.remove() 删除一个文件 os.rename("oldname","newname") 重命名文件/目录 os.stat('path/filename') 获取文件/目录信息 os.sep 输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/" os.linesep 输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n" os.pathsep 输出用于分割文件路径的字符串 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不存在,返回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[, ...]]) 将多个路径组合后返回,第一个绝对路径之前的参数将被忽略 os.path.getatime(path) 返回path所指向的文件或者目录的最后存取时间 os.path.getmtime(path) 返回path所指向的文件或者目录的最后修改时间 |
更多猛击这里
sys模块
?
1 2 3 4 5 6 7 8 |
sys.argv 命令行参数List,第一个元素是程序本身路径 sys.exit(n) 退出程序,正常退出时exit(0) sys.version 获取Python解释程序的版本信息 sys.maxint 最大的Int值 sys.path 返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值 sys.platform 返回操作系统 sys.stdout.write('please:') val = sys.stdin.readline()[:-1] |
台名称 |
源文档
shutil模块,路径字符前面 应加r,这样路径中出现的\无需被转译
shutil.copyfileobj(原文件,目标文件) |
源文件的内容复制到目标文件中,但是需要实现按照需要打开对应的文件 |
with open("123.log","r") as file1,open("456.log","w") as file2: shutil.copyfileobj(f1,f2) |
shutil.copy(src,des) |
将src文件复制到des路径下,同时拷贝文件和权限
如果des是一个目录,就会将src文件复制到这个路径下 如果des是一个文件,则会复制src并将src改名为des的文件名
注意:路径前需要加r,r"路径",复制过去时会连同源文件所有权限一同copy过去
实际上copy方法内部执行了copyfile和copymod两种方法。copyfile用来复制文件,copymod用来复制权限
|
shutil.copy(r"D:\MyPython\newobject\s14day6\test.py",r"D:\MyPython\newobject\test1.log") # 将test.py复制到test1.log |
shutil.copyfile(src,des) |
shutil.copy方法就是调用的这个方法来复制文件的 |
|
shutil.copymod(src,des) |
shutil.copy方法用来复制用户权限,此方法仅复制用户的权限,不复制文件的内容,组和用户 |
|
shutil.copystat(src,des) |
copy文件的状态信息,包括修改时间等信息 |
|
shutil.copy2(src,des) |
拷贝文件和状态信息 |
|
shutil.copytree(src,des) |
拷问目录,之前的源文件都是一个文件,这个src是一个目录 |
|
shutil.rmtree(path) |
删除非空目录,如果是空目录可以用os.removedirs(),方法会逐级删除路径上级的空目录,例如C:/123/234/345,如果345为空则删除并检查234,如果234为空则删除并检查123。 删除文件:os.remove() |
|
shutil.move(src,des) |
移动文件 |
|
shutil.make_archive(base_name,format,root_dir,owner,group,logger) |
创建压缩文件 base_name:压缩包的文件名(文件名会自动放到根目录下的文件名)或路径, format:压缩包格式zip,tar,bztar,gztar, root_dir:被压缩的文件夹路径, owner:用户(默认当前用户), group:组,默认当前组, loggeer:用于记录日志,通常是logging,logger对象 |
shutil.make_archive(r"D:\MyPython\\newobject\\test1","zip",root_dir=r"D:\MyPython\newobject\test") |
zipfile模块(感觉tarfile更好)
import zipfile
# 压缩
z = zipfile.ZipFile("111.zip","w") # 用w的方式创建一个压缩文件111.zip,使用ZipFile方法创建压缩文件,压缩文件中包含压缩包的完整目录结构
z.write("a.log") # 指定想要压缩的文件
z.write("b.log")
z.write("a.log")
z.close() # 关闭压缩文件
# 上面的方法就是压缩文件的方法,可以指定需要压缩的文件
# 解压缩
z = zipfile.ZipFile("111.zip","r") # 用r方式读取压缩文件
z.extractall() # 解压缩全部文件
z.close()
tarfile模块,打包同时也可以压缩的方法。感觉比zipfile灵活
import tarfile |
|
||||||||||||||||
tar = tarfile.open(r"d:\your.tar.bz2","w:bz2") |
源文档
|
||||||||||||||||
tar.add(r"D:\MyPython\new object\s13day4",arcname="AAA") |
# 此处如果不指定arcname,则打包后这个文件夹将保留绝对路径的目录结构,如果指定了arcname则只有arcname的路径,例如s13day4里有一个123.log文件,那么如果不指定arcname,那么打包之后的路径就是[tar_root]\MyPython\new object\s13day4\123.log,如果指定了路径就是[tar_root]AAA\123.log |
||||||||||||||||
tar.add(r"D:\MyPython\new object\test1.zip",arcname="test.zip") |
|
||||||||||||||||
tar.close() |
|
上面的结果为:
[tar_root]AAA
[tar_root]test.zip
解包
tar = tarfile.open(r"d:\your.tar","r") |
|
tar.extractall(path=r"D:\MyPython\newobject") |
解包,可设置路径, |
tar.close() |
|
json模块和pickle模块
-
-
- json,用于字符串 和 python数据类型间进行转换
- pickle,用于python特有的类型 和 python的数据类型间进行转换
-
两个模块都提供相同的方法:
json.dump(data,文件名) |
将data转为字符串,并写入文件中 |
json.dumps(data) |
将data转为字符串,如果需要写入文件则 |
json.load(data) |
将一个文件内容转为python的数据类型 |
json.loads(data) |
将一个字符串转为python的数据类型 |
源文档
shelve模块
是一个简单的k,v将内存数据通过文件持久化的模块,可以支持任何pickle可支持的python数据格式
详见链接http://www.cnblogs.com/alex3714/articles/5161349.html>
xml模块
ElementTree 方式创建文档时,无法设置缩进
读取和查询
python中处理xml语句
例如一个xml
//XML版本信息
import xml.etree.ElementTree as ET |
导入xml模块 |
tree = ET.parse("test.xml") |
# 打开一个test.xml文件 |
root = tree.getroot() |
# 获取xml的根节点,此时root的值就是xml里面的根节点config的内容实际就是整个xml的内容,实际上此时的root是一个迭代器 |
print(root.tag,root.attrib) |
# 打印这个根节点,root.tag就是打印这个根节点的标签config。.tag是打印标签的方法 |
for i in root: |
#循环root |
print(i.tag,i.attrib,i.text) |
#遍历root,tag是标签,attrib是标签属性,text是标签内容。 tag:PrimaryServerName text:142.100.64.11
tag:config attrib:version="1.0",但是现实时会显示为字典格式{'version': '1.0'}
|
如果我需要过滤下面的year标签,使用iter("year")
源文档
import xml.etree.ElementTree as ET |
导入xml模块 |
tree = ET.parse("test.xml") |
# 打开一个test.xml文件 |
root = tree.getroot() |
# 获取xml的根节点,此时root的值就是xml里面的根节点config的内容实际就是整个xml的内容,实际上此时的root是一个迭代器 |
for node in root.iter("year"): |
过滤所有标签为year的标签内容,这个遍历不论层级,只要再当前这个层级一下的标签为year就会被遍历 |
print(node.tag,node.text) |
打印查询出的标签的标签名和内容 |
删除和修改xml文件
修改
tree = ET.parse("test.xml") |
# 打开一个test.xml文件 |
root = tree.getroot() |
# 获取xml的根节点,此时root的值就是xml里面的根节点config的内容实际就是整个xml的内容,实际上此时的root是一个迭代器 |
for node in root.iter("year"): |
|
new_year = int(node.text) + 1 |
#定义一个变量,值为标签year的值+1 |
node.text = str(new_year) |
#给year的值赋新值,新值的类型必须是字符串 |
node.set("updated","yes") |
#这句话的意思是出了上面的动作外,我还想要给year增加一个属性,为"updated":"yes",这样year这个标签就由 变成
|
tree.write("test.xml",encoding="utf-8") |
最后将更新的XML脚本写入文件,这个文件可以是原先的文件也可以是新文件 |
删除节点
tree = ET.parse("test.xml") |
# 打开一个test.xml文件 |
root = tree.getroot() |
# 获取xml的根节点,此时root的值就是xml里面的根节点config的内容实际就是整个xml的内容,实际上此时的root是一个迭代器 |
for country in root.findall("country"): |
检索xml文件中,所有country标签下的内容(findall),因为country下面是标签,不是内容,所以需要用findall检索所有的标签,如果是find("country"),就是检索下面的内容 |
if int(country.find("rank").text) > 50: |
country.find("rank").text的意思是获取country标签下的rank标签的内容,因为这个内容是字符串,所以需要变为int类型
|
root.remove(country) |
移除当前这个country的标签 |
tree.write("test.xml",encoding="utf-8") |
最后将更新的XML脚本写入文件,这个文件可以是原先的文件也可以是新文件 |
创建xml文本
namelist=ET.Element("namelist") |
# 创建一个根标签namelist |
name=ET.SubElement(namelist,"name",attrib={"test":"yes"}) |
# 创建一个新的标签,这个标签是跟标签namelist下的标签,标签名为name,其属性是{"test":"yes"}) |
name.text="alex" |
#name标签的内容为alex |
age=ET.SubElement(name,"age") |
创建了一个namelist下的标签名为age |
age.text="30" |
内容为30 |
role=ET.SubElement(name,"role") |
|
role.text="teacher" |
|
name=ET.SubElement(namelist,"name",attrib={"test":"yes"}) |
|
name.text="zhangsan" |
|
age=ET.SubElement(name,"age") |
|
age.text="30" |
|
role=ET.SubElement(name,"role") |
|
role.text="ST" |
|
et=ET.ElementTree(namelist) |
文本建立后,生成这个xml语句,因为所有的标签都是在namelist标签下,所以直接生成根就可以 |
et.write("jabber-config3.xml",encoding="utf-8",xml_declaration=True) |
将生成的语句写入文件xml_declaration=True这句话的意思是生成后的文件里自动加上版本声明: |
|
|
ET.dump(namelist) |
这个语句是将生成的内容直接在屏幕输出 |
import xml.dom.minidom as MD
http://www.cnblogs.com/kaituorensheng/p/4493306.html
http://www.oschina.net/question/234345_48732
xml.dom.minidom方法在创建xml文档时可以指定缩进,从而形成标准的xml格式
import xml |
导入模块 |
impl=xml.dom.getDOMImplementation() |
|
namelist=impl.createDocument(None,'namelist',None) |
创建这个文档namelist |
root=namelist.documentElement |
指定namelist为根标签 |
name=namelist.createElement('name') |
在文档namelist中创建name子标签 |
name_value=namelist.createTextNode("zhangsan") |
在文档namelist中间内容name_value |
name.appendChild(name_value) |
将name_value赋值给name |
root.appendChild(name) |
将其加入到根标签下 |
|
|
age=namelist.createElement('age') |
在文档namelist中创建子标签age |
age.setAttribute("attr","30") |
设置age标签的属性为attr = 30 |
age_value=namelist.createTextNode("222") |
在文档namelist中创建一个文本节点age_value |
age.appendChild(age_value) |
将age_value赋值给age |
name.appendChild(age) |
将age标签追加到name下 |
file=open('1.xml','w',encoding='utf-8') |
打开文档 |
namelist.writexml(file,addindent=" '',newl="\n",encoding='utf-8') |
将namelist文档写入,addindent设置每级标签的缩进,newl设置了xml中每一行的换行方式 |
file.close() |
关闭文档 |
下面的例子是通过循环的方式创建xml文件
import xml
from xml.dom import minidom
list = [("zhangsan","30"),("lisi","40"),("wangwu","50")]
impl=xml.dom.getDOMImplementation()
namelist=impl.createDocument(None,'namelist',None)
root=namelist.documentElement
for i in list:
name=namelist.createElement('name')
name_value=namelist.createTextNode(i[0])
name.appendChild(name_value)
root.appendChild(name)
age=namelist.createElement('age')
age.setAttribute("attr",i[1])
age_value=namelist.createTextNode(i[1])
age.appendChild(age_value)
name.appendChild(age)
f=open('1.xml','w',encoding='utf-8')
namelist.writexml(f,addindent=" ",newl="\n",encoding='utf-8')
f.close()
输出结果为
zhangsan
lisi
wangwu
ConfigParser模块
用于生成和修改常见配置文档,当前模块在python3.x版本更名为configparser
配置文件
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
[bitbucket.org]
User = hg
[topsecret.server.com]
Port = 50022
ForwardX11 = no
源文档
要创建上面的文件 按如下写法
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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'] # 第三种,将key作为变量直接追加,总之都 topsecret['Host Port'] = '50022' # mutates the parser # 是按字典的方式写,将标签看做key里面的 topsecret['ForwardX11'] = 'no' # same here # 内容作为value config['DEFAULT']['ForwardX11'] = 'yes' # 看好标签,追加到哪里了?
with open('example.ini', 'w') as configfile: config.write(configfile) # 此处写入文档时的方法和open不一样 注意! |
源文档
读取配置文件
import configparser |
|
config = configparser.ConfigParser() |
|
print(config.sections()) |
此时sections里什么都没有因为没有读任何内容,输出: |
config.read('example.ini') |
读取一个配置文件 |
print(config.sections()) |
此时sections里会显示出了DEFAULT标签外的其他标签 |
print('bitbucket.org' in config) |
检查标签是否存在在配置文件内 |
print('bytebong.com' in config) |
False |
print(config['bitbucket.org']['User']) |
获取特定标签的值,如果没有回报错 |
print(config['bitbucket.org'].get('User1')) |
使用这种方法,如果没有会返回 |
topsecret = config['topsecret.server.com'] print(topsecret['ForwardX11']) |
同理 可以赋值一个key获取 |
for key in config['bitbucket.org']: print(key) |
循环的方式获取key,出了当前的key还有DEFAULT的 |
for key,v in config['bitbucket.org'].items():print(key,v) |
连值一起取 |
print(config.options('bitbucket.org')) |
options方法,是可以以列表的形式输出出当前的标签里的key和DEFAULT里的key 配置文件中内容解释 section:就是[DEFAULT]这样的标签 option:是标签内的内容ServerAliveInterval = 45 因此 config.options('bitbucket.org')的意思就是打印配置文件中bitbucket.org标签下的内容 |
print(config.items('bitbucket.org')) |
items方法,以元祖形式输出当前标签和DEFAULT里的key和值 |
源文档
删除内容
config.remove_option("DEFAULT","compression") |
删除特定section中的特定的option |
with open('example1.ini', 'w') as configfile: config.write(configfile) |
将删除后的配置文件保存 |
config.remove_section("bitbucket.org") |
删除特定的section |
with open('example1.ini', 'w') as configfile: config.write(configfile) |
|
更改内容
config.set("DEFAULT","compressionlevel","8888") |
变更option的值,括号内的内容一次为section,option,value |
增加内容
config.add_section("zhangsan") |
增加一个section |
config["liubo"]["age"] = "30" |
给这个section增加一个option并赋值 |
config.write(open("example4.ini","w")) |
写入文件 |
判断是否存在
print(config.has_section("lisi")) |
判断是否存在特定的section |
print(config.has_option("lisi","age")) |
判断在特定的section中是否存在option,括号内的顺序为section,option |
hashlib散列
md5等方法无法反解,但可能被暴力破解(即攻击者穷举法md5每个密码,将结果与md5值对比来破解)
散列将任意长度的字符串转成特定长度的字符串,hash的特点为输入固定则输出一定固定,输入变化输出一定变化。进行文件一致性验证
散列的方式可以有md5,sha1,sha256,sha384,sha512,通常使用sha256,下例采用md5方式,其他方法值需要变更散列方式即可(m = hashlib.sha256())
import hashlib |
导入hashlib模块 |
m = hashlib.md5() |
|
m.update(b"alex") |
对alex这个字符串进行md5操作,字符串必须是字节形式(b)才能md5 |
print(m.hexdigest()) |
以十六进制形式输出 |
对于hashlib.md5来说,
m.update(b"alex")
print(m.hexdigest())
m.update(b"liubo")
print(m.hexdigest())
最终输入的m.hexdigest和
m.update(b"alexliubo")
print(m.hexdigest())
输出的值是一样的,
其意义在于,当我们对文件进行散列时,完全可以用for循环逐行update,最终的结果和直接将文件全部加载再散列是一样的。当文件非常庞大时我们不需要一次性加载再整体散列。使用for循环的方式肯定会比整个文件散列要慢,但是这样做可以节约内存空间。
hmac模块
hmac对创建的kye和内容进行处理后再加密,安全性比hash高
一般用于网络消息加密传输,其加密性能比hash256要好
import hmac |
|
h_obj = hmac.new(b"key",b"cisco,123") |
第一个参数为key就是hmac在处理时加入在正式内容中的key,key可以为任何字符串,第二个参数是要加密的字符串 |
print(h_obj.exdigest()) |
打印结果 |
Subprocess模块
此模块会替换os.system()和os.spawn方法。
logging模块
日志处理模块
logging模块
import logging
logging.warning("")
logging.critical("")
logging日志级别(从上向下级别越来越高,debug最低)
DEBUG |
最详细的日志级别,用于故障排查 |
INFO |
一些通用时间 |
WARNING |
一些非预期的时间发生,表明出现了一些问题,但是软件仍然工作 |
ERROR |
一些使软件的功能无法正常工作的错误 |
CRITICAL |
一系列的ERROR,软件已经无法继续运行 |
将文件记录进文件的方法
logging.basicConfig(filename="文件名",level=logging.INFO) # 只有日志级别不低于INFO的日志才会被记录到文件中
logging.debug("")
logging.info("")
logging.warning("")
将时间记录今日志
logging.basicConfig(filename="文件名",level=logging.INFO,format="%(asctime)s %(message)s",datefmt="%m%d%Y %I:%M:%S %p")
在上面的例子中,asctime和message都是占位符,代表将来额日志先显示时间,后显示消息。datefmt则是时间的格式
同时将日志打印到屏幕和日志文件中
logging模块有4个部分:
logger |
是一个接口,用来产生日志条目 |
handlers |
发送日志条目到指定的目的地(日志条目由logger产生) |
filters |
提供一种日志过滤的方法(关键字等) |
formatters |
将日志格式化输出 |
import logging
# create logger
logger = logging.getLogger("TEST-LOG") # 首先获取logger这个对象
logger.setLevel(logging.DEBUG) # 设定一个全局的日志级别
# create console handler and set level to debug
ch = logging.StreamHandler() # 在屏幕上打印日志条目
ch.setLevel(looging.DEBUG) # 指定在屏幕上打印debug以上级别的信息
# create file handler and set level to waring
fh = logging.FileHandler("access.log") # 在文件上打印日志条目
fh.setLevel(logging.WARNING) # 指定打印warning以上级别的信息到文件中
# create formatter
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s" # %(name)s 这个变量指代的就是getLogger中的文本"TEST-LOG"
formatter_file = logging.Formatter("%(asctime)s - %(name)s - %(evelname)s - %(message)s")
# add formatter to ch and fh
ch.setFormatter(formatter) # 传递给屏幕上的日志条目应该按照formatter的格式输出
fh.setFormatter(formatter_file) # 传递给文件的日志条目应该按照formatter_file的格式输出
# add ch and fh to logger
logger.addHandler(ch) # 告诉logger将日志按照ch的设置输出到屏幕上
logger.addHandler(fh) # 告诉logger将日志按照fh的设置输出到文件中
logger.debug("") # 调用创建的logger来记录数据
logger.info("")
logger.warning("")
logger.error("")
logger.critical("")
在上面的例子中 logger和handle都定义了level,在这里 logger是全局的日志级别,他定义了最低能显示的级别,handler的级别只能等于或高于logger的级别
在同一个logger中被多个同类handler调用,就会生成多条相同的记录(比如我想要同一个logger,分别向2个文件输出,那么每个文件中都会得到2条一样的数据)。因此如果我需要同时向多个文件输出日志,就需要创建多个logger handler来分开记录。
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参数中可能用到的格式化串:
%(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 |
用户输出的消息 |
源文档
logging.handlers.RotatingFileHandler() |
此方法用于管理日志文件大小,当文件达到一定大小之后,它会自动将当前 日志文件改名,然后创建一个新的同名文件继续输入 |
日志分割(按大小分割)
import logging |
引入模块 |
logger = logging.getLogger("log") |
定义一个名为log的logger |
log_file = "time.log" |
|
logger.setLevel(logging.DEBUG) |
|
# fh = logging.FileHandler(log_file,encoding="utf-8") |
这是原来定义的没有分割的方法,只是定义日志的文件名 |
fh = handlers.RotatingFileHandler(filename = log_file,maxBytes = 10,backupCount = 3,encoding="utf-8") |
现在使用新的方法,这个方法出了定义了文件位置,还定义了一个文件最大的大小为10字节,并且只保留3个(即当出现第四个日志文件时,第一个会被删除,滚动输出),utf-8编码保证了输出中文时不乱码,如果maxBytes为0则不分割 |
fh.setLevel(logging.WARNING) |
|
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s" # %(name)s) |
|
fh.setFormatter(formatter) |
|
logger.addHandler(fh) |
|
日志分割(按时间)
fh = handlers.TimeRotatingFileHandler(filename=log_file,when="S",interval=5,backupCount=3)i |
when指定了时间的单位: M 分 H 小时 D 天(以当前生效时间开始计时) W 每星期(interval = 0则为星期一) midnight 每天凌晨
|
正则表达式模块 re模块
import re |
引入正则模块 |
a = re.match("规则","str") |
match根据定义的规则,从字符串的开头开始匹配 |
print(a.group()) |
输出结果 |
re.match(规则,字符串) |
从头开始匹配 |
|
re.search(规则,字符串) |
匹配包含,但是只返回第一次匹配的结果 |
这是常用方法 |
re.findall(规则,字符串) |
把所有匹配到的字符放到以列表中的元素返回 |
|
re.split(规则,字符串) |
以匹配到的字符当做列表分隔符,无论如何匹配以及匹配了多长的字符,只要匹配了 他就是分隔符,输出的是被分割的结果 |
|
re.sub(规则,替换字符串,原字符串) |
匹配字符并替换 |
|
源文档
正则表达式符号
'.' |
默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行 |
'^' |
匹配字符开头,若指定flags MULTILINE,这种也可以匹配上(r"^a","\nabc\neee",flags=re.M) |
'$' |
匹配字符结尾,或e.search("foo$","bfoo\nsdfsf",flags=re.MULTILINE).group()也可以 |
'*' |
匹配*号前的字符0次或多次,re.findall("ab*","cabb3abcbbac") 结果为['abb', 'ab', 'a'] |
'+' |
匹配前一个字符1次或多次,re.findall("ab+","ab+cd+abb+bba") 结果['ab', 'abb'] |
'?' |
匹配前一个字符1次或0次 |
'{m}' |
匹配前一个字符m次 |
'{n,m}' |
匹配前一个字符n到m次,re.findall("ab{1,3}","abb abc abbcbbb") 结果'abb', 'ab', 'abb'] |
'|' |
匹配|左或|右的字符,re.search("abc|ABC","ABCBabcCD").group() 结果'ABC' |
'(...)' |
分组匹配,re.search("(\w{2})a(123|456)c", "abcabca456c").group() 结果 bca456c 当使用groups()时则显示('bc', '456'),小括号代表将查询规则分组,group没有特别的就显示了匹配的结果,而goups则按照分组显示匹配到的内容,显示结果为元组 |
'\A' |
只从字符开头匹配,re.search("\Aabc","alexabc") 是匹配不到的 |
'\Z' |
匹配字符结尾,同$ |
'\d' |
匹配数字0-9 |
'\D' |
匹配非数字 |
'\w' |
匹配[A-Za-z0-9] 不匹配特殊字符 |
'\W' |
匹配非[A-Za-z0-9] |
's' |
匹配空白字符、\t、\n、\r , re.search("\s+","ab\tc1\n3").group() 结果 '\t' |
'(?P |
分组匹配 re.search("(?P ").groupdict("city") 结果{'province': '3714', 'city': '81', 'birthday': '1993'},这种方法可以将之前()的分组显示为字典并加入key |
源文档
a.group() |
显示匹配的完整字符串 |
a.groups() |
根据分组显示匹配的分组字符串 |
re.search和re.findall
import re
str1 ="""
以太网适配器 VMware Network Adapter VMnet8:
连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::78c3:7b4c:577d:3c05%19
IPv4 地址 . . . . . . . . . . . . : 192.168.44.1
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . :
"""
print(re.search("(\d{1,3}\.){3}(\d{1,3})",str1).group())
匹配IP地址的规则,在这里因为"."在正则表达式中代表任意一位字符,因此此处必须转译"\."
如果我想取出 IP 和掩码,就需要用re.findall,而且规则也要变化,而且因为findall是以列表输出,因此不能使用group
print(re.findall("(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})",str1))
re.sub
print(re.sub("(\d{1,3}\.){3}(\d{1,3})","333.333.33.3",str1))
输出
以太网适配器 VMware Network Adapter VMnet8:
连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::78c3:7b4c:577d:3c05%19
IPv4 地址 . . . . . . . . . . . . : 333.333.33.3
子网掩码 . . . . . . . . . . . . : 333.333.33.3
默认网关. . . . . . . . . . . . . :
========================================================================================
如果指定count,则只替换count次
print(re.sub("(\d{1,3}\.){3}(\d{1,3})","333.333.33.3",str1,count=1))
以太网适配器 VMware Network Adapter VMnet8:
连接特定的 DNS 后缀 . . . . . . . :
本地链接 IPv6 地址. . . . . . . . : fe80::78c3:7b4c:577d:3c05%19
IPv4 地址 . . . . . . . . . . . . : 333.333.33.3
子网掩码 . . . . . . . . . . . . : 255.255.255.0
默认网关. . . . . . . . . . . . . :
r"字符串"
r"字符串"代表字符串中的内容无论有何意义都不做处理当做纯文本内容,例如一个路径,因为其分割为\:D:\MyPython\new object\s14day6\time.log.1
这是如果直接"D:\MyPython\new object\s14day6\time.log.1",那么里面的\会被认为为转移符,那么当我们制定为r"D:\MyPython\new object\s14day6\time.log.1"时,则里面的\或者里面有任何可能被认为是有其他用法的符号都会被当做字符串的一部分没有特殊意义。
反斜杠的困扰
与大多数编程语言相同,正则表达式里使用"\"作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符"\",那么使用编程语言表示的正则表达式里将需要4个反斜杠"\\\\":前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。Python里的原生字符串很好地解决了这个问题,这个例子中的正则表达式可以使用r"\\"表示。同样,匹配一个数字的"\\d"可以写成r"\d"。有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
源文档
本来在写正则表达式时,匹配\时应该写成\\,但是当使用python写的时候,正则中的2个\ 又必须要被转译 因此\\\\才能匹配正则表达式中的\\ 实际匹配一个\。而用r"\\"则可以省略4个\\\\
因此当我需要替换\:
print(re.sub(r"\\","%",r"D:\MyPython\newobject\s14day6\time.log.1"))
输出
D:%MyPython%new object%s14day6%time.log.1
仅需轻轻知道的几个匹配模式
在re的所有模块参数中,都存在一个flag参数,其值可以设置一些匹配的功能
re.I(re.IGNORECASE): 忽略大小写(括号内是完整写法,下同) |
print(re.search("^a.*","ABC")),这样因为a是小写,无法匹配A |
re.M(MULTILINE): 多行模式,改变'^'和'$'的行为(参见上图) |
print(re.search(r"^a","\nabc\neee",flags=re.M)),这里字符串 以\n开头,代表第一行为空a是第二行开头,那么如果不加re.M则匹配不上,加了以后就变为多行模式a为第二行的开始,就可以匹配上 |
re.S(DOTALL): 点任意匹配模式,改变'.'的行为 |
.这个符号在正则中代表任意字符,但是除了\n,这样也就是说他可以匹配一行中的任意字符,但是如果字符串时多行的,因为.无法代表换行符,他仍然只能匹配一行的字符,无法返回多行,那么这就需要flags=re.S,这样.就可以指代换行符,在多行的字符串中一个.+,就可以匹配全部多行的字符串 |
当我们要将某些字符加入特定的前缀时,需要re.sub结合lambda函数使用
re.sub("\d{1,4}",lambdamatch:"AA"+match.group(),line) |
替换部分的函数时这样的,其默认会将match部分的内容作为参数传入函数中,此时我们可以通过match.group()来取得匹配的内容 |
下面是上面例句的完整例子:
需求,在配置网关时,我们希望将网关的dial-peer编号加上特定的前缀
配置文件:
dial.log
dial-peer voice 102 pots
translation-profile incoming E1-incoming
port 0/1/0:15
!
dial-peer voice 1023 pots
translation-profile incoming E1-incoming
port 0/1/1:15
!
dial-peer voice 1 pots
translation-profile incoming E1-incoming
port 0/0/0:15
!
dial-peer voice 10 pots
translation-profile incoming E1-incoming
port 0/0/1:15
with open("dial.log","r",encoding="utf-8") as file1,open("dial1.log","w",encoding="utf-8") as file2:
for line in file1:
if re.search("^dial",line.strip()):
file2.write(re.sub("\d{1,4}",lambda match:"AA" + match.group(),line).strip() + "\n")
else:
file2.write(line.strip() + "\n")
新配置文件:
dial1.log
dial-peer voice AA102 pots
translation-profile incoming E1-incoming
port 0/1/0:15
!
dial-peer voice AA1023 pots
translation-profile incoming E1-incoming
port 0/1/1:15
!
dial-peer voice AA1 pots
translation-profile incoming E1-incoming
port 0/0/0:15
!
dial-peer voice AA10 pots
translation-profile incoming E1-incoming
port 0/0/1:15
本节作业
1、实现加减乘除及括号优先级的解析
2、用户输入 1 - 2 * ( (60-30 +(-40/5) * (9-2*5/3 + 7 /3*99/4*2998 +10 * 568/14 )) - (-4*3)/ (16-3*2) )等类似公式后,必须自己解析里面的(),+,-,*,/符号和公式(不能调用eval等类似功能偷懒实现),运算后得出结果,结果必须与真实的计算器所得出的结果一致
[^()]+
源文档