Sử dụng 'diff' (hoặc bất kỳ thứ gì khác) để nhận được sự khác biệt ở cấp độ ký tự giữa các tệp văn bản


91

Tôi muốn sử dụng 'diff' để có sự khác biệt cả hai dòng giữa và sự khác biệt về ký tự. Ví dụ, hãy xem xét:

Tệp 1

abcde
abc
abcccd

Tệp 2

abcde
ab
abccc

Sử dụng diff -u tôi nhận được:

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

Tuy nhiên, nó chỉ cho tôi thấy đó là những thay đổi trong những dòng này. Những gì tôi muốn thấy là một cái gì đó như:

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

Bạn hiểu được sự trôi dạt của tôi.

Bây giờ, tôi biết tôi có thể sử dụng các động cơ khác để đánh dấu / kiểm tra sự khác biệt trên một dòng cụ thể. Nhưng tôi muốn sử dụng một công cụ làm được tất cả.


2
per char diff đặc biệt hữu ích khi nói đến văn bản CJK, nơi không có khoảng trắng được áp dụng cho việc tách từ.
把 友情 留 在 无 盐

Câu trả lời:


72

Git có một khác biệt từ và việc xác định tất cả các ký tự như các từ sẽ mang lại cho bạn một khác biệt về ký tự. Tuy nhiên, các thay đổi dòng mới bị bỏ qua .

Thí dụ

Tạo một kho lưu trữ như thế này:

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

Bây giờ, hãy làm git diff --word-diff=color --word-diff-regex=. master^ mastervà bạn sẽ nhận được:

git diff

Lưu ý cách cả phần thêm và phần xóa đều được nhận dạng ở cấp ký tự, trong khi cả phần thêm và xóa dòng mới đều bị bỏ qua.

Bạn cũng có thể muốn thử một trong những cách sau:

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

73
Bạn không cần phải tạo một repo nào cả, bạn có thể chỉ cần cung cấp git diff hai tệp bất kỳ, ở bất kỳ đâu trên hệ thống tệp của bạn và nó hoạt động. Lệnh của bạn phù hợp với tôi theo cách đó, vì vậy cảm ơn! git diff --word-diff=color --word-diff-regex=. file1 file2
qwertzguy

1
Điều này vô cùng hữu ích! Sẽ +1 một lần với tư cách là nhà phát triển phần mềm và +1 hai lần nữa với tư cách là tác giả / nhà văn nếu tôi có thể. Không giống như trong mã, nơi các dòng có xu hướng ngắn một cách hợp lý, khi viết báo / câu chuyện, mỗi đoạn văn có xu hướng ở dạng một dòng dài được bao bọc bởi các từ và tính năng này làm cho các khác biệt thực sự hữu ích về mặt hình ảnh.
mtraceur

28
Tôi cần thêm --no-indexphản hồi của @ qwertzguys ở trên để khiến nó hoạt động cho tôi bên ngoài git repo. Vì vậy:git diff --no-index --word-diff=color --word-diff-regex=. file1 file2
Nathan Bell

2
git diff không hoạt động trong cài đặt chung: git diff --no-index --word-diff = color --word-diff-regex =. <(echo string1) <(echo string2) .. Không có gì, nhưng điều này hoạt động: diff --color <(echo string1) <(echo string2).
mosh

1
@NathanBell tôi cần phải thêm --no-indexbên trong một repo quá
JShorthouse

32

Bạn có thể dùng:

diff -u f1 f2 |colordiff |diff-highlight

ảnh chụp màn hình

colordifflà một gói Ubuntu. Bạn có thể cài đặt nó bằng cách sử dụng sudo apt-get install colordiff.

diff-highlightlà từ git (kể từ phiên bản 2.9). Nó nằm ở /usr/share/doc/git/contrib/diff-highlight/diff-highlight. Bạn có thể đặt nó ở đâu đó trong của bạn $PATH.


6
colordiff cũng có sẵn trên homebrew cho Mac:brew install colordiff
Emil Stenström

5
Trên Mac, bạn có thể tìm thấy diff-highlighttrong$(brew --prefix git)/share/git-core/contrib/diff-highlight/diff-highlight
StefanoP

2
Trong trường hợp bạn không cài đặt git sử dụng brew - diff-highlightcũng có thể được cài đặt với pip python - pip install diff-highlight(Tôi thích nó ngay cả khi git được cài đặt thông qua brew)
Yaron U.

21

Difflib của Python là tuyệt vời nếu bạn muốn làm điều này theo chương trình. Để sử dụng tương tác, tôi sử dụng chế độ khác biệt của vim (đủ dễ sử dụng: chỉ cần gọi vim bằng vimdiff a b). Tôi cũng thường xuyên sử dụng Beyond Compare , nó thực hiện khá nhiều thứ bạn có thể hy vọng từ một công cụ khác biệt.

Tôi không thấy bất kỳ công cụ dòng lệnh nào thực hiện điều này một cách hữu ích, nhưng như Will lưu ý, mã ví dụ difflib có thể hữu ích.


1
Oh .. Tôi đã hy vọng một cái gì đó chuẩn hóa hơn (như đối số dòng lệnh ẩn). Điều tuyệt vời nhất là tôi có Beyond Compare 2 và nó thậm chí còn hỗ trợ xuất văn bản ra tệp / bảng điều khiển của khác biệt nhưng nó vẫn chỉ bao gồm line-diffs chứ không phải char-diffs. Tôi sẽ xem xét python nếu không ai có bất cứ điều gì khác.
VitalyB

6
+1 vì đã giới thiệu tôi với vimdiff. Tôi thấy các màu mặc định không thể đọc được, nhưng đã tìm thấy giải pháp cho điều đó tại stackoverflow.com/questions/2019281/… .
không xác định

17

Bạn có thể sử dụng cmplệnh trong Solaris:

cmp

So sánh hai tệp và nếu chúng khác nhau, hãy cho biết byte và số dòng đầu tiên mà chúng khác nhau.


2
cmpcũng có sẵn trên (ít nhất một số) bản phân phối Linux.
Jeff Evans

7
Nó cũng có sẵn trên Mac OS X.
Eric R. Rath

Các ký tự có thể bao gồm nhiều byte và OP yêu cầu so sánh trực quan.
Cees Timmerman

1
@CeesTimmerman: cmp cho phép so sánh trực quan, với cờ -l -b.
Smar

9

Python có thư viện tiện lợi được đặt tên difflibcó thể giúp trả lời câu hỏi của bạn.

Dưới đây là hai dòng oneliners sử dụng difflibcho các phiên bản python khác nhau.

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

Những thứ này có thể hữu ích như một bí danh shell giúp bạn dễ dàng di chuyển hơn .${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

Và phiên bản dễ đọc hơn để đưa vào một tệp độc lập.

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

Một lớp lót tuyệt vời. Sẽ rất tuyệt nếu có một đầu ra cô đọng bỏ qua các dòng không thay đổi.
aidan.plenert.macdonald

6
cmp -l file1 file2 | wc

Làm việc tốt cho tôi. Số ngoài cùng bên trái của kết quả cho biết số ký tự khác nhau.


1
Hoặc chỉ lấy số ngoài cùng bên trái:cmp -l file1 file2 | wc -l
Tony

5

Tôi cũng đã viết tập lệnh của riêng mình để giải quyết vấn đề này bằng cách sử dụng thuật toán con chung dài nhất.

Nó được thực hiện như vậy

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

Kết quả là trong html có màu đỏ và xanh lá cây. Các tệp lớn hơn thường mất nhiều thời gian hơn để xử lý nhưng điều này thực hiện so sánh ký tự thực theo ký tự mà không cần kiểm tra từng dòng trước.


Tôi đã thấy rằng JLDiff chạy nhanh hơn rất nhiều dưới pypy.
Joshua

4

Màu, nhân vật cấp diff ouput

Đây là những gì bạn có thể làm với script dưới đây và đánh dấu khác biệt (là một phần của git):

Ảnh chụp màn hình khác màu

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

(Tín dụng cho câu trả lời của @ retracement cho phần sedđánh dấu)


Nó hiển thị khác biệt tốt trên màn hình shell, nhưng làm thế nào để tôi thấy khác biệt đó trong GVim ??
Hemant Sharma

1
Đó thực sự là một câu hỏi gvim :). command | gvim -sẽ làm những gì bạn muốn.
Att Righ

Để tham khảo, phần đánh dấu khác biệt dường như được bao gồm như một phần của gitnhưng không được đặt trên đường dẫn của bạn. Một chiếc máy của tôi, chiếc máy này đang tồn tại /usr/share/doc/git/contrib/diff-highlight.
Att Righ

liên kết bị hỏng. Làm cách nào để cài đặt diff-highlight. Dường như không có trong trình quản lý gói.
Trevor Hickey,

3

Difflib của Python có thể làm điều này.

Tài liệu này bao gồm một chương trình dòng lệnh ví dụ cho bạn.

Định dạng chính xác không như bạn đã chỉ định, nhưng sẽ dễ dàng phân tích cú pháp đầu ra kiểu ndiff hoặc sửa đổi chương trình mẫu để tạo ký hiệu của bạn.


Cảm ơn! Tôi sẽ xem xét nó. Tôi đã hy vọng một cái gì đó chuẩn hóa hơn (như đối số dòng lệnh ẩn). Nhưng nó vẫn có thể làm tốt. Tôi sẽ xem xét python nếu không ai có bất kỳ điều gì tiêu chuẩn hơn (mặc dù có vẻ như không).
VitalyB

2

Đây là một công cụ so sánh văn bản trực tuyến: http://text-compare.com/

Nó có thể làm nổi bật mọi ký tự khác nhau và tiếp tục so sánh các ký tự còn lại.


Điều này dường như tạo ra sự khác biệt ở cấp độ dòng mà không có tùy chọn cho các ký tự đơn lẻ. Làm thế nào để bạn lấy nó để so sánh các ký tự?
Dragon

Ah; nó làm nổi bật các ký tự khác nhau. Nhưng nó vẫn còn dòng cấp trong đó catdogcat\ndogsẽ chỉ phù hợp trêncat
Rồng

1

Tôi nghĩ giải pháp đơn giản hơn luôn là giải pháp tốt. Trong trường hợp của tôi, đoạn mã dưới đây sẽ giúp tôi rất nhiều. Tôi hy vọng nó sẽ giúp bất kỳ ai khác.

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

Bạn có thể so sánh hai tệp với cú pháp sau tại thiết bị đầu cuối yêu thích của mình:

$ ./diff.py fileNumber1 fileNumber2

0

Nếu bạn giữ các tệp của mình trong Git, bạn có thể khác nhau giữa các phiên bản bằng tập lệnh đánh dấu khác biệt , sẽ hiển thị các dòng khác nhau, với các điểm khác biệt được đánh dấu.

Thật không may, nó chỉ hoạt động khi số dòng bị xóa khớp với số dòng được thêm vào - có mã sơ khai khi các dòng không khớp, vì vậy có lẽ điều này có thể được khắc phục trong tương lai.


0

Không phải là một câu trả lời đầy đủ, nhưng nếu cmp -lđầu ra của không đủ rõ ràng, bạn có thể sử dụng:

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

trên OSX sử dụng `` sed 's / (.) / \ 1 \' $ '\ n / g' file1> file1.vertical sed 's / \ (. \) / \ 1 \' $ '\ n / g 'file2> file2.vertical' '
mmacvicar

0

Hầu hết các câu trả lời này đề cập đến việc sử dụng diff-highlight , một mô-đun Perl. Nhưng tôi không muốn tìm cách cài đặt mô-đun Perl. Vì vậy, tôi đã thực hiện một vài thay đổi nhỏ để nó trở thành một tập lệnh Perl độc lập.

Bạn có thể cài đặt nó bằng cách sử dụng:

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

Và cách sử dụng (nếu bạn có Ubuntu colordiffđược đề cập trong câu trả lời của zhanxw):

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

Và cách sử dụng (nếu bạn không):

▶ diff -u f1 f2 | DiffHighlight.pl
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.