Verwenden Sie 'diff' (oder etwas anderes), um Unterschiede auf Zeichenebene zwischen Textdateien zu erhalten


91

Ich möchte 'diff' verwenden, um einen Unterschied zwischen Zeilen und Zeichen zu erhalten. Betrachten Sie zum Beispiel:

Datei 1

abcde
abc
abcccd

Datei 2

abcde
ab
abccc

Mit diff -u bekomme ich:

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

Es zeigt mir jedoch nur, dass sich diese Zeilen geändert haben. Was ich gerne sehen würde, ist so etwas wie:

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

Du verstehst meinen Drift.

Jetzt weiß ich, dass ich andere Motoren verwenden kann , um den Unterschied in einer bestimmten Zeile zu markieren / zu überprüfen. Aber ich würde lieber ein Tool verwenden, das alles erledigt.


2
per char diff ist besonders nützlich, wenn es um CJK-Texte geht, bei denen kein Leerzeichen für die Wortaufteilung verwendet wird.
29 友情 留 在 无 29

Antworten:


71

Git hat einen Wortunterschied, und wenn Sie alle Zeichen als Wörter definieren, erhalten Sie effektiv einen Zeichenunterschied. Änderungen an Zeilenumbrüchen werden jedoch ignoriert .

Beispiel

Erstellen Sie ein Repository wie folgt:

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

Jetzt tun git diff --word-diff=color --word-diff-regex=. master^ masterSie und Sie erhalten:

Git Diff

Beachten Sie, wie sowohl Hinzufügungen als auch Löschungen auf Zeichenebene erkannt werden, während Hinzufügungen und Löschungen von Zeilenumbrüchen ignoriert werden.

Vielleicht möchten Sie auch eines davon ausprobieren:

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

73
Sie müssen überhaupt kein Repo erstellen, Sie können git diff einfach zwei beliebige Dateien geben, überall auf Ihrem Dateisystem, und es funktioniert. Ihr Befehl funktioniert auf diese Weise hervorragend für mich, also danke! git diff --word-diff=color --word-diff-regex=. file1 file2
qwertzguy

1
Das ist zutiefst hilfreich! Würde +1 einmal als Softwareentwickler und +1 zweimal als Autor / Autor, wenn ich könnte. Anders als im Code, wo die Zeilen beim Schreiben von Papieren / Geschichten in der Regel relativ kurz sind, hat jeder Absatz die Form einer langen, in Wörter eingeschlossenen Zeile, und diese Funktion macht die Unterschiede tatsächlich visuell nützlich.
mtraceur

28
Ich musste --no-indexdie Antwort von @ qwertzguys oben ergänzen , damit sie außerhalb eines Git-Repos für mich funktioniert. Also:git diff --no-index --word-diff=color --word-diff-regex=. file1 file2
Nathan Bell

2
git diff funktioniert in der allgemeinen Einstellung nicht: git diff --no-index --word-diff = color --word-diff-regex =. <(echo string1) <(echo string2) .. Nichts, aber das funktioniert: diff --color <(echo string1) <(echo string2).
Mosh

1
@ NathanBell Ich musste auch --no-indexinnerhalb eines Repos hinzufügen
JShorthouse

32

Sie können verwenden:

diff -u f1 f2 |colordiff |diff-highlight

Bildschirmfoto

colordiffist ein Ubuntu-Paket. Sie können es mit installieren sudo apt-get install colordiff.

diff-highlightist von git (seit Version 2.9). Es befindet sich in /usr/share/doc/git/contrib/diff-highlight/diff-highlight. Sie können es irgendwo in Ihrem platzieren $PATH.


6
colordiff ist auch auf Homebrew für Mac verfügbar:brew install colordiff
Emil Stenström

5
Auf Mac finden Sie diff-highlightin$(brew --prefix git)/share/git-core/contrib/diff-highlight/diff-highlight
StefanoP

2
Falls Sie Git nicht mit Brew installiert haben - diff-highlightkann auch mit Python's Pip installiert werden - pip install diff-highlight(ich bevorzuge es auch, wenn Git über Brew installiert wird)
Yaron U.

21

Pythons Difflib ist ein Ass, wenn Sie dies programmgesteuert tun möchten. Für die interaktive Verwendung verwende ich den Diff-Modus von vim (einfach zu bedienen: Rufen Sie einfach vim mit auf vimdiff a b). Gelegentlich verwende ich auch Beyond Compare , das so ziemlich alles macht, was man sich von einem Diff-Tool erhoffen kann.

Ich habe kein Befehlszeilentool gesehen, das dies sinnvoll macht, aber wie Will bemerkt, könnte der difflib-Beispielcode helfen.


1
Oh ... ich hatte auf etwas Standardisierteres gehofft (wie ein verstecktes Kommandozeilenargument). Das Verdammteste ist, dass ich Beyond Compare 2 habe und es sogar die Textausgabe in eine Datei / Konsole des Diff unterstützt, aber es enthält immer noch nur Zeilendifferenzen und keine Zeichendifferenzen. Ich werde in Python schauen, wenn niemand etwas anderes hat.
VitalyB

6
+1 für die Einführung in vimdiff. Ich fand die Standardfarben unlesbar, fand aber eine Lösung dafür unter stackoverflow.com/questions/2019281/… .
undefiniert

17

Sie können den cmpBefehl in Solaris verwenden:

cmp

Vergleichen Sie zwei Dateien und teilen Sie, falls sie sich unterscheiden, dem ersten Byte und der Zeilennummer mit, wo sie sich unterscheiden.


2
cmpist auch auf (zumindest einigen) Linux-Distributionen verfügbar.
Jeff Evans

7
Es ist auch unter Mac OS X verfügbar.
Eric R. Rath

Zeichen können aus mehreren Bytes bestehen, und OP fordert einen visuellen Vergleich an.
Cees Timmerman

1
@CeesTimmerman: cmp ermöglicht einen visuellen Vergleich mit flag -l -b.
Smar

9

Python hat eine praktische Bibliothek mit dem Namen, difflibdie Ihnen bei der Beantwortung Ihrer Frage helfen kann.

Im Folgenden sind zwei Oneliner aufgeführt, die difflibfür verschiedene Python-Versionen verwendet werden.

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()))'

Diese können als Shell-Alias ​​nützlich sein, der sich leichter mit Ihrem bewegen lässt .${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

Und eine besser lesbare Version zum Einfügen in eine eigenständige Datei.

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

Ausgezeichnete Einzeiler. Wäre schön, einen komprimierten Ausgang zu haben, der unveränderte Zeilen ignoriert.
aidan.plenert.macdonald

6
cmp -l file1 file2 | wc

Hat gut für mich funktioniert. Die Zahl ganz links im Ergebnis gibt die Anzahl der Zeichen an, die sich unterscheiden.


1
Oder um einfach die Nummer ganz links zu bekommen:cmp -l file1 file2 | wc -l
Tony

5

Ich habe auch mein eigenes Skript geschrieben , um dieses Problem mit dem längsten gemeinsamen Subsequenzalgorithmus zu lösen .

Es wird als solches ausgeführt

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

Das Ergebnis ist in HTML mit roter und grüner Färbung. Größere Dateien benötigen exponentiell mehr Zeit für die Verarbeitung, dies führt jedoch einen echten Zeichen-für-Zeichen-Vergleich durch, ohne zuerst Zeile für Zeile zu überprüfen.


Ich habe festgestellt, dass JLDiff unter Pypy viel schneller läuft.
Joshua

4

Farbige Ausgabe auf Charakterebene diff

Folgendes können Sie mit dem folgenden Skript und dem Diff-Highlight (das Teil von git ist) tun :

Farbiger Diff Screenshot

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

( Dank an die Antwort von @ retracile für die sedHervorhebung)


Es zeigt einen guten Unterschied auf dem Shell-Bildschirm, aber wie sehe ich diesen Unterschied in GVim?
Hemant Sharma

1
Was ist das wirklich eine GVIM-Frage :). command | gvim -wird tun was du willst.
Att Righ

Als Referenz scheint Diff-Highlight als Teil gitIhres Pfades enthalten, aber nicht auf diesem platziert zu sein. Eine meiner Maschinen, an der das lebt /usr/share/doc/git/contrib/diff-highlight.
Att Righ

defekter Link. Wie installiere ich Diff-Highlight? Scheint nicht in einem Paketmanager zu sein.
Trevor Hickey

3

Pythons Difflib kann dies tun.

Die Dokumentation enthält ein Beispiel für ein Befehlszeilenprogramm für Sie.

Das genaue Format entspricht nicht Ihren Angaben, es ist jedoch unkompliziert, entweder die Ausgabe im ndiff-Stil zu analysieren oder das Beispielprogramm zu ändern, um Ihre Notation zu generieren.


Vielen Dank! Ich werde es untersuchen. Ich hatte auf etwas Standardisierteres gehofft (wie ein verstecktes Kommandozeilenargument). Aber es könnte immer noch gut gehen. Ich werde in Python schauen, wenn niemand mehr Standard hat (obwohl es so scheint, als ob nicht).
VitalyB

2

Hier ist ein Online-Textvergleichstool: http://text-compare.com/

Es kann jedes einzelne Zeichen hervorheben, das anders ist, und den Rest weiter vergleichen.


Dies scheint Unterschiede auf Zeilenebene ohne Option für einzelne Zeichen zu bewirken. Wie bringt man es dazu, Charaktere zu vergleichen?
Dragon

Ah; Es hebt Zeichen hervor, die unterschiedlich sind. Aber es ist immer noch Line-Level catdogund cat\ndogwird nur aufcat
Dragon

1

Ich denke, die einfachere Lösung ist immer eine gute Lösung. In meinem Fall hilft mir der folgende Code sehr. Ich hoffe es hilft jemand anderem.

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

Sie können zwei Dateien mit der folgenden Syntax an Ihrem bevorzugten Terminal vergleichen:

$ ./diff.py fileNumber1 fileNumber2

0

Wenn Sie Ihre Dateien in Git behalten, können Sie mit dem Diff-Highlight-Skript zwischen verschiedenen Versionen unterscheiden , das verschiedene Zeilen mit hervorgehobenen Unterschieden unterscheiden.

Leider funktioniert es nur, wenn die Anzahl der entfernten Zeilen mit der Anzahl der hinzugefügten Zeilen übereinstimmt. Es gibt einen Stub-Code für den Fall, dass die Zeilen nicht übereinstimmen. Vermutlich könnte dies in Zukunft behoben werden.


0

Keine vollständige Antwort, aber wenn cmp -ldie Ausgabe nicht klar genug ist, können Sie Folgendes verwenden:

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

Verwenden Sie unter OSX `` `sed 's / (.) / \ 1 \' $ '\ n / g' file1> file1.vertical sed 's / \ (. \) / \ 1 \' $ '\ n / g 'file2> file2.vertical `` `
mmacvicar

0

Die meisten dieser Antworten erwähnen die Verwendung von Diff-Highlight , einem Perl-Modul. Aber ich wollte nicht herausfinden, wie man ein Perl-Modul installiert. Deshalb habe ich ein paar kleinere Änderungen vorgenommen, um ein in sich geschlossenes Perl-Skript zu erstellen.

Sie können es installieren mit:

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

Und die Verwendung (wenn Sie das colordiffin der Antwort von zhanxw erwähnte Ubuntu haben):

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

Und die Verwendung (wenn Sie nicht):

▶ diff -u f1 f2 | DiffHighlight.pl
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.