哈希及文件操作

1.哈希 hash

在将文件操作的方法之前,我们先来对上节的知识做一个拓展,就是哈希算法,那么什么是哈希算法呢?

(1)哈希:hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。

这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间。

哈希算法的输入可以是字符串,可以是数据,可以是任何文件,经过哈希运算后,都变成一个固定长度的输出,该输出就是哈希值。

>>> hash('我爱你')
3471388576844338423
>>> hash('小猿圈')
5000768010434506639

如上所示,输入“我爱你”三个字,经过哈希运算后,会得到一个随机数列,而且不管你的输入文件多大,最后得到的结果都是这么一个固定长度的数列,即使你输入的是一部电影,输出也是这么大。

(2)特性:

  1.不可逆性:在具备编码功能的同时,哈希算法也作为一种加密算法存在。即,你无法通过分析哈希值计算出源文件的样子。

  无论是什么形式,任意大小的输入,最终结果都是一串长度相等的随机数列,因此你无法通过哈希值来推断数据原本的样子。

  2.计算极快:不论是一个5G的电影还是一个5k的文件,运用哈希算法计算量都极小,很快就可以计算出哈希值。

(3)用途:

哈希算法的不可逆特性使其在以下领域使用广泛

  1. 密码,我们日常使用的各种电子密码本质上都是基于hash的,你不用担心支付宝的工作人员会把你的密码泄漏给第三方,因为你的登录密码是先经过 hash+各种复杂算法得出密文后 再存进支付宝的数据库里的

  2. 文件完整性校验,通过对文件进行hash,得出一段hash值 ,这样文件内容以后被修改了,hash值就会变。 MD5 Hash算法的”数字指纹”特性,使它成为应用最广泛的一种文件完整性校验和(Checksum)算法,不少Unix系统有提供计算md5 checksum的命令。

  3. 数字签名,数字签名技术是将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用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()

注:以上内容部分参考自路飞学城。

原文地址:https://www.cnblogs.com/sxy-blog/p/11932528.html