2013-04-03 5 views
2

У меня есть два двоичных файла. Они выглядят примерно так, но данные более случайна:Diffing Binary Files В Python

Файл A:

FF FF FF FF 00 00 00 00 FF FF 44 43 42 41 FF FF ... 

Файл B:

41 42 43 44 00 00 00 00 44 43 42 41 40 39 38 37 ... 

То, что я хотел бы, чтобы назвать что-то вроде:

>>> someDiffLib.diff(file_a_data, file_b_data) 

И получить что-то вроде:

[Match(pos=4, length=4)] 

Указывает, что в обоих файлах байты в позиции 4 одинаковы для 4 байтов. Последовательность 44 43 42 41 не будет соответствовать, потому что они не находятся в одинаковых положениях в каждом файле.

Есть ли библиотека, которая будет делать разницу для меня? Или я должен просто написать циклы для сравнения?

+0

http://docs.python.org/2/library/difflib.html - первый результат в Google для "дифф в питон" – Andrey

+0

возможно дубликат [разницу между двумя строками в Python/PHP] (HTTP : //stackoverflow.com/questions/1209800/difference-between-two-strings-in-python-php) – Andrey

+0

@ Андрей спасибо, я пробовал это, но похоже, что 'get_matching_blocks()' не проверяет, находятся в одном месте в каждом файле, только что последовательность существует в каждом файле. В противном случае, да, это в значительной степени то, что я хочу. – omghai2u

ответ

10

Вы можете использовать itertools.groupby() для этого, вот пример:

from itertools import groupby 

# this just sets up some byte strings to use, Python 2.x version is below 
# instead of this you would use f1 = open('some_file', 'rb').read() 
f1 = bytes(int(b, 16) for b in 'FF FF FF FF 00 00 00 00 FF FF 44 43 42 41 FF FF'.split()) 
f2 = bytes(int(b, 16) for b in '41 42 43 44 00 00 00 00 44 43 42 41 40 39 38 37'.split()) 

matches = [] 
for k, g in groupby(range(min(len(f1), len(f2))), key=lambda i: f1[i] == f2[i]): 
    if k: 
     pos = next(g) 
     length = len(list(g)) + 1 
     matches.append((pos, length)) 

Или то же самое, как описано выше, используя список понимание:

matches = [(next(g), len(list(g))+1) 
      for k, g in groupby(range(min(len(f1), len(f2))), key=lambda i: f1[i] == f2[i]) 
       if k] 

Здесь установка для примера, если вы используют Python 2.x:

f1 = ''.join(chr(int(b, 16)) for b in 'FF FF FF FF 00 00 00 00 FF FF 44 43 42 41 FF FF'.split()) 
f2 = ''.join(chr(int(b, 16)) for b in '41 42 43 44 00 00 00 00 44 43 42 41 40 39 38 37'.split()) 
+0

Горячая. Мне нравится то, что вы там делаете. Я надеялся на такой красивый ответ. – omghai2u

3

Предоставленный itertools.groupbysolution отлично работает, но это довольно медленно.

Я написал довольно наивную попытку с использованием numpy и протестировал ее по сравнению с другим решением на конкретном 16-мегабайтном файле, который у меня был, и он был примерно в 42 раза быстрее на моей машине. Кто-то, знакомый с numpy, может значительно улучшить это.

import numpy as np 

def compare(path1, path2): 
    x,y = np.fromfile(path1, np.int8), np.fromfile(path2, np.int8) 
    length = min(x.size, y.size) 
    x,y = x[:length], y[:length] 

    z = np.where(x == y)[0] 
    if(z.size == 0) : return z 

    borders = np.append(np.insert(np.where(np.diff(z) != 1)[0] + 1, 0, 0), len(z)) 
    lengths = borders[1:] - borders[:-1] 
    starts = z[borders[:-1]] 
    return np.array([starts, lengths]).T