Pregunta

Yo estaba buscando hacer algo como lo que creo que los sistemas de control de cambios hacen, se comparan dos archivos, y guardar una pequeña diff cada vez que los cambios en los archivos. He estado leyendo esta página: http://docs.python.org/library/difflib.html y no es que se hunde en la cabeza a mi parecer.

Yo estaba tratando de recrear esto en un programa algo simple que se muestra a continuación, pero lo que me parece que falta es que los de Delta contienen al menos tanto como el archivo original, y mucho más.

No es posible llegar a sólo los cambios puros? La razón que pido es de esperar que obvia - para ahorrar espacio en disco
. Acabo de poder guardar todo el trozo de código cada vez, pero sería mejor ahorrar código actual una vez y luego pequeñas diferenciaciones de los cambios.

También estoy todavía tratando de averiguar por qué muchas funciones difflib devuelven un generador en lugar de una lista, ¿cuál es la ventaja allí?

trabajará difflib para mí - o necesito encontrar un paquete más profesional con más funciones?

# 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

Gracias!

ACTUALIZACIÓN:

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

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

----- ----- MOSTRAR CONTEXTDIFF




* 5,9 **

 print "HIJ"

 print "JKL"

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

    print "XYZ"

--- 5,9 ----

 print "HIJ"

 print "JKL"

 print "Hello World"

 print "XYZ"
  • imprimir "El final"

Otra actualización:

En los viejos tiempos de Panvalet un bibliotecario, herramientas de gestión de fuente para el mainframe, que podría crear un conjunto de cambios como esto:

++ADD 9
   print "j=" + j 

¿Qué significa simplemente añadir una línea (o líneas) después de la línea 9. Entonces las palabras hay palabras como ++ ++ sustituir o actualizar. http://www4.hawaii.gov/dags/icsd/ppmo /Stds_Web_Pages/pdf/it110401.pdf

¿Fue útil?

Solución

Diffs debe contener suficiente información para que sea posible para parchear una versión a otra, de modo que sí, para la prueba de un cambio de una sola línea a una parte muy pequeña de documentos, almacenar toda la documentación podría ser más barato.

Funciones de biblioteca vuelven iteradores para que sea más fácil para los clientes que están apretados en la memoria, o sólo tiene que mirar a parte de la secuencia resultante. Está bien en Python porque cada iterador se puede convertir en una lista con una expresión list(an_iterator) muy corto.

La mayoría de diferenciación se realiza en líneas de texto, pero es posible bajar a la char-por-char, y difflib lo hace. Echar un vistazo a la href="http://docs.python.org/library/difflib.html#differ-objects" rel="nofollow"> Differ clase de objeto en difflib.

Todos los ejemplos sobre la salida humano agradable lugar de usar, pero los diferenciales se gestionan internamente en una forma mucho más compacta usar el ordenador. Además, las diferenciaciones por lo general contienen información redundante (como el texto de una línea de borrar) para hacer parches y fusionando los cambios de seguro. La redundancia puede ser removido por su propio código, si se siente cómodo con eso.

acabo de leer que opta por lo menos difflib-sorpresa en favor de optimalidad, que es algo que no voy a argumentar en contra. Hay bien conocido algoritmos que son rápidos en la producción de un conjunto mínimo de cambios.

I una vez codificado un motor diffing genérico junto con uno de los algoritmos óptimos en aproximadamente 1250 líneas de Java ( JRCS ). Funciona para cualquier secuencia de elementos que se pueden comparar por la igualdad. Si usted quiere construir su propia solución, creo que una traducción / reimplementación de JRCS debe tomar no más de 300 líneas de Python.

El procesamiento de la salida producida por difflib para que sea más compacto es también una opción. Este es un ejemplo de un pequeño archivos con tres cambios (una adición, un cambio, y una supresión):

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

¿Qué dice el parche se puede condensar fácilmente a:

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

Para su propio ejemplo la salida condensada sería:

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

Por razones de seguridad, dejando en un marcador de ataque ( '>') para las líneas que deben ser insertados podría ser una buena idea.

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

Es eso más cerca de lo que necesita?

Esta es una función simple de hacer la compactación. Vas a tener que escribir su propio código para aplicar el parche en ese formato, pero debe ser sencillo.

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

Otros consejos

También estoy todavía tratando de averiguar por eso muchas funciones devuelven un difflib generador en lugar de una lista, lo que es la ventaja allí?

Bueno, pensar en ello por un segundo - si se compara archivos, esos archivos pueden, en teoría (y serán en la práctica) ser bastante grande - la devolución del delta como una lista, por exampe, medios de lectura de los datos completos en la memoria , que no es una cosa elegante a hacer.

En cuanto a la diferencia y no regresaron, bueno, hay otra ventaja en el uso de un generador -. Simplemente iterar sobre el delta y mantener líneas lo que le interesa

Si usted lee la difflib documentación para Difieren - deltas de estilo, verá un párrafo que dice lo siguiente:

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

Por lo tanto, si sólo desea diferencias, se puede filtrar fácilmente los a cabo mediante el uso de str.startswith

También puede utilizar difflib.context_diff para obtener un delta compacta que muestra sólo los cambios.

You want to use the unified or context diff if you just want the changes. You're seeing bigger files because it includes the lines they have in common.

The advantage of returning a generator is that the entire thing doesn't need to be held in memory at once. This can be useful for diffing very large files.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top