Usar 'diff' (o cualquier otra cosa) para obtener diferencias a nivel de caracteres entre archivos de texto


93

Me gustaría usar 'diff' para obtener una diferencia entre líneas y caracteres. Por ejemplo, considere:

Archivo 1

abcde
abc
abcccd

Archivo 2

abcde
ab
abccc

Usando diff -u obtengo:

@@ -1,3 +1,3 @@
 abcde
-abc
-abcccd
\ No newline at end of file
+ab
+abccc
\ No newline at end of file

Sin embargo, solo me muestra que hubo cambios en estas líneas. Lo que me gustaría ver es algo como:

@@ -1,3 +1,3 @@
 abcde
-ab<ins>c</ins>
-abccc<ins>d</ins>
\ No newline at end of file
+ab
+abccc
\ No newline at end of file

Entiendes mi deriva.

Ahora, sé que puedo usar otros motores para marcar / verificar la diferencia en una línea específica. Pero prefiero usar una herramienta que lo haga todo.


2
per char diff es especialmente útil cuando se trata de textos CJK, donde no se aplican espacios en blanco para la división de palabras.
把 友情 留 在 无 盐

Respuestas:


76

Git tiene una diferencia de palabras, y definir todos los caracteres como palabras efectivamente le da una diferencia de caracteres. Sin embargo, se ignoran los cambios de nueva línea .

Ejemplo

Crea un repositorio como este:

mkdir chardifftest
cd chardifftest
git init
echo -e 'foobarbaz\ncatdog\nfox' > file
git add -A; git commit -m 1
echo -e 'fuobArbas\ncat\ndogfox' > file
git add -A; git commit -m 2

Ahora, hazlo git diff --word-diff=color --word-diff-regex=. master^ mastery obtendrás:

git diff

Tenga en cuenta cómo se reconocen tanto las adiciones como las eliminaciones a nivel de personaje, mientras que las adiciones y eliminaciones de nuevas líneas se ignoran.

También puede probar uno de estos:

git diff --word-diff=plain --word-diff-regex=. master^ master
git diff --word-diff=porcelain --word-diff-regex=. master^ master

76
No necesitas crear un repositorio en absoluto, simplemente puedes darle a git diff dos archivos cualquiera, en cualquier lugar de tu sistema de archivos y funciona. Tu comando funciona muy bien para mí de esa manera, ¡así que gracias! git diff --word-diff=color --word-diff-regex=. file1 file2
qwertzguy

1
¡Esto es de gran ayuda! Haría +1 una vez como desarrollador de software y +1 dos veces más como autor / escritor si pudiera. A diferencia del código, donde las líneas tienden a ser razonablemente cortas, al escribir artículos / historias, cada párrafo tiende a tomar la forma de una línea larga envuelta en palabras, y esta característica hace que las diferencias sean realmente útiles visualmente.
mtraceur

29
Necesitaba agregar --no-indexa la respuesta de @ qwertzguys anterior para que funcionara para mí fuera de un repositorio de git. Entonces:git diff --no-index --word-diff=color --word-diff-regex=. file1 file2
Nathan Bell

2
git diff no funciona en la configuración general: git diff --no-index --word-diff = color --word-diff-regex =. <(echo string1) <(echo string2) .. Nada, pero esto funciona: diff --color <(echo string1) <(echo string2).
mosh

1
@NathanBell También necesitaba agregar --no-indexdentro de un repositorio
JShorthouse

32

Puedes usar:

diff -u f1 f2 |colordiff |diff-highlight

captura de pantalla

colordiffes un paquete de Ubuntu. Puede instalarlo usando sudo apt-get install colordiff.

diff-highlightes de git (desde la versión 2.9). Se encuentra en /usr/share/doc/git/contrib/diff-highlight/diff-highlight. Puede ponerlo en algún lugar de su $PATH.


6
colordiff también está disponible en homebrew para Mac:brew install colordiff
Emil Stenström

5
En Mac se puede encontrar diff-highlighten$(brew --prefix git)/share/git-core/contrib/diff-highlight/diff-highlight
StefanoP

2
En caso de que no haya instalado git usando brew, diff-highlighttambién se puede instalar con pip de python pip install diff-highlight(lo prefiero incluso si git se instala a través de brew)
Yaron U.13 de

22

El difflib de Python es excelente si quieres hacer esto de forma programática. Para uso interactivo, uso el modo diff de vim (lo suficientemente fácil de usar: simplemente invoca vim con vimdiff a b). Ocasionalmente también uso Beyond Compare , que hace casi todo lo que podrías esperar de una herramienta de diferencias.

No he visto ninguna herramienta de línea de comandos que haga esto de manera útil, pero como señala Will, el código de ejemplo difflib podría ayudar.


1
Oh ... esperaba algo más estandarizado (como un argumento de línea de comando oculto). Lo más maldito es que tengo Beyond Compare 2 e incluso admite la salida de texto al archivo / consola del diff, pero todavía solo incluye line-diffs y no char-diffs. Buscaré Python si nadie tiene nada más.
VitalyB

6
+1 por presentarme a vimdiff. Encontré que los colores predeterminados no se podían leer, pero encontré una solución para eso en stackoverflow.com/questions/2019281/… .
indefinido

18

Puede utilizar el cmpcomando en Solaris:

cmp

Compare dos archivos y, si difieren, le indica al primer byte y al número de línea dónde difieren.


2
cmptambién está disponible en (al menos algunas) distribuciones de Linux.
Jeff Evans

7
También está disponible en Mac OS X.
Eric R. Rath

Los caracteres pueden constar de varios bytes y OP solicitó una comparación visual.
Cees Timmerman

1
@CeesTimmerman: cmp permite la comparación visual, con bandera -l -b.
Smar

10

Python tiene una biblioteca conveniente nombrada difflibque podría ayudar a responder su pregunta.

A continuación se muestran dos oneliners que se utilizan difflibpara diferentes versiones de Python.

python3 -c 'import difflib, sys; \
  print("".join( \
    difflib.ndiff( \ 
      open(sys.argv[1]).readlines(),open(sys.argv[2]).readlines())))'
python2 -c 'import difflib, sys; \
  print "".join( \
    difflib.ndiff( \
      open(sys.argv[1]).readlines(), open(sys.argv[2]).readlines()))'

Estos pueden ser útiles como un alias de shell que es más fácil de mover con su .${SHELL_NAME}rc.

$ alias char_diff="python2 -c 'import difflib, sys; print \"\".join(difflib.ndiff(open(sys.argv[1]).readlines(), open(sys.argv[2]).readlines()))'"
$ char_diff old_file new_file

Y una versión más legible para poner en un archivo independiente.

#!/usr/bin/env python2
from __future__ import with_statement

import difflib
import sys

with open(sys.argv[1]) as old_f, open(sys.argv[2]) as new_f:
    old_lines, new_lines = old_f.readlines(), new_f.readlines()
diff = difflib.ndiff(old_lines, new_lines)
print ''.join(diff)

Excelente one liners. Sería bueno tener una salida condensada que ignora las líneas sin cambios.
aidan.plenert.macdonald

6
cmp -l file1 file2 | wc

Funcionó bien para mí. El número más a la izquierda del resultado indica el número de caracteres que difieren.


1
O simplemente para obtener el número más a la izquierda:cmp -l file1 file2 | wc -l
Tony

5

También escribí mi propio script para resolver este problema usando el algoritmo de subsecuencia común más largo.

Se ejecuta como tal

JLDiff.py a.txt b.txt out.html

El resultado está en html con coloración roja y verde. Los archivos más grandes requieren exponencialmente una mayor cantidad de tiempo para procesarse, pero esto hace una verdadera comparación carácter por carácter sin verificar línea por línea primero.


He descubierto que JLDiff se ejecuta mucho más rápido en pypy.
Joshua

4

Color, carácter de nivel diff ouput

Esto es lo que puede hacer con el siguiente script y diff-highlight (que es parte de git):

Captura de pantalla de diferencias de colores

#!/bin/sh -eu

# Use diff-highlight to show word-level differences

diff -U3 --minimal "$@" |
  sed 's/^-/\x1b[1;31m-/;s/^+/\x1b[1;32m+/;s/^@/\x1b[1;34m@/;s/$/\x1b[0m/' |
  diff-highlight

(Crédito a la respuesta de @ retracile por el sedresaltado)


Muestra una buena diferencia en la pantalla de shell, pero ¿cómo veo esa diferencia en GVim?
Hemant Sharma

1
Lo que es realmente una pregunta de gvim :). command | gvim -harás lo que quieras.
Att Righ

Como referencia, diff-highlight parece estar incluido como parte de gitsu ruta, pero no en ella. Una de mi máquina en la que vive /usr/share/doc/git/contrib/diff-highlight.
Att Righ

Enlace roto. ¿Cómo instalo diff-highlight. No parece estar en un administrador de paquetes.
Trevor Hickey

3

El difflib de Python puede hacer esto.

La documentación incluye un programa de línea de comandos de ejemplo para usted.

El formato exacto no es el que especificó, pero sería sencillo analizar la salida de estilo ndiff o modificar el programa de ejemplo para generar su notación.


¡Gracias! Lo miraré. Esperaba algo más estandarizado (como un argumento de línea de comando oculto). Pero aún podría funcionar bien. Examinaré Python si nadie tiene algo más estándar (aunque parece que no).
VitalyB

2

Aquí hay una herramienta de comparación de texto en línea: http://text-compare.com/

Puede resaltar cada carácter que es diferente y continúa comparando el resto.


Esto parece hacer diferencias de nivel de línea sin opción para caracteres individuales. ¿Cómo lo consigues para comparar personajes?
Dragón

Ah; destaca personajes que son diferentes. Pero sigue siendo de nivel de línea catdogy cat\ndogsolo coincidirá encat
Dragon

1

Creo que la solución más simple es siempre una buena solución. En mi caso, el siguiente código me ayuda mucho. Espero que ayude a alguien más.

#!/bin/env python

def readfile( fileName ):
    f = open( fileName )
    c = f.read()
    f.close()
    return c

def diff( s1, s2 ):
    counter=0
    for ch1, ch2 in zip( s1, s2 ):
        if not ch1 == ch2:
            break
        counter+=1
    return counter < len( s1 ) and counter or -1

import sys

f1 = readfile( sys.argv[1] )
f2 = readfile( sys.argv[2] )
pos = diff( f1, f2 )
end = pos+200

if pos >= 0:
    print "Different at:", pos
    print ">", f1[pos:end]
    print "<", f2[pos:end]

Puede comparar dos archivos con la siguiente sintaxis en su terminal favorito:

$ ./diff.py fileNumber1 fileNumber2

0

Si mantiene sus archivos en Git, puede diferenciar entre versiones con el script diff-highlight , que mostrará diferentes líneas, con las diferencias resaltadas.

Desafortunadamente, solo funciona cuando la cantidad de líneas eliminadas coincide con la cantidad de líneas agregadas; hay un código auxiliar para cuando las líneas no coinciden, por lo que presumiblemente esto podría solucionarse en el futuro.


0

No es una respuesta completa, pero si cmp -lla salida no es lo suficientemente clara, puede usar:

sed 's/\(.\)/\1\n/g' file1 > file1.vertical
sed 's/\(.\)/\1\n/g' file2 > file2.vertical
diff file1.vertical file2.vertical

en OSX use `` `sed 's / (.) / \ 1 \' $ '\ n / g' archivo1> archivo1.vertical sed 's / \ (. \) / \ 1 \' $ '\ n / g 'file2> file2.vertical' '
mmacvicar

0

La mayoría de estas respuestas mencionan el uso de diff-highlight , un módulo de Perl. Pero no quería averiguar cómo instalar un módulo Perl. Así que hice algunos cambios menores para que fuera un script Perl autónomo.

Puedes instalarlo usando:

▶ curl -o /usr/local/bin/DiffHighlight.pl \
   https://raw.githubusercontent.com/alexharv074/scripts/master/DiffHighlight.pl

Y el uso (si tiene el Ubuntu colordiffmencionado en la respuesta de zhanxw):

▶ diff -u f1 f2 | colordiff | DiffHighlight.pl

Y el uso (si no lo hace):

▶ diff -u f1 f2 | DiffHighlight.pl

0

ccdiffes una práctica herramienta dedicada a la tarea. Así es como se ve su ejemplo con él:

salida de ejemplo de ccdiff

De forma predeterminada, resalta las diferencias de color, pero también se puede usar en una consola sin soporte de color.

El paquete está incluido en el repositorio principal de Debian:

ccdiff es una diferencia de color que también colorea dentro de las líneas cambiadas.

Todas las herramientas de línea de comandos que muestran la diferencia entre dos archivos se quedan cortas en mostrar cambios menores de manera visible y útil. ccdiff intenta dar la apariencia de diff --coloro colordiff, pero extendiendo la visualización de la salida en color desde líneas coloreadas eliminadas y agregadas a colores para caracteres eliminados y agregados dentro de las líneas cambiadas.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.