一,hashlib 文件一致性校验
为何要进行文件一致性校验?
为了确保你得到的文件是正确的版本,而没有被注入病毒和木马程序。例如我们经常在网上下载软件,而这些软件已经被注入了一些广告和病毒等,如果不进行文件与原始发布商的一致性校验的话,可能会给我们带来一定的损失。
文件一致性校验原理
要进行文件的一致性校验,我们不可能像文本文件比较那样,将两个文件放到一起对比,因为很多的时候文件很大。目前最理想的办法就是,是通过加密算法,对文件生成对应的值,通过生成的值与发布商提供的值比较来确认两个文件是否一致。
MD5和SHA1就是目前使用最为广泛的良种加密算法。
举例:
先手动创建2个文件,file1 和 file 2 ,内容123
使用MD5计算file1的加密值
import hashlib md5obj = hashlib.md5() with open('file1','rb')as f: content = f.read() md5obj.update(content) print(md5obj.hexdigest())
执行后输出:
再计算fiel2的加密值,再把上面的代码复制一遍?太low了,如果有多个文件怎么办?
定义一个方法:
import hashlib def check_md5(filename): md5obj = hashlib.md5() with open(filename,'rb')as f: content = f.read() md5obj.update(content) return md5obj.hexdigest() ret1 = check_md5('file1') ret2 = check_md5('file2') print(ret1) print(ret2)
执行输出:
这样就可以知道,两个文件是否一致了。
但是上面的方法,有一个缺陷,当文件达到GB级别的时候那内存怎么支撑?(这种比对相当于要先把所有的文件都读入内存中)
那么怎么办?先看下面的一个小栗子:
import hashlib md5obj = hashlib.md5() md5obj.update(b'john') #b 'string'表示bytes类型,不能有中文符号 print(md5obj.hexdigest())
拆分字符串
import hashlib md5obj = hashlib.md5() #创建MD5对象 md5obj.update(b'john') #拆分字符串 md5obj.update(b'alen') print(md5obj.hexdigest())
执行输出:
结论:
一段字符串直接进行摘要和分成几段摘要的结果是相同的
那么就可以把大文件,分段进行MD5加密,就可以了
那么就可以把大文件,分段进行md5加密,就可以了
下载一部电影《海上钢琴师》,文件有1.58GB
本片讲述了一个钢琴天才传奇的一生。 豆瓣评分9.2
计算电影的md5值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import
hashlib
def
check(filename):
md5obj
=
hashlib.md5()
with
open
(filename,
'rb'
) as f:
while
True
:
content
=
f.read(
1048576
)
# 每次读取1048576字节,也就是1MB
if
content:
md5obj.update(content)
else
:
break
# 当内容为空时,终止循环
return
md5obj.hexdigest()
ret1
=
check(
'E:\迅雷下载\[迅雷下载www.2tu.cc]海上钢琴师.BD1280高清中英双字.rmvb'
)
print
(ret1)
|
花费了9秒,执行输出:
30c7f078203d761d3f13bec6f8fd3088
总结:
序列化 把数据类型变成字符串
为什么要有序列化,因为在网络上和文件中能存在的只有字节
json
在所有的语言中通用,只对有限的数据类型进行序列化 字典 列表 字符串 数字 元祖
在多次写入dump数据进入文件的时候,不能通过load来取。
pickle
只能在python种使用,对绝对大多数数据类型都可以进行序列化
在load的是哦湖,必须拥有load数据类型对应的类在内存里
dumps 序列化
loads 反序列化
dump 直接向文件中序列化
load 直接对文件反序列化
shelve
f = open() 打开文件
json 和 pickle 必须熟练掌握
二,configarser模块
该模块适用于配置文件的格式与windows ini 文件类似,可以包含一个或多个字节(section),每个字节可以有多个参数(键=值).
创建文件
来看一个好多软件的常见文档格式如下:
section 称之为节点,节点里面赋值对,称之为项
如果想用python生成一个这样的文档怎么做呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
import
configparser
config
=
configparser.ConfigParser()
#创建一个ConfigParser对象
config[
"DEFAULT"
]
=
{
'ServerAliveInterval'
:
'45'
,
#默认参数
'Compression'
:
'yes'
,
'CompressionLevel'
:
'9'
,
'ForwardX11'
:
'yes'
}
config[
'bitbucket.org'
]
=
{
'User'
:
'hg'
}
#添加一个节点bitbucket.org
config[
'topsecret.server.com'
]
=
{
'Host Port'
:
'50022'
,
'ForwardX11'
:
'no'
}
with
open
(
'example.ini'
,
'w'
) as configfile:
#写入配置文件example.ini
config.write(configfile)
|
执行程序,查看example.ini的内容
1
2
3
4
5
6
7
8
9
10
11
12
|
[DEFAULT]
serveraliveinterval
=
45
forwardx11
=
yes
compression
=
yes
compressionlevel
=
9
[bitbucket.org]
user
=
hg
[topsecret.server.com]
forwardx11
=
no
host port
=
50022
|
可以看出节点的项,都变成小写了。
这是因为它在写入的时候,将所有字符串使用了lower()方法,转换为小写了。
查找文件
import configparser config = configparser.ConfigParser() config.read('example.ini') ###上面内容为固定部分### print(config.sections()) # 查看所有的节点,但默认不显示DEFAULT,返回列表
执行输出:
下面的代码,固定部分我就不贴了
1
|
print
(
'bitbucket.org'
in
config)
# 验证某个节点是否在文件中
|
执行输出: True
1
|
print
(config[
'bitbucket.org'
][
'user'
])
# 查看某节点下面的某个项的值
|
执行输出: hg
1
|
print
(config[
'bitbucket.org'
])
# 输出一个可迭代对象
|
执行输出: <Section: bitbucket.org>
1
2
3
|
#使用for循环一个可迭代对象
for
key
in
config[
'bitbucket.org'
]:
# 注意,有default时,会默认输出它的键
print
(key)
|
执行输出:
user
serveraliveinterval
forwardx11
compression
compressionlevel
1
|
print
(config.items(
'bitbucket.org'
))
# 找到'bitbucket.org'下所有的键值对
|
执行输出:
[('serveraliveinterval', '45'), ('forwardx11', 'yes'), ('compression', 'yes'), ('compressionlevel', '9'), ('user', 'hg')]
1
|
print
(config.get(
'bitbucket.org'
,
'compression'
))
# get方法section下的key对应的value
|
执行输出: yes
增删改操作
增加一个节点
1
|
print
(config.add_section(
'yuan'
))
# 增加一个节点
|
注意,它不会立即写入!必须执行下面的代码
1
|
config.write(
open
(
'example.ini'
,
"w"
))
# 写入文件
|
open('example.ini',w) 表示清空文件
config.write 表示写入内容
再次查看文件内容:
[DEFAULT] serveraliveinterval = 45 forwardx11 = yes compression = yes compressionlevel = 9 [bitbucket.org] user = hg [topsecret.server.com] forwardx11 = no host port = 50022 [yuan]
删除一个节点
config.remove_section('bitbucket.org') config.write(open('example.ini','w'))
修改节点
1
2
|
config.
set
(
'yuan'
,
'k2'
,
'222'
)
# yuan节点增加项k2 = 222
config.write(
open
(
'example.ini'
,
"w"
))
# 写入文件
|
总结:
section 可以直接操作它的对象来获取所有的节信息
option 可以通过找到的节来查看多有项
三,loggin
为了保护数据安全
所有的增加,修改,删除操作,都要记录日志
比如log日志,管理员操作日志,消费记录...
日志给我们在内部操作的时候提供很多遍历
日志给用户提供更多的信息
在程序使用的过程中自己调试需要看的信息
帮助程序员排查程序的问题
ogging模块 不会自动帮你添加日志的内容
你自己想打印什么 你就写什么
import logging logging.debug('debug message') logging.info('info message') logging.warning('warning message') logging.error('error message') logging.critical('critical message')
执行输出:
设置INFO,只显示INFO以上的错误
能不能只显示一种级别信息呢?不行!
只能打印某个级别以上的信息
增加时间显示
import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[line:%(lineno)d] ' '%(levelname)s %(message)s') logging.debug('debug message') #debug调试模式 级别模式 logging.info('info message') #info 显示正常信息 logging.warning('warning messafe') #warning 显示警告信息 logging.error('error message') #error 显示错误信息 logging.critical('critical message') #critical 显示验证错误信息
执行输出:
设置时间格式
#设置时间格式 import logging 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', logging.debug('debug message') # debug 调试模式 级别最低 logging.info('info message') # info 显示正常信息 logging.warning('warning message') # warning 显示警告信息 logging.error('error message') # error 显示错误信息 logging.critical('critical message') # critical 显示验证错误信息
执行输出:
logging.basicConfig()函数中可通过具体参数来更改logging模块默认行为,可用参数有: filename:用指定的文件名创建FiledHandler,这样日志会被存储在指定的文件中。 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用户输出的消息
写入文件
1
2
3
4
5
6
7
8
9
10
11
|
import
logging
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
=
'userinfo.log'
)
logging.debug(
'debug message'
)
# debug 调试模式 级别最低
logging.info(
'info message'
)
# info 显示正常信息
logging.warning(
'warning message'
)
# warning 显示警告信息
logging.error(
'error message'
)
# error 显示错误信息
logging.critical(
'critical message'
)
# critical 显示验证错误信息
|
执行程序,查看文件内容
某些情况下,查看文件是乱码的。
它的局限性有2个
编码格式不能设置
不能同时输出到文件和屏幕