用Python哈希文件


98

我想让python读取EOF,这样我就可以获取适当的哈希,无论它是sha1还是md5。请帮忙。这是我到目前为止的内容:

import hashlib

inputFile = raw_input("Enter the name of the file:")
openedFile = open(inputFile)
readFile = openedFile.read()

md5Hash = hashlib.md5(readFile)
md5Hashed = md5Hash.hexdigest()

sha1Hash = hashlib.sha1(readFile)
sha1Hashed = sha1Hash.hexdigest()

print "File Name: %s" % inputFile
print "MD5: %r" % md5Hashed
print "SHA1: %r" % sha1Hashed

6
有什么问题吗?
isedev

1
我希望它能够散列文件。无论文件大小如何,我都需要将其读取到EOF。
user3358300 2014年

3
正是file.read()这样-读取整个文件。
isedev

read()方法的文档说?
伊格纳西奥·巴斯克斯

您应该经历“什么是哈希?”。
Sharif Mamun 2014年

Answers:


135

TL; DR使用缓冲区不使用大量内存。

我相信,当我们考虑使用非常大的文件对内存的影响时,我们就陷入了问题的症结。我们不希望这个坏男孩为2 GB的文件流过2 gigs的ram,因此,正如pasztorpisti指出的那样,我们必须将那些较大的文件分块处理!

import sys
import hashlib

# BUF_SIZE is totally arbitrary, change for your app!
BUF_SIZE = 65536  # lets read stuff in 64kb chunks!

md5 = hashlib.md5()
sha1 = hashlib.sha1()

with open(sys.argv[1], 'rb') as f:
    while True:
        data = f.read(BUF_SIZE)
        if not data:
            break
        md5.update(data)
        sha1.update(data)

print("MD5: {0}".format(md5.hexdigest()))
print("SHA1: {0}".format(sha1.hexdigest()))

我们所做的是,随着hashlib方便的dandy update方法的进行,我们将以64kb的块更新这个坏男孩的哈希。这样,我们使用的内存就比一次哈希一个家伙所需的2gb少得多!

您可以使用以下方法进行测试:

$ mkfile 2g bigfile
$ python hashes.py bigfile
MD5: a981130cf2b7e09f4686dc273cf7187e
SHA1: 91d50642dd930e9542c39d36f0516d45f4e1af0d
$ md5 bigfile
MD5 (bigfile) = a981130cf2b7e09f4686dc273cf7187e
$ shasum bigfile
91d50642dd930e9542c39d36f0516d45f4e1af0d  bigfile

希望有帮助!

右侧的链接问题中也概述了所有这些内容:在Python中获取大文件的MD5哈希


附录!

通常,在编写python时,它有助于养成遵循pep-8的习惯。例如,在python中,变量通常用下划线分隔而不是驼峰式。但这只是样式,除了必须阅读不良样式的人之外,没有人真正关心这些事情……这可能是您从现在开始阅读此代码的原因。


@ranman您好,我无法获得{0}“。format(sha1.hexdigest())部分。为什么我们使用它而不是仅仅使用sha1.hexdigest()?
Belial

@Belial什么不起作用?我主要是用它来区分两个散列...
Randall Hunt

@ranman一切正常,我只是从未使用过它,也没有在文献中看到它。“ {0}”。format()...我不知道。:)
Belial

1
我该如何选择BUF_SIZE
马丁·托马

1
这不会产生与shasum二进制相同的结果。下面列出的另一个答案(使用memoryview的答案)与其他哈希工具兼容。
tedivm

61

为了正确有效地计算文件的哈希值(在Python 3中):

  • 以二进制模式(即添加'b'到文件模式)打开文件,以避免字符编码和行尾转换问题。
  • 不要将整个文件读到内存中,因为那样会浪费内存。而是逐块顺序读取它,并更新每个块的哈希值。
  • 消除双重缓冲,即不使用缓冲的IO,因为我们已经使用了最佳的块大小。
  • 使用readinto()以避免缓冲区翻腾。

例:

import hashlib

def sha256sum(filename):
    h  = hashlib.sha256()
    b  = bytearray(128*1024)
    mv = memoryview(b)
    with open(filename, 'rb', buffering=0) as f:
        for n in iter(lambda : f.readinto(mv), 0):
            h.update(mv[:n])
    return h.hexdigest()

2
您如何知道最佳块大小?
米塔尔(Mitar)

1
@Mitar,下限是物理块的最大值(传统上为512字节,对于较新的磁盘为4KiB)和系统页面大小(在许多系统上为4KiB,其他常见选择是8KiB和64 KiB)。然后,您基本上会进行一些基准测试和/或查看已发布的基准测试结果和相关工作(例如,检查当前使用的rsync / GNU cp / ...)。
maxschlepzig

请问resource.getpagesize有什么用处在这里,如果我们想尝试有点动态优化呢?那又如何mmap呢?
jpmc26

@ jpmc26,getpagesize()在这里不是很有用-常用值为4 KiB或8 KiB,在此范围内,即比128 KiB小得多-128 KiB通常是一个不错的选择。在我们的用例中,mmap并没有多大帮助,因为我们从前到后依次读取完整的文件。当访问模式更随机访问时,例如,如果页面被多次访问和/或访问,则mmap具有优势。mmap简化了读取缓冲区的管理。
maxschlepzig

3
我对(1)@Randall Hunt和(2)您的解决方案进行了基准测试(此顺序很重要,由于文件缓存很重要),文件大小约为116GB,并采用sha1sum算法。修改了解决方案1,以便使用20 * 4096(PAGE_SIZE)的缓冲区并将缓冲参数设置为0。仅修改了解决方案2的算法(sha256-> sha1)。结果:(1)3m37.137s(2)3m30.003s。二进制模式下的本地sha1sum:3分31.395秒
生物信息学

18

我会简单地建议:

def get_digest(file_path):
    h = hashlib.sha256()

    with open(file_path, 'rb') as file:
        while True:
            # Reading is buffered, so we can read smaller chunks.
            chunk = file.read(h.block_size)
            if not chunk:
                break
            h.update(chunk)

    return h.hexdigest()

这里所有其他答案似乎过于复杂。Python在读取时已经在缓冲(以理想的方式,或者如果您有更多有关基础存储的信息,则可以配置该缓冲),因此最好分块读取散列函数找到的理想值,这样可以使其更快或更省时地减少CPU占用计算哈希函数。因此,您可以使用Python缓冲并控制应该控制的内容,而不是禁用缓冲并尝试自己模拟它,即数据消费者可以找到理想的哈希块大小。


完美的答案,但是如果您使用相关文档来支持您的语句,那就太好了:Python3-open()Python2-open()。即使记住两者之间的区别,Python3的方法也更加复杂。不过,我真的很欣赏以消费者为中心的观点!
Murmel

hash.block_size记录为“散列算法的内部块大小”。Hashlib 没有找到理想的。软件包文档中没有任何内容建议使用大小update()合适hash.block_size的输入。如果这样调用它,它不会占用更少的CPU。您的file.read()通话导致从文件中许多不必要的对象的创建和多余的缓冲区拷贝到新的块字节对象。
maxschlepzig

哈希将block_size大块更新其状态。如果您没有在这些块中提供它们,则它们必须缓冲并等待足够的数据出现,或者在内部将给定的数据拆分为块。因此,您可以在外部进行处理,然后简化内部发生的事情。我觉得这个理想。参见例如:stackoverflow.com/a/51335622/252025
Mitar

block_size比任何有用的读取尺寸小得多。同样,任何有用的块和读取大小均为2的幂。因此,所有读取的读取大小都可以被块大小整除,除了最后一个读取之外。例如,sha256块大小为64个字节。这意味着update()可以直接处理输入,而无需对进行任何缓冲block_size。因此,只有最后一次读取不能被块大小整除时,它才必须缓冲多达63个字节。因此,您的最后评论不正确,不支持您在回答中提出的主张。
maxschlepzig

重点是不必优化缓冲,因为在读取时已经由Python完成。因此,您只需要确定在现有缓冲区上进行哈希处理时要执行的循环数量即可。
米塔尔

5

我已经编写了一个模块,该模块能够使用不同的算法对大文件进行哈希处理。

pip3 install py_essentials

像这样使用模块:

from py_essentials import hashing as hs
hash = hs.fileChecksum("path/to/the/file.txt", "sha256")

3

这是Python 3,POSIX解决方案(不是Windows!),用于mmap将对象映射到内存中。

import hashlib
import mmap

def sha256sum(filename):
    h  = hashlib.sha256()
    with open(filename, 'rb') as f:
        with mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ) as mm:
            h.update(mm)
    return h.hexdigest()

-2
import hashlib
user = input("Enter ")
h = hashlib.md5(user.encode())
h2 = h.hexdigest()
with open("encrypted.txt","w") as e:
    print(h2,file=e)


with open("encrypted.txt","r") as e:
    p = e.readline().strip()
    print(p)

2
基本上echo $USER_INPUT | md5sum > encrypted.txt && cat encrypted.txt,您所做的是不处理文件散列,特别是不处理大文件。
Murmel

哈希!=加密
bugmenot123
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.