7

У меня есть проблема для сравнения двух файлов. В общем, что я хочу сделать, это UNIX-подобных различий между двумя файлами, например:Создать «нечеткое» различие двух файлов в Python с приблизительным сравнением поплавков

$ Diff -u левый файл правой кнопкой файл

Однако мои два файла содержат поплавки; и поскольку эти файлы были сгенерированы на разных архитектурах (но вычисляя одни и те же вещи), плавающие значения не совсем одинаковы (они могут отличаться, например, от 1e-10). Но то, что я пытаюсь «отличить» файлы, - это найти то, что я считаю существенными различиями (например, разница больше 1е-4); при использовании команды diff UNIX, я получаю почти все мои строки, содержащие значения плавающей переменной! Это моя проблема: как я могу получить полученный diff, например, diff -u, но с меньшими ограничениями относительно сравнения поплавков?

Мне показалось, что я написал скрипт Python для этого и узнал модуль difflib, который обеспечивает сравнение сравнения. Но документация, которую я нашел, объясняет, как использовать ее как есть (с помощью одного метода) и объясняет внутренние объекты, но я не могу найти ничего о том, как настроить объект difflib для удовлетворения моих потребностей (например, переписывать только метод сравнения или такие) ... Я думаю, что решение может состоять в том, чтобы извлечь унифицированную разницу и проанализировать ее вручную, чтобы удалить мои «ложные» различия, поскольку это не изящно; Я предпочел бы использовать уже существующую структуру.

Итак, кто-нибудь знает, как настроить эту библиотеку, чтобы я мог делать то, что искал? Или, по крайней мере, указать мне в правильном направлении ... Если бы не Python, возможно, сценарий оболочки мог бы работать?

Любая помощь была бы принята с благодарностью! Заранее благодарю за ваши ответы!

+1

Может быть, вы также как: [модули Хороший Python для нечеткого сравнения строк ?] (http://stackoverflow.com/questions/682367/good-python-modules-for-fuzzy-string-comparison) – miku

+0

более простой альтернативой является предварительная обработка файлов и форматирование поплавков до нужной точности –

+0

Пожалуйста, разместите пару соответствующих строк из входных файлов с примерами? – smci

ответ

4

В вашем случае мы специализируемся на general case: прежде чем мы передадим вещи в difflib, нам необходимо обнаружить и отдельно обрабатывать строки, содержащие поплавки. Вот базовый подход, если вы хотите сгенерировать дельт, строки контекста и т. Д., Вы можете построить на этом. Обратите внимание, что проще нечеткие-сравнивать поплавки как фактические поплавки, а не строки (хотя вы можете кодировать столбцы за столбцом, и игнорировать символы после 1-e4).

import re 

float_pat = re.compile('([+-]?\d*\.\d*)') 
def fuzzydiffer(line1,line2): 
    """Perform fuzzy-diff on floats, else normal diff.""" 
    floats1 = float_pat.findall(line1) 
    if not floats1: 
     pass # run your usual diff() 
    else: 
     floats2 = float_pat.findall(line2) 
     for (f1,f2) in zip(floats1,floats2): 
      (col1,col2) = line1.index(f1),line2.index(f2) 
      if not fuzzy_float_cmp(f1,f2): 
       print "Lines mismatch at col %d", col1, line1, line2 
      continue 
    # or use a list comprehension like all(fuzzy_float_cmp(f1,f2) for f1,f2 in zip(float_pat.findall(line1),float_pat.findall(line2))) 
    #return match 

def fuzzy_float_cmp(f1,f2,epsilon=1e-4): 
    """Fuzzy-compare two strings representing floats.""" 
    float1,float2 = float(f1),float(f2) 
    return (abs(float1-float2) < epsilon) 

Некоторые тесты:

fuzzydiffer('text: 558.113509766 +23477547.6407 -0.867086648057 0.009291785451', 
'text: 558.11351 +23477547.6406 -0.86708665 0.009292000001') 

и в качестве бонуса, вот версия, которая выдвигает на первый план-столбцы различия:

import re 

float_pat = re.compile('([+-]?\d*\.\d*)') 
def fuzzydiffer(line1,line2): 
    """Perform fuzzy-diff on floats, else normal diff.""" 
    floats1 = float_pat.findall(line1) 
    if not floats1: 
     pass # run your usual diff() 
    else: 
     match = True 
     coldiffs1 = ' '*len(line1) 
     coldiffs2 = ' '*len(line2) 
     floats2 = float_pat.findall(line2) 
     for (f1,f2) in zip(floats1,floats2): 
      (col1s,col2s) = line1.index(f1),line2.index(f2) 
      col1e = col1s + len(f1) 
      col2e = col2s + len(f2) 
      if not fuzzy_float_cmp(f1,f2): 
       match = False 
       #print 'Lines mismatch:' 
       coldiffs1 = coldiffs1[:col1s] + ('v'*len(f1)) + coldiffs1[col1e:] 
       coldiffs2 = coldiffs2[:col2s] + ('^'*len(f2)) + coldiffs2[col2e:] 
      #continue # if you only need to highlight first mismatch 
     if not match: 
      print 'Lines mismatch:' 
      print ' ', coldiffs1 
      print '< ', line1 
      print '> ', line2 
      print ' ', coldiffs2 
     # or use a list comprehension like 
     # all() 
     #return True 

def fuzzy_float_cmp(f1,f2,epsilon=1e-4): 
    """Fuzzy-compare two strings representing floats.""" 
    print "Comparing:", f1, f2 
    float1,float2 = float(f1),float(f2) 
    return (abs(float1-float2) < epsilon)