Domanda

che stavo cercando di fare qualcosa di simile a quello che io credo sistemi di controllo cambiamento fanno, confrontare due file, e salvare una piccola diff ogni volta che il file cambia. Ho letto questa pagina: http://docs.python.org/library/difflib.html e non è affondare nella mia testa a quanto pare.

I stava cercando di ricreare questo in maniera un po semplice programma riportato di seguito, ma la cosa che mi sembra mancare è che i Delta contengono almeno tanto quanto il file originale, e altro ancora.

Non è possibile raggiungere solo le modifiche puri? Il motivo che mi chiedo è ovvio si spera - per risparmiare spazio su disco
. Solo potessi salvare l'intero pezzo di codice ogni volta, ma sarebbe meglio risparmiare codice corrente una volta, poi piccoli diff dei cambiamenti.

Inoltre sto ancora cercando di capire il motivo per cui molte funzioni difflib restituiscono un generatore invece di una lista, qual è il vantaggio c'è?

lavorerà difflib per me - o ho bisogno di trovare un pacchetto più professionale con più funzioni?

# Python Difflib demo 
# Author: Neal Walters 
# loosely based on http://ahlawat.net/wordpress/?p=371
# 01/17/2011 

# build the files here - later we will just read the files probably 
file1Contents="""
for j = 1 to 10: 
   print "ABC"
   print "DEF" 
   print "HIJ"
   print "JKL"
   print "Hello World"
   print "j=" + j 
   print "XYZ"
"""

file2Contents = """
for j = 1 to 10: 
   print "ABC"
   print "DEF" 
   print "HIJ"
   print "JKL"
   print "Hello World"
   print "XYZ"
print "The end"
"""

filename1 = "diff_file1.txt" 
filename2 = "diff_file2.txt" 

file1 = open(filename1,"w") 
file2 = open(filename2,"w") 

file1.write(file1Contents) 
file2.write(file2Contents) 

file1.close()
file2.close() 
#end of file build 

lines1 = open(filename1, "r").readlines()
lines2 = open(filename2, "r").readlines()

import difflib

print "\n FILE 1 \n" 
for line in lines1:
  print line 

print "\n FILE 2 \n" 
for line in lines2: 
  print line 

diffSequence = difflib.ndiff(lines1, lines2) 

print "\n ----- SHOW DIFF ----- \n" 
for i, line in enumerate(diffSequence):
    print line

diffObj = difflib.Differ() 
deltaSequence = diffObj.compare(lines1, lines2) 
deltaList = list(deltaSequence) 

print "\n ----- SHOW DELTALIST ----- \n" 
for i, line in enumerate(deltaList):
    print line



#let's suppose we store just the diffSequence in the database 
#then we want to take the current file (file2) and recreate the original (file1) from it
#by backward applying the diff 

restoredFile1Lines = difflib.restore(diffSequence,1)  # 1 indicates file1 of 2 used to create the diff 

restoreFileList = list(restoredFile1Lines)

print "\n ----- SHOW REBUILD OF FILE1 ----- \n" 
# this is not showing anything! 
for i, line in enumerate(restoreFileList): 
    print line

Grazie!

UPDATE:

contextDiffSeq = difflib.context_diff(lines1, lines2) 
contextDiffList = list(contextDiffSeq) 

print "\n ----- SHOW CONTEXTDIFF ----- \n" 
for i, line in enumerate(contextDiffList):
    print line

----- MOSTRA CONTEXTDIFF -----




* 5,9 **

 print "HIJ"

 print "JKL"

 print "Hello World"
  • stampare "j =" + j

    print "XYZ"

--- 5,9 ----

 print "HIJ"

 print "JKL"

 print "Hello World"

 print "XYZ"
  • stampare "La fine"

Un altro aggiornamento:

Ai vecchi tempi di Panvalet un bibliotecario, strumenti di gestione di origine per il mainframe, è possibile creare un insieme di modifiche in questo modo:

++ADD 9
   print "j=" + j 

Il che significa semplicemente aggiungere una riga (o righe) dopo la linea 9. Poi le parole c'è parola come UPDATE ++ sostituire o ++. http://www4.hawaii.gov/dags/icsd/ppmo /Stds_Web_Pages/pdf/it110401.pdf

È stato utile?

Soluzione

Diffs deve contenere informazioni sufficienti per consentire di patch per una versione in un altro, quindi sì, per l'esperimento di un cambiamento a linea singola per un piccolo documento, l'archiviazione dei documenti integrali potrebbe essere più conveniente.

Le funzioni di libreria restituiscono iteratori per rendere più facile per i clienti che sono stretti in memoria o solo bisogno di guardare parte della sequenza risultante. Va bene in Python, perché ogni iteratore può essere convertito in un elenco con una breve espressione list(an_iterator).

La maggior parte di differenziazione viene effettuata su linee di testo, ma è possibile scendere al char-by-char, e difflib lo fa. Date un'occhiata alla Differ classe di oggetti in difflib.

Gli esempi sopra tutti l'uscita human-friendly posto uso, ma il diff sono gestiti internamente in un modo molto più compatto, computer-friendly. Inoltre, diff di solito contengono informazioni ridondanti (come il testo di una linea da cancellare) per rendere patch e fusione modifiche al sicuro. La ridondanza può essere rimosso con il proprio codice, se ti trovi bene con quello.

Ho appena letto che opta per difflib meno sorpresa a favore di ottimalità, che è qualcosa che non voglio argomentare contro. Ci sono noto algoritmi che sono veloci a produrre un insieme minimo di cambiamenti.

Una volta ho codificato un motore diffing generico insieme con uno degli algoritmi ottimali in circa 1250 linee di Java ( i CCR ). Funziona per qualsiasi sequenza di elementi che possono essere paragonato per l'uguaglianza. Se si vuole costruire la vostra soluzione, penso che una traduzione / reimplementazione dei CCR non dovrebbe prendere più di 300 linee di Python.

Elaborazione dell'output prodotto da difflib per renderlo più compatto è anche un'opzione. Questo è un esempio da un piccolo file con tre cambi (un'aggiunta, un cambiamento, e una soppressione):

---  
+++  
@@ -7,0 +7,1 @@
+aaaaa
@@ -9,1 +10,1 @@
-c= 0
+c= 1
@@ -15,1 +16,0 @@
-    m = re.match(code_re, text)

Quello che il cerotto dice può essere facilmente condensato a:

+7,1 
aaaaa
-9,1 
+10,1
c= 1
-15,1

Per il vostro esempio l'uscita condensata potrebbe essere:

-8,1
+9,1
print "The end"

Per motivi di sicurezza, lasciando in un marcatore leader ( '>') per le linee che devono essere inseriti potrebbe essere una buona idea.

-8,1
+9,1
>print "The end"

E 'questo più vicino a quello che ti serve?

Questa è una semplice funzione per fare la compattazione. Dovrete scrivere il proprio codice per applicare la patch in questo formato, ma dovrebbe essere semplice.

def compact_a_unidiff(s):
    s = [l for l in s if l[0] in ('+','@')]
    result = []
    for l in s:
        if l.startswith('++'):
            continue
        elif l.startswith('+'):
            result.append('>'+ l[1:])
        else:
            del_cmd, add_cmd = l[3:-3].split()
            del_pair, add_pair = (c.split(',') for c in (del_cmd,add_cmd))
            if del_pair[1]  != '0':
                result.append(del_cmd)
            if add_pair[1] != '0':
                result.append(add_cmd)
    return result

Altri suggerimenti

Sono anche ancora cercando di capire il motivo per cui molte funzioni restituiscono un difflib generatore invece di un elenco, che cosa è il vantaggio c'è?

Bene, pensate per un secondo - se si confrontano i file, questi file possono in teoria (e saranno in pratica) è abbastanza grande - la restituzione del Delta come una lista, per exampe, mezzi di lettura i dati completi in memoria , che non è una cosa intelligente da fare.

Per quanto riguarda solo restituendo la differenza, beh, c'è un altro vantaggio nell'utilizzo di un generatore -. Basta scorrere i delta e mantenere tutto ciò che le linee che ti interessa

Se leggete la difflib documentazione per Differ - delta di stile, si vedrà un paragrafo che recita:

Each line of a Differ delta begins with a two-letter code:
Code    Meaning
'- '    line unique to sequence 1
'+ '    line unique to sequence 2
'  '    line common to both sequences
'? '    line not present in either input sequence

Quindi, se desideri solo differenze, si può facilmente filtrare quelli fuori utilizzando str.startswith

È inoltre possibile utilizzare difflib.context_diff per ottenere un delta compatto che mostra solo le modifiche.

Si desidera utilizzare la diff unificata o il contesto se si desidera solo le modifiche. Stai vedendo file più grandi, perché comprende le linee che hanno in comune.

Il vantaggio di restituire un generatore è che l'intera cosa non ha bisogno di essere tenuto in memoria in una sola volta. Questo può essere utile per diffing file di grandi dimensioni.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top