1.哈希 hash
在将文件操作的方法之前,我们先来对上节的知识做一个拓展,就是哈希算法,那么什么是哈希算法呢?
(1)哈希:hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。
这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间。
哈希算法的输入可以是字符串,可以是数据,可以是任何文件,经过哈希运算后,都变成一个固定长度的输出,该输出就是哈希值。
>>> hash('我爱你') 3471388576844338423 >>> hash('小猿圈') 5000768010434506639
如上所示,输入“我爱你”三个字,经过哈希运算后,会得到一个随机数列,而且不管你的输入文件多大,最后得到的结果都是这么一个固定长度的数列,即使你输入的是一部电影,输出也是这么大。
(2)特性:
1.不可逆性:在具备编码功能的同时,哈希算法也作为一种加密算法存在。即,你无法通过分析哈希值计算出源文件的样子。
无论是什么形式,任意大小的输入,最终结果都是一串长度相等的随机数列,因此你无法通过哈希值来推断数据原本的样子。
2.计算极快:不论是一个5G的电影还是一个5k的文件,运用哈希算法计算量都极小,很快就可以计算出哈希值。
(3)用途:
哈希算法的不可逆特性使其在以下领域使用广泛
-
密码,我们日常使用的各种电子密码本质上都是基于hash的,你不用担心支付宝的工作人员会把你的密码泄漏给第三方,因为你的登录密码是先经过 hash+各种复杂算法得出密文后 再存进支付宝的数据库里的
-
文件完整性校验,通过对文件进行hash,得出一段hash值 ,这样文件内容以后被修改了,hash值就会变。 MD5 Hash算法的”数字指纹”特性,使它成为应用最广泛的一种文件完整性校验和(Checksum)算法,不少Unix系统有提供计算md5 checksum的命令。
-
数字签名,数字签名技术是将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用HASH函数对收到的原文产生一个摘要信息,与解密的摘要信息对比。如果相同,则说明收到的信息是完整的,在传输过程中没有被修改,否则说明信息被修改过,因此数字签名能够验证信息的完整性。
此外,hash算法在区块链领域也使用广泛。
(4)在Python中基于HASH的数据类型是dict (字典)和set (集合)。之前说到的字典查询速度极快,以及集合天生去重就是运用了hash的特性。以下可以作一下了解。
1.dict查询速度快
假设一个dict中有很多信息,dict会将每一个key进行哈希,将所有的哈希值按照从大到小的顺序放到一个列表中,如keys=[ -22,-10,11,23,99]
当需要查找某个信息时,dict将被查找信息的key进行哈希,同一个输入值进行哈希得到的哈希值时相等的,因此只需再列表中找到这个哈希值,就能找到对应的value。
那么问题来了,如果字典的数据比较小,计算机就能很快找到对应的值,但是如果字典中有几十亿条数据,怎么快速的找到对应的值呢?
dict采用的是二分法查找,即将被查找信息的key的哈希值与列表的中间的值比较大小,这样就可以舍弃一半的值,这样搜索区间就小了很多,多进行几次这样的操作,很快就可以找到对应的值。
这就是字典为何查询速度快的基本原理,当然真实的算法会复杂的多。
2.set 天生去重
因为每存一个值到set里时, 都要先经过hash,然后通过得出的这个hash值算出应该存在set里的哪个位置,存的时候会先检查那个位置上有没有值 ,
有的话就对比是否相等,如果相等,则不再存储此值。 如果不相等(即为空),则把新值存在这。
2.文件操作
Python的文件操作基本通过以下步骤
f = open(filename) # 打开文件
f.write("hello world") # 写操作
f.read( ) # 读操作
f.close( ) # 保存并关闭文件
常用操作模式
python文件有三种基本的操作模式
r 只读模式
w 创建模式,不能读,用此模式操作,新的内容会覆盖旧的内容。即清空原来的内容,写入新的内容。
a 追加模式 ,写入的内容会追加到文件最后
只读模式
f = open('filename.text', 'r') f.readline() # 读一行内容 content = f.read() # 读所有剩下的内容 print(content) f.close()
创建模式
f = open('filename.text', 'w') f.write('hello world') # 此时该文件的内容为hello world(不管原文件内容是什么) f.close()
追加模式
f = open('filename.text', 'a') f.write("小猿圈") # 此时文件内容为 hello world小猿圈 f.close()
循环文件
f = open('filename.tesxt' , 'r') for line in f: # 遍历文件的每一行 print(line)
文件的其他操作功能
f = open('filename.text', 'r') f.mode # 返回文件的打开方式 f.name # 返回文件名 f.fileno() # 返回文件句柄在内核中的索引值,以后做IO多路复用时可以用到 f.flush() #将内容写入硬盘时,由于硬盘的处理速度慢,内容会先在内存中,达到一定数量一# 起写入硬盘提升效率,flush()方法可将内存中的数据写入硬盘 f.readable() # 判断文件是否可读 f.readline() # 只读一行,遇到 或 为止 f.seek() # 把操作文件的光标移动到指定位置 seek的长度是按字节算的 不同的编码方式每 #个字符所占的字节不同。如gbk编码下中文字符占两个字节,utf-8编码下中文字符占三个字 #节,如果读取文件时的编码方式不同会导致错误产生。 f.seekable() # 判断文件是否能进行 seek 操作 f.tell() # 返回当前文件操作光标的位置 f.truncate() # 按指定长度截取文件 # 指定长度的话,就从文件开头开始截断指定长度; # 不指定长度的话,就从光标当前位置到文件 # 尾部的内容全去掉。 f.writeable() # 判断文件是否可写
混合模式
文件打开还有三种混合模式,既可以读也可以写。
w+ 写读 ,它会创建一个新文件 ,写一段内容,可以再把写的内容读出来,一般不用。
r+ 读写,能读能写,但都是写在文件最后,跟追加一样,用的较多。
a+ 追加读,文件一打开时光标会在文件尾部,写的数据全会是追加的形式。
w+模式
f=open('filename.text','w+') # w+读取时 光标在最后一行 读取时需要指定光标 f.write("alex 23 male") f.seek(0) # 将光标移至开头 再读取 print(f.readline()) f.close()
r+模式
f = open('filename', 'r+') f.write() # 默认往文件尾部写 f.readline() # 读的是第一行
r+模式会将内容自动往文件末尾写,但是如果想修改数据应该怎么办?
将光标移动到中间的某一个位置,插入一个信息,会发现原来的信息会被写入的信息所覆盖。原因是,当你将文件存到硬盘上时,就在硬盘上划分了一段空间,空间就那么大,当你想写入新的内容时,就只能覆盖掉原来的数据,而不能使数据整体向后移。
如果想要修改文件,就只能将文件加载到内存当中,数据在内存当中可以随便增删改查,之后再将修改完的数据存入硬盘覆盖掉原来的数据,就完成了文件的修改。
注:r+使用时,该文件必须存在。
当你想修改一份特别大的文件的时候,一下把文件加载到内存,是一种不明智的方式。如果想不占内存,可以采用一边读一边写的方式,就是创建一个新的文件,一边从原文件中读,一边写入新的文件。
f_name = "filename.txt" f_new_name = "%s.new" % f_name old_str = "eric" new_str = "alex" f = open(f_name,'r') # 打开原文件 f_new = open(f_new_name,'w') # 创建一个新文件 for line in f: # 读原文件 if old_str in line: new_line = line.replace(old_str,new_str) # 修改文件 else: new_line = line f_new.write(new_line) 写入新文件 f.close() f_new.close()
注:以上内容部分参考自路飞学城。