Форум сайта python.su
Пространства для Вашего Я, коллеги!
В свободное время развлекаю себя тем, что потихонечку добиваю набор своих пылесосов. Вот, некоторое время назад, написалась такая функция…
# coding: utf-8
import os as _os
import hashlib as _hashlib
from subprocess import Popen as _Popen
def file_md5(file, use_system = False):
"""file_md5(file, use_system = False) -> md5sum of "file" as hexdigest string.
"file" may be a file name or file object, opened for read.
If "use_system" is True, if possible use system specific program. This ignore, if file object given."""
if isinstance(file, basestring):
if use_system:
sysname = _os.uname()[0]
if sysname in ('Darwin', 'FreeBSD'):
po = _Popen('md5 -q "%s"' % file, shell=True, stdout=-1, stderr=-1)
po.wait()
m = po.stdout.readline().strip()
if len(m) == 32:
return m
# elif sysname == ...
file = open(file, 'rb')
h = _hashlib.md5()
block = file.read(h.block_size)
while block:
h.update(block)
block = file.read(h.block_size)
return h.hexdigest()
"""
fn = "/Users/Shared/Фильмы/Аниме/Evangelion/Neon Genesis Evangelion - 13.avi"
print file_md5(fn)
print file_md5(open(fn, 'rb'))
print file_md5(fn, True)
import os
os.system('md5 -q "%s"' % fn)
"""
Офлайн
Держите под линукс.
Выдержка из man:
Печатает или проверяет контрольные суммы MD5 (128-битные).
-b, --binary читать в двоичном режиме
-t, --text читать в текстовом режиме (по умолчанию)
По умолчанию печатает строку с контрольной суммой, знак, показывающий
тип файла (`*' для двоичных, ` ' для текстовых), и имя каждого ФАЙЛА.
svartalf@svartalf:~$ md5sum -b ~/amarok2.jpeg
c08cce704ad1877a6d62b47e1bf593f2 *~/amarok2.jpeg
svartalf@svartalf:~$ md5sum -t rfc3920.txt
f102c3f9f6e2e45601a083a03d0c4c4c rfc3920.txt
svartalf@svartalf:~$ uname -a
Linux svartalf 2.6.27-11-generic #1 SMP Thu Jan 29 19:24:39 UTC 2009 i686 GNU/Linux
Офлайн
Что-то я идеей не проникся. Попробовал читать весь файл целиком - результаты почти такие же, как при запуске md5sum.
Вот код:
#!/usr/bin/python
import sys
import os
from md5 import md5
from subprocess import Popen
from timeit import Timer
def test1():
po = Popen('md5sum -b "%s"' % sys.argv[1], shell=True, stdout=-1, stderr=-1)
po.wait()
return po.stdout.readline().strip()
def test2():
#h = md5()
#file = open(sys.argv[1], 'rb')
# block = file.read(h.block_size)
# while block:
# h.update(block)
# block = file.read(h.block_size)
# return h.hexdigest()
return md5(open(sys.argv[1], 'rb').read()).hexdigest()
print os.uname()[0]
print sys.argv[1], os.path.getsize(sys.argv[1])
print test1()
print test2()
print Timer("test1()", "from __main__ import test1").timeit(3)
print Timer("test2()", "from __main__ import test2").timeit(3)
Офлайн
SvartalFСенкс… Ещё бы os.uname() узнать… :-)
Держите под линукс.
EdАга, а если надо узнать контрольную сумму, например, образа DVD9? У меня в макбуке всего три гига памяти… :-)
Что-то я идеей не проникся. Попробовал читать весь файл целиком - результаты почти такие же, как при запуске md5sum.
EdТоже сенкс. Но уже не важно.
PS: Ваш код не будет работать в 2.4. Там в объекте md5 нет block_size.
def file_md5(file, use_system=False, block_size=1024):
"""file_md5(file, use_system = False) -> md5sum of "file" as hexdigest string.
"file" may be a file name or file object, opened for read.
If "use_system" is True, if possible use system specific program. This ignore, if file object given."""
if isinstance(file, basestring):
if use_system:
sysname = _os.uname()[0]
if sysname in ('Darwin', 'FreeBSD'):
po = _Popen('md5 -q "%s"' % file, shell=True, stdout=-1, stderr=-1)
po.wait()
m = po.stdout.readline().strip()
if len(m) == 32:
return m
elif sysname == "Linux2":
po = _Popen('md5sum -b "%s"' % file, shell=True, stdout=-1, stderr=-1)
po.wait()
m = po.stdout.readline().strip()
if len(m) > 32:
return m[:32]
# elif sysname == ...
file = open(file, 'rb')
h = _hashlib.md5()
block = file.read(block_size)
while block:
h.update(block)
block = file.read(block_size)
return h.hexdigest()
Отредактировано (Фев. 17, 2009 12:24:38)
Офлайн
os.uname() должен быть Linux, а не Linux2
Это видно из моего предыдущего поста.
В остальном все правильно. по крайней мере работает на моем линухе.
А почему вы используете shell=True, если не секрет? Вроде на *nix оно False по-умолчанию, что в общем-то и правильно и дешевле.
Офлайн
не понимаю, зачем popen ?
чем стандартный md5 плох ?
а ещё имхо слишком маленький block_size минимум 8 килобайт
Отредактировано (Фев. 17, 2009 15:33:44)
Офлайн
Офлайн
EdСорри за невнимательность. Спать надо больше.
os.uname() должен быть Linux, а не Linux2
Это видно из моего предыдущего поста.
EdУ меня в MacOS X оно с shell=False отказывается работать. А разбираться почему я полгода назад так написал… Спать надо… Больше… :-)
А почему вы используете shell=True, если не секрет? Вроде на *nix оно False по-умолчанию, что в общем-то и правильно и дешевле.
evgenylВ смысле такой, как при use_system=False?
не понимаю, зачем popen ?
чем стандартный md5 плох ?
slav0nicНу это почти тоже такое, что и написал я, только без отсылке к системе.
http://code.activestate.com/recipes/266486/
Офлайн
ZZZалгоритм расчета md5 очень прост и никоем образом не упирается в процесор, узкое место диск, так что лучше делать очередь и считать их не паралельно, так ты не убьеш сервер и добьешся оптимальной загрузки дисковой подсистемы
В смысле такой, как при use_system=False?
Мысль первая: скорость. Но эта мысль явно устарела, так как при 14 (?!) килобайтном буфере оно работает быстрее, чем мой макосный md5. 1.3s против 1.9s.
Мысль вторая: параллельность. Что там у нас с GIL? Сколько ядер я загружу при одновременном вычислении нескольких хэшей? А с помощью использования отдельных процессов я загружу их все. Поправте уж, если я ошибаюсь. Дело в том, что сейчас это планируется использовать на сервере, где контрольные суммы больших и не очень файлов будут вычисляться с завидной регулярностью.
Офлайн
Вот об этом я тоже думал. Поэтому позможно сделаю более динамичный код подсчёта. На питоне. Но заниматься такой оптимизацией раньше времени не стоит. Как, впрочем, и любой другой.
А сейчас просто хотелось сделать как-нить красиво и в общем удалось. Я доволен. А так… Это просто вылысыпэд и пылысос в одном флаконе.
Обратите внимание на алгоритм выделения контрольой суммы из md5sum. Мне кажется, что это самый простой подход, удовлетворяющий принципу KISS, но всё-таки, с высокой долей вероятности, отлавливающий ошибку самого md5sum.
# coding: utf-8
import os as _os
import hashlib as _hashlib
from subprocess import Popen as _Popen
def file_md5(file, use_system=False, block_size=1024*14):
"""file_md5(file, use_system = False) -> md5sum of "file" as hexdigest string.
"file" may be a file name or file object, opened for read.
If "use_system" is True, if possible use system specific program. This ignore, if file object given.
"block_size" -- size in bytes buffer for calc md5. Used with "use_system=False"."""
if isinstance(file, basestring):
if use_system:
sysname = _os.uname()[0]
if sysname in ('Darwin', 'FreeBSD'):
po = _Popen('md5 -q "%s"' % file, shell=True, stdout=-1, stderr=-1)
po.wait()
m = po.stdout.readline().strip()
if len(m) == 32:
return m
else:
print "Warning: md5 on %s is error" % sysname
elif sysname == "Linux":
po = _Popen('md5sum -b "%s"' % file, shell=True, stdout=-1, stderr=-1)
po.wait()
m = po.stdout.readline().strip()
# if len(m) > 32:
if m.find(' ') == 32:
return m[:32]
else:
print "Warning: md5sum on Linux is error"
# elif sysname == ...
file = open(file, 'rb')
h = _hashlib.md5()
block = file.read(block_size)
while block:
h.update(block)
block = file.read(block_size)
return h.hexdigest()
Офлайн