Utilisation de 'diff' (ou autre) pour obtenir des différences au niveau des caractères entre les fichiers texte


91

Je voudrais utiliser «diff» pour obtenir une différence à la fois entre les lignes et les caractères. Par exemple, considérez:

Fichier 1

abcde
abc
abcccd

Fichier 2

abcde
ab
abccc

En utilisant diff -u, j'obtiens:

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

Cependant, cela ne me montre que des changements dans ces lignes. Ce que j'aimerais voir, c'est quelque chose comme:

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

Vous comprenez ma dérive.

Maintenant, je sais que je peux utiliser d' autres moteurs pour marquer / vérifier la différence sur une ligne spécifique. Mais je préfère utiliser un seul outil qui fait tout cela.


2
per char diff est particulièrement utile lorsqu'il s'agit de textes CJK, où aucun espace n'est appliqué pour le fractionnement de mots.
把 友情 留 在 无 盐

Réponses:


72

Git a un mot diff, et définir tous les caractères comme des mots vous donne effectivement un caractère diff. Cependant, les modifications de nouvelle ligne sont ignorées .

Exemple

Créez un référentiel comme celui-ci:

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

Maintenant, faites git diff --word-diff=color --word-diff-regex=. master^ masteret vous obtiendrez:

git diff

Notez comment les ajouts et les suppressions sont reconnus au niveau des caractères, tandis que les ajouts et les suppressions de nouvelles lignes sont ignorés.

Vous pouvez également essayer l'une de ces solutions:

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

73
Vous n'avez pas du tout besoin de créer un dépôt, vous pouvez simplement donner à git diff deux fichiers quelconques, n'importe où sur votre système de fichiers et cela fonctionne. Votre commande fonctionne très bien pour moi de cette façon, alors merci! git diff --word-diff=color --word-diff-regex=. file1 file2
qwertzguy

1
Ceci est profondément utile! Donnerait +1 une fois en tant que développeur de logiciels et +1 deux fois de plus en tant qu'auteur / écrivain si je le pouvais. Contrairement au code, où les lignes ont tendance à être raisonnablement courtes, lorsque vous écrivez des articles / histoires, chaque paragraphe a tendance à prendre la forme d'une longue ligne enveloppée de mots, et cette fonctionnalité rend les différences réellement utiles visuellement.
mtraceur

28
J'avais besoin d'ajouter --no-indexà la réponse de @ qwertzguys ci-dessus afin de la faire fonctionner pour moi en dehors d'un dépôt git. Donc:git diff --no-index --word-diff=color --word-diff-regex=. file1 file2
Nathan Bell

2
git diff ne fonctionne pas dans les paramètres généraux: git diff --no-index --word-diff = color --word-diff-regex =. <(echo string1) <(echo string2) .. Rien, mais cela fonctionne: diff --color <(echo string1) <(echo string2).
mosh

1
@NathanBell J'avais aussi besoin d'ajouter à l' --no-indexintérieur d'un repo
JShorthouse

32

Vous pouvez utiliser:

diff -u f1 f2 |colordiff |diff-highlight

capture d'écran

colordiffest un package Ubuntu. Vous pouvez l'installer en utilisant sudo apt-get install colordiff.

diff-highlightest de git (depuis la version 2.9). Il est situé dans /usr/share/doc/git/contrib/diff-highlight/diff-highlight. Vous pouvez le mettre quelque part dans votre fichier $PATH.


6
colordiff est également disponible sur homebrew pour Mac:brew install colordiff
Emil Stenström

5
Sur Mac, vous pouvez trouver diff-highlightdans$(brew --prefix git)/share/git-core/contrib/diff-highlight/diff-highlight
StefanoP

2
Si vous n'avez pas installé git en utilisant brew - diff-highlightpeut également être installé avec le pip de python - pip install diff-highlight(je le préfère même si git est installé via brew)
Yaron U.

22

La difflib de Python est ace si vous voulez faire cela par programme. Pour une utilisation interactive, j'utilise le mode diff de vim (assez simple à utiliser: il suffit d'appeler vim avec vimdiff a b). J'utilise aussi occasionnellement Beyond Compare , qui fait à peu près tout ce que vous pouvez espérer d'un outil de comparaison.

Je n'ai vu aucun outil de ligne de commande qui le fasse utilement, mais comme Will le note, l'exemple de code difflib pourrait aider.


1
Oh ... j'espérais quelque chose de plus standardisé (comme un argument de ligne de commande caché). Le plus gros problème est que j'ai Beyond Compare 2 et qu'il prend même en charge la sortie de texte vers le fichier / console du diff, mais il n'inclut toujours que les diffs de ligne et non les char-diffs. J'examinerai python si personne n'a autre chose.
VitalyB

6
+1 pour m'avoir présenté vimdiff. J'ai trouvé les couleurs par défaut illisibles, mais j'ai trouvé une solution pour cela sur stackoverflow.com/questions/2019281/… .
undefined

17

Vous pouvez utiliser la cmpcommande dans Solaris:

cmp

Comparez deux fichiers et, s'ils diffèrent, indique au premier octet et au numéro de ligne où ils diffèrent.


2
cmpest également disponible sur (au moins certaines) distributions Linux.
Jeff Evans

7
Il est également disponible sur Mac OS X.
Eric R. Rath

Les caractères peuvent être constitués de plusieurs octets et OP a demandé une comparaison visuelle.
Cees Timmerman le

1
@CeesTimmerman: cmp permet une comparaison visuelle, avec indicateur -l -b.
Smar

9

Python a nommé une bibliothèque pratique difflibqui pourrait aider à répondre à votre question.

Voici deux oneliners utilisant difflib pour différentes versions 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()))'

Ceux-ci peuvent être utiles en tant qu'alias de shell qui est plus facile à déplacer avec votre .${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

Et une version plus lisible à mettre dans un fichier autonome.

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

Excellente doublure. Ce serait bien d'avoir une sortie condensée qui ignore les lignes inchangées.
aidan.plenert.macdonald

6
cmp -l file1 file2 | wc

A bien fonctionné pour moi. Le nombre le plus à gauche du résultat indique le nombre de caractères qui diffèrent.


1
Ou pour simplement obtenir le nombre le plus à gauche:cmp -l file1 file2 | wc -l
Tony

5

J'ai également écrit mon propre script pour résoudre ce problème en utilisant l' algorithme de sous-séquence commune la plus longue.

Il est exécuté comme tel

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

Le résultat est en HTML avec une coloration rouge et verte. Les fichiers plus volumineux prennent exponentiellement plus de temps à traiter, mais cela effectue une véritable comparaison caractère par caractère sans vérifier d'abord ligne par ligne.


J'ai trouvé que JLDiff fonctionne beaucoup plus rapidement sous pypy.
Joshua

4

Couleur, caractère niveau diff ouput

Voici ce que vous pouvez faire avec le script ci-dessous et diff-highlight (qui fait partie de git):

Capture d'écran de diff couleur

#!/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édit à la réponse de @ retracile pour la sedmise en évidence)


Il montre une bonne différence sur l'écran du shell, mais comment puis-je voir cette différence dans GVim ??
Hemant Sharma

1
Qu'est-ce que c'est vraiment une question gvim :). command | gvim -fera ce que vous voulez.
Att Righ

Pour référence, diff-highlight semble être inclus dans le cadre de gitmais pas placé sur votre chemin. Une de ma machine où elle vit /usr/share/doc/git/contrib/diff-highlight.
Att Righ

lien rompu. Comment installer diff-highlight. Ne semble pas être dans un gestionnaire de paquets.
Trevor Hickey

3

Le difflib de Python peut le faire.

La documentation comprend un exemple de programme de ligne de commande pour vous.

Le format exact n'est pas celui que vous avez spécifié, mais il serait simple d'analyser la sortie de style ndiff ou de modifier l'exemple de programme pour générer votre notation.


Merci! Je vais l'examiner. J'espérais quelque chose de plus standardisé (comme un argument de ligne de commande caché). Mais cela pourrait tout de même bien se passer. Je vais regarder en python si personne n'a rien de plus standard (bien que cela semble ne pas l'être).
VitalyB

2

Voici un outil de comparaison de texte en ligne: http://text-compare.com/

Il peut mettre en évidence chaque caractère différent et continue de comparer le reste.


Cela semble faire des différences au niveau de la ligne sans option pour les caractères uniques. Comment pouvez-vous comparer des personnages?
Dragon

Ah; il met en évidence des caractères différents. Mais c'est toujours au niveau de la ligne catdoget cat\ndogne correspondra que surcat
Dragon

1

Je pense que la solution la plus simple est toujours une bonne solution. Dans mon cas, le code ci-dessous m'aide beaucoup. J'espère que cela aide quelqu'un d'autre.

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

Vous pouvez comparer deux fichiers avec la syntaxe suivante sur votre terminal préféré:

$ ./diff.py fileNumber1 fileNumber2

0

Si vous conservez vos fichiers dans Git, vous pouvez faire des différences entre les versions avec le script diff-highlight , qui affichera différentes lignes, avec les différences mises en évidence.

Malheureusement, cela ne fonctionne que lorsque le nombre de lignes supprimées correspond au nombre de lignes ajoutées - il existe un code de remplacement pour les lignes ne correspondant pas, donc vraisemblablement cela pourrait être corrigé à l'avenir.


0

Ce n'est pas une réponse complète, mais si cmp -lle résultat n'est pas assez clair, vous pouvez utiliser:

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

sous OSX, utilisez `` sed 's / (.) / \ 1 \' $ '\ n / g' file1> file1.vertical sed 's / \ (. \) / \ 1 \' $ '\ n / g 'file2> file2.vertical `` ``
mmacvicar

0

La plupart de ces réponses mentionnent l'utilisation de diff-highlight , un module Perl. Mais je ne voulais pas savoir comment installer un module Perl. J'y ai donc apporté quelques modifications mineures pour en faire un script Perl autonome.

Vous pouvez l'installer en utilisant:

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

Et l'utilisation (si vous avez l'Ubuntu colordiffmentionné dans la réponse de zhanxw):

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

Et l'utilisation (si vous ne le faites pas):

▶ diff -u f1 f2 | DiffHighlight.pl
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.