Usare 'diff' (o qualsiasi altra cosa) per ottenere differenze a livello di carattere tra file di testo


91

Vorrei usare "diff" per ottenere una differenza di riga tra e una differenza di carattere. Ad esempio, considera:

File 1

abcde
abc
abcccd

File 2

abcde
ab
abccc

Usando diff -u ottengo:

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

Tuttavia, mi mostra solo che sono stati cambiamenti in queste righe. Quello che mi piacerebbe vedere è qualcosa del tipo:

@@ -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

Ottieni la mia deriva.

Ora, so che posso usare altri motori per contrassegnare / controllare la differenza su una riga specifica. Ma preferisco usare uno strumento che fa tutto.


2
per char diff è particolarmente utile quando si tratta di testi CJK, dove non vengono applicati spazi per la suddivisione delle parole.
把 友情 留 在 无 盐

Risposte:


72

Git ha una differenza di parole e definire tutti i caratteri come parole ti dà effettivamente una differenza di caratteri. Tuttavia, le modifiche alla nuova riga vengono ignorate .

Esempio

Crea un repository come questo:

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

Ora fallo git diff --word-diff=color --word-diff-regex=. master^ mastere otterrai:

git diff

Nota come sia le aggiunte che le eliminazioni vengono riconosciute a livello di carattere, mentre sia le aggiunte che le eliminazioni di nuove righe vengono ignorate.

Potresti anche provare uno di questi:

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

73
Non è necessario creare un repository, puoi semplicemente dare a git diff due file qualsiasi, ovunque sul tuo filesystem e funziona. Il tuo comando funziona alla grande per me in questo modo, quindi grazie! git diff --word-diff=color --word-diff-regex=. file1 file2
qwertzguy

1
Questo è profondamente utile! Se potessi, farei +1 una volta come sviluppatore di software e +1 due volte in più come autore / scrittore. A differenza del codice, dove le righe tendono ad essere ragionevolmente brevi, quando si scrivono articoli / storie, ogni paragrafo tende ad assumere la forma di una lunga riga racchiusa in una parola, e questa caratteristica rende le differenze visivamente utili.
mtraceur

28
Avevo bisogno di aggiungere --no-indexalla risposta di @ qwertzguys sopra per farlo funzionare per me al di fuori di un repository git. Quindi:git diff --no-index --word-diff=color --word-diff-regex=. file1 file2
Nathan Bell

2
git diff non funziona nelle impostazioni generali: git diff --no-index --word-diff = color --word-diff-regex =. <(echo stringa1) <(echo stringa2) .. Niente, ma funziona: diff --color <(echo stringa1) <(echo stringa2).
mosh

1
@NathanBell avevo bisogno di aggiungere anche --no-indexall'interno di un repo
JShorthouse

32

Puoi usare:

diff -u f1 f2 |colordiff |diff-highlight

immagine dello schermo

colordiffè un pacchetto Ubuntu. Puoi installarlo usando sudo apt-get install colordiff.

diff-highlightè di git (dalla versione 2.9). Si trova a /usr/share/doc/git/contrib/diff-highlight/diff-highlight. Puoi metterlo da qualche parte nel tuo file $PATH.


6
colordiff è disponibile anche su homebrew per Mac:brew install colordiff
Emil Stenström

5
Su Mac puoi trovare diff-highlightin$(brew --prefix git)/share/git-core/contrib/diff-highlight/diff-highlight
StefanoP

2
Nel caso in cui non hai installato git usando brew - diff-highlightpuò anche essere installato con pip di python - pip install diff-highlight(lo preferisco anche se git è installato tramite brew)
Yaron U.

22

Il difflib di Python è un asso se vuoi farlo in modo programmatico. Per l'uso interattivo, utilizzo la modalità diff di vim (abbastanza facile da usare: basta invocare vim con vimdiff a b). Di tanto in tanto uso Beyond Compare , che fa praticamente tutto ciò che potresti sperare da uno strumento diff.

Non ho visto nessuno strumento da riga di comando che lo faccia in modo utile, ma come osserva Will, il codice di esempio difflib potrebbe aiutare.


1
Oh .. speravo in qualcosa di più standardizzato (come un argomento della riga di comando nascosto). La cosa più dannata è che ho Beyond Compare 2 e supporta anche l'output di testo su file / console del diff, ma include solo differenze di riga e non differenze di caratteri. Guarderò Python se nessuno ha nient'altro.
VitalyB

6
+1 per avermi presentato vimdiff. Ho trovato i colori predefiniti illeggibili, ma ho trovato una soluzione per questo su stackoverflow.com/questions/2019281/… .
undefined

17

Puoi usare il cmpcomando in Solaris:

cmp

Confronta due file e, se differiscono, indica al primo byte e al numero di riga dove differiscono.


2
cmpè disponibile anche su (almeno alcune) distribuzioni Linux.
Jeff Evans

7
È disponibile anche su Mac OS X.
Eric R. Rath,

I caratteri possono essere costituiti da più byte e OP ha richiesto un confronto visivo.
Cees Timmerman,

1
@CeesTimmerman: cmp permette il confronto visivo, con flag -l -b.
Smar

9

Python ha una comoda libreria denominata difflibche potrebbe aiutare a rispondere alla tua domanda.

Di seguito sono riportati due oneliner che utilizzano difflibper diverse versioni di 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()))'

Questi potrebbero tornare utili come alias di shell che è più facile da spostare con il tuo .${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

E una versione più leggibile da inserire in un file autonomo.

#!/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)

Eccellente one liners. Sarebbe bello avere un output condensato che ignori le righe non modificate.
aidan.plenert.macdonald

6
cmp -l file1 file2 | wc

Ha funzionato bene per me. Il numero più a sinistra del risultato indica il numero di caratteri che differiscono.


1
O solo per ottenere il numero più a sinistra:cmp -l file1 file2 | wc -l
Tony

5

Ho anche scritto il mio script per risolvere questo problema utilizzando l' algoritmo di sottosequenza comune più lungo.

Viene eseguito come tale

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

Il risultato è in html con colorazione rossa e verde. I file più grandi impiegano in modo esponenziale un tempo di elaborazione più lungo, ma questo fa un vero confronto carattere per carattere senza prima controllare riga per riga.


Ho scoperto che JLDiff funziona molto più velocemente sotto pypy.
Joshua

4

Colorato, personaggio di livello diff ouput

Ecco cosa puoi fare con lo script seguente e l' evidenziazione delle differenze (che fa parte di git):

Screenshot del diff colorato

#!/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

(Credito alla risposta di @ retracile per l' sedevidenziazione)


Mostra una buona differenza sullo schermo della shell, ma come la vedo in GVim ??
Hemant Sharma

1
Che cos'è davvero una domanda gvim :). command | gvim -farà quello che vuoi.
Att Righ

Per riferimento, l'evidenziazione delle differenze sembra essere inclusa come parte gitma non posizionata sul percorso. Una delle mie macchine in cui vive /usr/share/doc/git/contrib/diff-highlight.
Att Righ

collegamento non funzionante. Come installo diff-highlight. Non sembra essere in un gestore di pacchetti.
Trevor Hickey

3

Il difflib di Python può farlo.

La documentazione include un esempio di programma da riga di comando per te.

Il formato esatto non è quello specificato, ma sarebbe semplice analizzare l'output in stile ndiff o modificare il programma di esempio per generare la notazione.


Grazie! Lo esaminerò. Speravo in qualcosa di più standardizzato (come un argomento della riga di comando nascosto). Ma potrebbe ancora andare bene. Esaminerò Python se nessuno ha qualcosa di più standard (anche se sembra che non lo sia).
VitalyB

2

Ecco uno strumento di confronto del testo online: http://text-compare.com/

Può evidenziare ogni singolo carattere diverso e continua a confrontare il resto.


Questo sembra fare differenze a livello di riga senza alcuna opzione per i singoli caratteri. Come riesci a confrontare i personaggi?
Dragon

Ah; evidenzia personaggi diversi. Ma è ancora a livello di linea catdoge cat\ndogcorrisponderà solo acat
Dragon

1

Penso che la soluzione più semplice sia sempre una buona soluzione. Nel mio caso, il codice seguente mi aiuta molto. Spero che aiuti chiunque altro.

#!/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]

Puoi confrontare due file con la seguente sintassi sul tuo terminale preferito:

$ ./diff.py fileNumber1 fileNumber2

0

Se mantieni i tuoi file in Git, puoi differire tra le versioni con lo script di evidenziazione delle differenze , che mostrerà linee diverse, con le differenze evidenziate.

Sfortunatamente funziona solo quando il numero di linee rimosse corrisponde al numero di linee aggiunte - c'è un codice stub per quando le linee non corrispondono, quindi presumibilmente questo potrebbe essere corretto in futuro.


0

Non è una risposta completa, ma se cmp -ll'output di non è abbastanza chiaro, puoi usare:

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

su OSX usa `` sed 's / (.) / \ 1 \' $ '\ n / g' file1> file1.vertical sed 's / \ (. \) / \ 1 \' $ '\ n / g 'file2> file2.vertical ``
mmacvicar

0

La maggior parte di queste risposte menziona l'uso di diff-highlight , un modulo Perl. Ma non volevo capire come installare un modulo Perl. Quindi ho apportato alcune piccole modifiche affinché fosse uno script Perl autonomo.

Puoi installarlo usando:

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

E l'utilizzo (se hai l'Ubuntu colordiffmenzionato nella risposta di zhanxw):

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

E l'utilizzo (se non lo fai):

▶ diff -u f1 f2 | DiffHighlight.pl
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.