Generazione e l'applicazione di diff in pitone
Domanda
C'è un modo 'out-of-the-box' in python per generare un elenco di differenze tra i due testi, e quindi l'applicazione di questo diff in un unico file per ottenere l'altra, più tardi?
Voglio mantenere la cronologia di revisione di un testo, ma io non voglio salvare l'intero testo per ogni revisione se v'è solo una singola linea modificato. Ho guardato difflib , ma non ho potuto vedere come generare un elenco di solo il linee modificati che possono ancora essere utilizzati per modificare un testo per ottenere l'altro.
Soluzione
Hai uno sguardo al diff match-patch da Google? A quanto pare Google Documenti utilizza questo insieme di algoritmi. Esso comprende non solo un modulo diff, ma anche un modulo di patch, in modo da poter generare il file più recente dai file più grandi e diff.
Una versione di Python è incluso.
Altri suggerimenti
La difflib.unified_diff voglio che si desidera? C'è un esempio qui .
AFAIK maggior parte degli algoritmi diff usano un semplice corrispondenza più lunga Comune Subsequence , per trovare la parte comune tra i due testi e tutto ciò che è sinistra è considerata la differenza. Non dovrebbe essere troppo difficile da codificare il proprio algoritmo di programmazione dinamica per realizzare questo in python, la pagina di wikipedia sopra fornisce l'algoritmo di troppo.
Ho implementato una funzione di puro Python per applicare le patch diff di recuperare uno dei due stringhe di input, spero che qualcuno lo trova utile. Esso utilizza analizza i href="https://en.wikipedia.org/wiki/Diff_utility#Unified_format" unificata formato 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
Se ci sono linee di intestazione ("--- ...\n","+++ ...\n")
salta su di loro. Se abbiamo un diffstr
stringa di diff unificato che rappresenta la diff tra oldstr
e newstr
:
# recreate `newstr` from `oldstr`+patch
newstr = apply_patch(oldstr, diffstr)
# recreate `oldstr` from `newstr`+patch
oldstr = apply_patch(newstr, diffstr, True)
In Python è possibile generare un diff unificato di due stringhe con difflib (parte della libreria 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])
Su UNIX: diff -U0 a.txt b.txt
Codice è su GitHub qui con test utilizzando ASCII e caratteri Unicode casuali: https: // Gist. github.com/noporpoise/16e731849eb1231e86d78f9dfeca3abc
Essa non deve essere una soluzione pitone?
I miei primi pensieri come ad una soluzione potrebbe essere quella di utilizzare un sistema di controllo di versione (Subversion, Git, ecc) o le utilità diff
/ patch
che sono standard con un sistema Unix, o fanno parte di cygwin
per un Windows sistema basato.
Probabilmente è possibile utilizzare unified_diff per generare l'elenco di differenza in un file . i testi modificati nel file possono essere scritti solo in un nuovo file di testo in cui è possibile utilizzare per il vostro riferimento futuro. Questo è il codice che vi aiuta a scrivere solo la differenza per il nuovo file. Spero che questo è ciò che stai chiedendo!
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
Utilizza questo nel codice di salvare solo l'uscita differenza!