Générer et d'appliquer diffs en python
Question
Y at-il un « out-of-the-box » façon python pour générer une liste des différences entre les deux textes, puis l'application de cette diff dans un fichier pour obtenir l'autre, plus tard?
Je veux garder l'historique de révision d'un texte, mais je ne veux pas sauver s'il y a une seule ligne a modifié le texte entier pour chaque révision. Je regardais difflib , mais je ne pouvais pas voir comment générer une liste de seulement la les lignes modifiées qui peuvent encore être utilisés pour modifier une texte pour obtenir l'autre.
La solution
Avez-vous un coup d'oeil au diff match-patch de google? Apparantly Google Docs utilise cet ensemble de algoritms. Il comprend non seulement un module de diff, mais aussi un module de patch, de sorte que vous pouvez générer le fichier le plus récent à partir de fichiers anciens et diffs.
Une version python est inclus.
Autres conseils
algorithmes utilisent diff la plupart afaik simple match de Longest Common Subsequence, pour trouver la partie commune entre deux textes et ce qui reste est considéré comme la différence. Il ne devrait pas être trop difficile à coder votre propre algorithme de programmation dynamique pour y parvenir en python, la page wikipedia ci-dessus fournit l'algorithme aussi.
J'ai mis en place une fonction pure python pour appliquer des patchs diff pour récupérer ou l'autre des chaînes d'entrée, j'espère que quelqu'un juge utile. Il utilise les format diff unifié .
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 saute sur eux. Si nous avons une diffstr
chaîne diff unifiée représentant la 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 bibliothèque standard):
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])
Sur unix: diff -U0 a.txt b.txt
Le code est sur GitHub ici ainsi que des tests en utilisant les caractères ASCII et unicode aléatoires: https: // essentiel. github.com/noporpoise/16e731849eb1231e86d78f9dfeca3abc
At-il être une solution python?
Mes premières pensées quant à une solution serait d'utiliser un système de contrôle de version (de Subversion, Git, etc.) ou les services publics diff
/ patch
qui sont standard avec un système unix, ou font partie de cygwin
pour un système Windows à base.
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 peut être écrit dans un nouveau fichier texte dans lequel vous pouvez l'utiliser pour votre référence future. Voici le code qui vous permet d'écrire que la différence à 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 dans votre code pour enregistrer uniquement la sortie de différence!