2009-12-08 6 views
45

Próbuję obliczyć wartość SHA-1 pliku.Dlaczego Python obliczył "hashlib.sha1" inny niż "git hash-object" dla pliku?

mam sfabrykowane ten skrypt:

def hashfile(filepath): 
    sha1 = hashlib.sha1() 
    f = open(filepath, 'rb') 
    try: 
     sha1.update(f.read()) 
    finally: 
     f.close() 
    return sha1.hexdigest() 

Dla konkretnego pliku otrzymuję tę wartość hash:
8c3e109ff260f7b11087974ef7bcdbdc69a0a3b9
Ale kiedy obliczyć wartość z git hash_object, następnie uzyskać tę wartość: d339346ca154f6ed9e92205c3c5c38112e761eb7

Dlaczego oni się różnią? Czy robię coś złego, czy mogę po prostu zignorować różnicę?

+2

Nie można tak naprawdę zignorować różnicę, jeśli planujesz używać skrótów razem. –

+0

Zapomniałem wspomnieć, po prostu użyłem gita jako wzorca, nie zamierzam ich używać razem. – Ikke

+1

Jeśli plik może być dość duży, możesz przetworzyć go w bloku na raz, więc nie potrzebujesz całej rzeczy w pamięci RAM na raz: http://stackoverflow.com/questions/7829499/using-hashlib-to- compute-md5-digest-of-a-file-in-python3 – rakslice

Odpowiedz

51

git oblicza mieszań tak:

sha1("blob " + filesize + "\0" + data) 

Reference

+0

Powinienem był to sprawdzić, dzięki. – Ikke

+0

Nie ma problemu, odnośnik jest całkiem inny, po prostu znalazł go przy odrobinie szczęścia. –

+13

Należy wspomnieć, że git robi to, aby uniknąć ataków przedłużania długości. – Omnifarious

31

Dla porównania, tutaj jest bardziej zwięzła wersja:

def sha1OfFile(filepath): 
    import hashlib 
    with open(filepath, 'rb') as f: 
     return hashlib.sha1(f.read()).hexdigest() 

Na drugim myśli: chociaż nigdy nie widziałem go, Myślę, że istnieje potencjał, aby f.read() zwrócił mniej niż pełny plik lub plik o wielu gigabajtach, aby f.read() zabrakło pamięci. Dla każdego zbudowaniu, rozważmy, jak to naprawić: Pierwsza poprawka do tego jest:

def sha1OfFile(filepath): 
    import hashlib 
    sha = hashlib.sha1() 
    with open(filepath, 'rb') as f: 
     for line in f: 
      sha.update(line) 
     return sha.hexdigest() 

Jednakże, nie ma gwarancji, że '\n' pojawia się w pliku w ogóle, więc fakt, że pętla for dadzą nam bloki pliku, który kończy się '\n' może dać nam ten sam problem, który mieliśmy pierwotnie. Niestety, nie widzę podobnego Pythonowskiego sposobu na przerysowywanie bloków pliku tak dużych, jak to tylko możliwe, co, jak sądzę, oznacza, że ​​utknęliśmy w pętli while True: ... break i magiczną liczbą dla rozmiaru bloku:

def sha1OfFile(filepath): 
    import hashlib 
    sha = hashlib.sha1() 
    with open(filepath, 'rb') as f: 
     while True: 
      block = f.read(2**10) # Magic number: one-megabyte blocks. 
      if not block: break 
      sha.update(block) 
     return sha.hexdigest() 

Oczywiście, kto powiedział, że możemy przechowywać ciągi jednomega megabajtowe. Prawdopodobnie możemy, ale co, jeśli jesteśmy na małym wbudowanym komputerze?

Żałuję, że nie mogę wymyślić czystszego sposobu, który gwarantuje, że nie zabraknie pamięci na ogromnych plikach i który nie ma liczb magicznych i który działa tak samo, jak oryginalne proste rozwiązanie Pythonic.

+0

Po drugie, może to powodować problemy, jeśli f.read() nie może zwrócić całego pliku (np. W przypadku plików wielogigabajtowych), więc prawdopodobnie powinien on iterować po porcjach. – Ben