Python'da bir dosyanın karma işlemi


101

Python'un EOF'ye okumasını istiyorum, böylece sha1 veya md5 olsun, uygun bir karma elde edebilirim. Lütfen yardım et. Şimdiye kadar sahip olduğum şeyler:

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
ve sorun nedir?
isedev

1
Bir dosyaya hash yapabilmesini istiyorum. Dosya boyutu ne olursa olsun EOF'ye kadar okumam gerekiyor.
user3358300

3
tam olarak ne file.read()yapar - tüm dosyayı okuyun.
isedev

Yöntem için belgeler read()diyor mu?
Ignacio Vazquez-Abrams

"Hashing nedir?"
Sharif Mamun

Yanıtlar:


142

TL; DR tonlarca bellek kullanmamak için tampon kullanır.

İnanıyorum ki, çok büyük dosyalarla çalışmanın hafıza sonuçlarını göz önünde bulundurduğumuzda, probleminizin özüne iniyoruz . Bu kötü çocuğun 2 gigabaytlık bir dosya için 2 g'lık ram karıştırmasını istemiyoruz, bu yüzden pasztorpisti'nin de belirttiği gibi , bu daha büyük dosyalarla yığınlar halinde uğraşmalıyız!

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()))

Yaptığımız şey, hashlib'in kullanışlı züppe güncelleme yöntemiyle birlikte ilerledikçe bu kötü çocuğun hash'lerimizi 64kb'lik parçalar halinde güncellemektir . Bu şekilde, adamı tek seferde hash etmek için gereken 2 gb'den çok daha az bellek kullanırız!

Bunu şununla test edebilirsiniz:

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

Umarım yardımcı olur!

Ayrıca bunların tümü sağ taraftaki bağlantılı soruda özetlenmiştir: Python'da büyük dosyaların MD5 karmasını alın


Zeyilname!

Genel olarak python yazarken pep-8'i takip etme alışkanlığı kazanmanıza yardımcı olur . Örneğin, python'da değişkenler tipik olarak camelCased değil alt çizgi ile ayrılmıştır. Ama bu sadece stil ve kötü stil okumak zorunda olan insanlar dışında hiç kimse bu şeyleri gerçekten önemsemiyor ... bu kodu yıllar sonra okuyabilirsiniz.


@ranman Merhaba, {0} ". format (sha1.hexdigest ()) kısmını alamadım. Neden sha1.hexdigest () kullanmak yerine onu kullanıyoruz?
Belial

@Belial Ne işe yaramadı? Bunu esas olarak iki hash arasında ayrım yapmak için kullanıyordum ...
Randall Hunt

@ranman Her şey çalışıyor, bunu hiç kullanmadım ve literatürde görmedim. "{0}". Format () ... bilmediğim. :)
Belial

1
Nasıl seçmeliyim BUF_SIZE?
Martin Thoma

1
Bu, shasumikili dosyalar ile aynı sonuçları üretmez. Aşağıda listelenen diğer cevap (hafıza görünümünü kullanan) diğer hashing araçlarıyla uyumludur.
tedivm

61

Bir dosyanın hash değerinin doğru ve verimli hesaplanması için (Python 3'te):

  • 'b'Karakter kodlamasını ve satır bitirme dönüştürme sorunlarını önlemek için dosyayı ikili modda açın (yani dosya moduna ekleyin ).
  • Tüm dosyayı hafızaya okumayın, çünkü bu bir hafıza kaybıdır. Bunun yerine, sırayla bloklar halinde okuyun ve her blok için hash'i güncelleyin.
  • Çift arabelleğe almayı ortadan kaldırın, yani arabelleğe alınmış IO kullanmayın, çünkü zaten optimum blok boyutu kullanıyoruz.
  • readinto()Tampon çalkalamasını önlemek için kullanın .

Misal:

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
Optimum blok boyutunun ne olduğunu nasıl anlarsınız?
Mitar

1
@Mitar, bir alt sınır, fiziksel bloğun (geleneksel olarak daha yeni disklerle 512 bayt veya 4KiB) ve sistem sayfa boyutunun (birçok sistemde 4KiB, diğer yaygın seçenekler: 8KiB ve 64 KiB) maksimumudur. Daha sonra temelde bazı kıyaslamalar yaparsınız ve / veya yayınlanan kıyaslama sonuçlarına ve ilgili çalışmalara bakarsınız (örn. Şu anki rsync / GNU cp / ... neyin kullanıldığını kontrol edin).
maxschlepzig

Misiniz resource.getpagesizebiz biraz dinamik olarak optimize denemek istiyorsa, burada herhangi bir kullanım? Peki ya mmap?
jpmc26

@ jpmc26, getpagesize () burada o kadar kullanışlı değil - ortak değerler 4 KiB veya 8 KiB'dir, bu aralıktaki bir şey, yani 128 KiB - 128 KiB'den çok daha küçük bir şey genellikle iyi bir seçimdir. Dosyanın tamamını önden arkaya sırayla okuduğumuz için mmap kullanım durumumuzda pek yardımcı olmuyor. mmap, erişim örüntüsü daha rastgele erişim olduğunda, örneğin sayfalara birden fazla erişildiğinde ve / veya mmap okuma arabelleği yönetimini basitleştirdiğinde avantajlara sahiptir.
maxschlepzig

3
Hem (1) @Randall Hunt hem de (2) sizin çözümünüzü (bu sırada dosya önbelleği nedeniyle önemlidir) yaklaşık 116GB ve sha1sum algoritması ile karşılaştırdım. Çözüm 1, 20 * 4096 (PAGE_SIZE) tampon kullanmak ve tamponlama parametresini 0'a ayarlamak için değiştirildi. Yalnızca Çözüm 2 algoritması değiştirildi (sha256 -> sha1). Sonuç: (1) 3m37.137s (2) 3d30.003s. İkili modda yerli sha1sum: 3m31.395s
bioinfornatics

18

Ben basitçe şunu öneririm:

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()

Buradaki diğer tüm cevaplar çok karmaşık görünüyor. Python, okurken zaten arabelleğe alıyor (ideal bir şekilde veya temel alınan depolama hakkında daha fazla bilgiye sahipseniz bu arabelleği yapılandırırsanız) ve bu nedenle, hash işlevinin ideal bulduğu parçalar halinde okumak daha iyidir, bu da onu daha hızlı veya en azından daha az CPU yoğun kılar. hash işlevini hesaplayın. Bu nedenle, arabelleğe almayı devre dışı bırakmak ve onu kendiniz taklit etmeye çalışmak yerine, Python arabelleğini kullanır ve neyi kontrol etmeniz gerektiğini kontrol edersiniz: verilerinizin tüketicisinin ideal bulduğu şey, karma blok boyutu.


Mükemmel cevap, ancak ifadelerinizi ilgili belge ile desteklerseniz iyi olur: Python3 - open () ve Python2 - open () . Python3'ün yaklaşımı daha karmaşıktır. Yine de, tüketici merkezli bakış açısını gerçekten takdir ettim!
Murmel

hash.block_size'hash algoritmasının dahili blok boyutu' olarak belgelenmiştir. Hashlib gelmez buluyorum ideali . Paket dokümantasyonundaki hiçbir şey boyutlandırılmış girdinin update()tercih edildiğini göstermez hash.block_size. Böyle adlandırırsanız daha az CPU kullanmaz. Sizin file.read()Yeni öbek nesnesi bayti dosyadan gereksiz birçok nesne kreasyonları ve gereksiz kopyalarına çağrı yol açar tampon.
maxschlepzig

Hash'ler durumlarını block_sizeparçalar halinde günceller . Bunları bu yığınlarda sağlamıyorsanız, arabelleğe almaları ve yeterli verinin görünmesini beklemeleri veya verilen verileri dahili olarak parçalara ayırmaları gerekir. Yani, bunu dışarıdan halledebilir ve sonra içeride olanları basitleştirebilirsiniz. Bunu ideal buluyorum. Örneğin bkz .: stackoverflow.com/a/51335622/252025
Mitar

block_sizeHerhangi kullanışlı okuma boyutlarından çok daha küçüktür. Ayrıca, herhangi bir kullanışlı blok ve okuma boyutu ikinin üsleridir. Bu nedenle, okuma boyutu, muhtemelen sonuncusu hariç tüm okumalar için blok boyutuna bölünebilir. Örneğin, sha256 blok boyutu 64 bayttır. Bu update(), herhangi bir çoklu ara belleğe almadan girdiyi doğrudan işleyebileceği anlamına gelir block_size. Bu nedenle, yalnızca son okuma blok boyutuna bölünemezse, 63 bayta kadar arabelleğe alması gerekir. Dolayısıyla, son yorumunuz yanlış ve cevabınızda yaptığınız iddiaları desteklemiyor.
maxschlepzig

Mesele şu ki, tamponlamayı optimize etmek zorunda değilsiniz çünkü bu okuma sırasında Python tarafından zaten yapılıyor. Dolayısıyla, mevcut arabellek üzerinden hashing yaparken yapmak istediğiniz döngü miktarına karar vermeniz yeterlidir.
Mitar

6

Büyük dosyaları farklı algoritmalarla hash edebilen bir modül programladım.

pip3 install py_essentials

Modülü şu şekilde kullanın:

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

1
Çapraz platform mu (Linux + Win)? Python3 ile çalışıyor mu? Ayrıca hala korunuyor mu?
Basj

Evet, çapraz platformdur ve yine de çalışacaktır. Ayrıca paketteki diğer şeyler iyi çalışıyor. Ama artık bu kişisel deneyler paketini sürdürmeyeceğim, çünkü bu sadece bir geliştirici olarak benim için bir öğrenimdi.
phyyl

5

İşte mmapnesneyi belleğe eşlemek için kullanan bir Python 3, POSIX çözümü (Windows değil!) .

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()

Saf soru ... mmapbu senaryoda kullanmanın avantajı nedir?
Jonathan B.

1
@JonathanB. çoğu yöntem gereksiz yere bytesbellekte nesneler yaratır ve readçok fazla veya çok az kez çağırır . Bu, dosyayı doğrudan sanal belleğe eşler ve buradan hash hale getirir - işletim sistemi, dosya içeriğini doğrudan arabellek önbelleğinden okuma sürecine eşleyebilir. Bu, bu içinde önemli bir faktör daha hızlı olabileceği anlamına geliyor bu bir
Antti Haapala

@JonathanB. Testi yaptım ve bu durumda fark o kadar önemli değil , saf yönteme göre ~% 15'ten bahsediyoruz.
Antti Haapala

-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
Temelde echo $USER_INPUT | md5sum > encrypted.txt && cat encrypted.txt, dosyaların karma işlemi ile uğraşmayan, özellikle de büyük olanlarla ilgilenmiyorsunuz.
Murmel

1
hashing! = şifreleme
bugmenot123
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.