Générer et appliquer des diffs en python

Existe-t-il un moyen 'prêt à l'emploi' en python de générer une liste de différences entre deux textes, puis d'appliquer ce diff à un fichier pour obtenir l'autre, plus tard?

Je veux conserver l'historique des révisions d'un texte, mais je ne veux pas enregistrer le texte entier pour chaque révision s'il n'y a qu'une seule ligne éditée. J'ai regardé difflib , mais je ne pouvais pas voir comment générer une liste de seulement les lignes éditées qui peuvent encore être utilisées pour modifier un texte pour obtenir l'autre.

25
demandé sur ire_and_curses 2010-02-22 00:13:09

6 réponses

Avez-vous regardé diff-match-patch de google? Apparemment, google Docs utilise cet ensemble d'algorithmes. Il comprend non seulement un module diff, mais aussi un module patch, de sorte que vous pouvez générer le fichier le plus récent à partir de fichiers et de diffs plus anciens.

Une version python est incluse.

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

26
répondu Density 21.5 2010-02-21 22:39:28

Fait difflib.unified_diff voulez-vous voulez? Il y a un exemple ici .

10
répondu pwdyson 2010-02-21 22:39:35

AFAIK la plupart des algorithmes de diff utilisent une correspondance simple la plus longue sous-séquence commune , pour trouver la partie commune entre deux textes et tout ce qui reste est considéré comme la différence. Il ne devrait pas être trop difficile de coder votre propre algorithme de programmation dynamique pour y parvenir en python, la page wikipedia ci-dessus fournit également l'algorithme.

2
répondu jai 2010-02-21 21:34:57

Est-ce que ce doit être une solution python?
Mes premières pensées quant à une solution serait d'utiliser soit un système de contrôle de Version (Subversion, Git, etc.) ou diff / patch utilitaires qui sont standard avec un système unix, ou font partie de cygwin pour un système Windows.

1
répondu Simon Callan 2010-02-21 21:18:06

J'ai implémenté une fonction Python pure pour appliquer des correctifs diff pour récupérer l'une des chaînes d'entrée, j'espère que quelqu'un le trouvera utile. Il utilise analyse le format Unified diff .

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

S'il y a des lignes d'en-tête ("--- ...\n","+++ ...\n"), il les ignore. Si nous avons une chaîne de diff unifiée diffstr représentant le diff entre oldstr et newstr:

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

En Python, vous pouvez générer un diff unifié de deux chaînes en utilisant difflib (partie de la norme bibliothèque):

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])

Sous unix: diff -U0 a.txt b.txt

Le Code est sur GitHub ici avec des tests utilisant des caractères unicode ASCII et aléatoires: https://gist.github.com/noporpoise/16e731849eb1231e86d78f9dfeca3abc

1
répondu Isaac Turner 2016-12-05 04:55:09

Vous pouvez probablement utiliser unified_diff pour générer la liste des différences dans un fichier. Seuls les textes modifiés dans votre fichier peuvent être écrits dans un nouveau fichier texte où vous pouvez l'utiliser pour votre référence future. C'est le code qui vous aide à écrire seulement la différence pour votre nouveau fichier. J'espère que c'est ce que vous demandez !

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 

Utilisez ceci dans votre code pour enregistrer uniquement la sortie de différence !

0
répondu Karthik Hegde 2016-03-09 11:13:15