Pergunta

Existe uma maneira 'pronta para uso' em Python para gerar uma lista de diferenças entre dois textos e, em seguida, aplicar esse diff em um arquivo para obter o outro, mais tarde?

Quero manter o histórico de revisão de um texto, mas não quero salvar o texto inteiro para cada revisão se houver apenas uma única linha editada. Eu olhei para difflib, mas não consegui ver como gerar uma lista apenas das linhas editadas que ainda podem ser usadas para modificar um texto para obter o outro.

Foi útil?

Solução

Você deu uma olhada no Diff-Match-Patch do Google? Aparentemente, o Google Docs usa esse conjunto de algorits. Ele inclui não apenas um módulo DIFF, mas também um módulo de patch, para que você possa gerar o arquivo mais recente a partir de arquivos e diffs mais antigos.

Uma versão python está incluída.

http://code.google.com/p/google-diff-match-patch/

Outras dicas

Difflib.unified_diff deseja que você queira? Existe um exemplo aqui.

Afaik mais algoritmos diff usa um simples Subseqüência comum mais longa Combine, para encontrar a parte comum entre dois textos e o que resta é considerado a diferença. Não deve ser muito difícil codificar seu próprio algoritmo de programação dinâmica para realizar isso no Python, a página da Wikipedia acima também fornece o algoritmo.

Eu implementei uma função python pura para aplicar patches diff para recuperar qualquer uma das seqüências de entrada, espero que alguém ache isso útil. Ele usa parses o Formato de diferença unificada.

import re

_hdr_pat = re.compile("^@@ -(\d+),?(\d+)? \+(\d+),?(\d+)? @@$")

def apply_patch(s,patch,revert=False):
  """
  Apply unified diff patch to string s to recover newer string.
  If revert is True, treat s as the newer string, recover older string.
  """
  s = s.splitlines(True)
  p = patch.splitlines(True)
  t = ''
  i = sl = 0
  (midx,sign) = (1,'+') if not revert else (3,'-')
  while i < len(p) and p[i].startswith(("---","+++")): i += 1 # skip header lines
  while i < len(p):
    m = _hdr_pat.match(p[i])
    if not m: raise Exception("Cannot process diff")
    i += 1
    l = int(m.group(midx))-1 + (m.group(midx+1) == '0')
    t += ''.join(s[sl:l])
    sl = l
    while i < len(p) and p[i][0] != '@':
      if i+1 < len(p) and p[i+1][0] == '\\': line = p[i][:-1]; i += 2
      else: line = p[i]; i += 1
      if len(line) > 0:
        if line[0] == sign or line[0] == ' ': t += line[1:]
        sl += (line[0] != sign)
  t += ''.join(s[sl:])
  return t

Se houver linhas de cabeçalho ("--- ...\n","+++ ...\n") Ele pula sobre eles. Se tivermos uma string de diff unified diffstr representando o diferencial entre oldstr e newstr:

# recreate `newstr` from `oldstr`+patch
newstr = apply_patch(oldstr, diffstr)
# recreate `oldstr` from `newstr`+patch
oldstr = apply_patch(newstr, diffstr, True)

Em Python, você pode gerar um diferencial unificado de duas cordas usando difflib (parte da biblioteca padrão):

import difflib
_no_eol = "\ No newline at end of file"

def make_patch(a,b):
  """
  Get unified string diff between two strings. Trims top two lines.
  Returns empty string if strings are identical.
  """
  diffs = difflib.unified_diff(a.splitlines(True),b.splitlines(True),n=0)
  try: _,_ = next(diffs),next(diffs)
  except StopIteration: pass
  return ''.join([d if d[-1] == '\n' else d+'\n'+_no_eol+'\n' for d in diffs])

No Unix: diff -U0 a.txt b.txt

O código está no github aqui, juntamente com os testes usando caracteres ASCII e Unicode aleatórios: https://gist.github.com/noporpoise/16e731849EB1231E86D78F9DFECA3ABC

Tem que ser uma solução Python?
Meus primeiros pensamentos sobre uma solução seria usar um sistema de controle de versão (subversão, git etc.) ou o diff / patch utilitários padrão com um sistema UNIX ou fazem parte de cygwin Para um sistema baseado no Windows.

Provavelmente você pode usar unified_diff Para gerar a lista de diferença em um arquivo. Somente os textos alterados em seu arquivo podem ser gravados em um novo arquivo de texto, onde você pode usá -lo para sua referência futura. Este é o código que ajuda você a escrever apenas a diferença no seu novo arquivo. Espero que seja isso que você está pedindo!

diff = difflib.unified_diff(old_file, new_file, lineterm='')
    lines = list(diff)[2:]
    # linesT = list(diff)[0:3]
    print (lines[0])
    added = [lineA for lineA in lines if lineA[0] == '+']


    with open("output.txt", "w") as fh1:
     for line in added:
       fh1.write(line)
    print '+',added
    removed = [lineB for lineB in lines if lineB[0] == '-']
    with open("output.txt", "a") as fh1:
     for line in removed:
       fh1.write(line)
    print '-',removed 

Use isso no seu código para salvar apenas a saída da diferença!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top