Sự khác biệt của hai tập tin lớn


14

Tôi có "test1.csv" và nó chứa

200,400,600,800
100,300,500,700
50,25,125,310

và test2.csv và nó chứa

100,4,2,1,7
200,400,600,800
21,22,23,24,25
50,25,125,310
50,25,700,5

hiện nay

diff test2.csv test1.csv > result.csv

khác với

diff test1.csv test2.csv > result.csv

Tôi không biết thứ tự đúng nhưng tôi muốn thứ khác, cả hai lệnh trên sẽ tạo ra thứ gì đó như

2 > 100,4,2,1,7
   3 2,3c3,5
   4 < 100,300,500,700
   5 < 50,25,125,310
   6 \ No newline at end of file
   7 ---
   8 > 21,22,23,24,25
   9 > 50,25,125,310

Tôi muốn chỉ xuất ra sự khác biệt, do đó results.csv sẽ trông như thế này

100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Tôi đã thử diff -qdiff -shọ đã không làm điều đó. Thứ tự không thành vấn đề, điều quan trọng là tôi muốn chỉ thấy sự khác biệt, không> cũng không <cũng như không gian trống.

grep -FvF đã lừa trên các tệp nhỏ hơn không phải trên các tệp lớn

tệp đầu tiên chứa hơn 5 triệu dòng, tệp thứ hai chứa 1300.

vì vậy results.csv sẽ dẫn đến ~ 4,998,700 dòng

Tôi cũng đã thử grep -F -x -v -f mà không làm việc.



1
@Tim tôi thấy liên kết của bạn và tôi là thành viên cũ nên tôi biết các quy tắc nhưng bất cẩn, xin lỗi :) đã chỉnh sửa nó và tôi thấy một cửa sổ bật lên rằng bài đăng đã được chỉnh sửa nên bạn đã làm việc cho tôi và tôi cảm ơn ngài.
Lynob

50,25,125,310là chung cho cả hai tệp..bạn cần xóa tệp đó khỏi đầu ra mong muốn của mình ..
heemayl

Có nên giữ gìn trật tự?
kos

1
loại phụ thuộc vào những gì bạn muốn làm với thông tin, diff, IMO, là để tạo một bản vá. Ở mức nào, tôi chắc chắn về công cụ tốt nhất của bạn, diff, grep, awk hoặc perl.
Panther

Câu trả lời:


20

Nghe có vẻ như một công việc cho comm:

$ comm -3 <(sort test1.csv) <(sort test2.csv)
100,300,500,700
    100,4,2,1,7
    21,22,23,24,25
    50,25,700,5

Như đã giải thích trong man comm:

   -1     suppress column 1 (lines unique to FILE1)

   -2     suppress column 2 (lines unique to FILE2)

   -3     suppress column 3 (lines that appear in both files)

Vì vậy, -3có nghĩa là chỉ các dòng duy nhất cho một trong các tệp sẽ được in. Tuy nhiên, những tệp này được thụt lề theo tệp mà chúng được tìm thấy. Để xóa tab, hãy sử dụng:

$ comm -3 <(sort test1.csv) <(sort test2.csv) | tr -d '\t'
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Trong trường hợp này, bạn thậm chí không cần phải sắp xếp các tệp và bạn có thể đơn giản hóa phần trên để:

comm -3 test1.csv test2.csv | tr -d '\t' > difference.csv

Bạn đã không bị lừa bởi khoảng trống sau 200,[...]dòng hả? :)
kos

@kos không, tôi đã xóa dấu cách từ các tệp trước. Tôi giả sử các tệp của OP không thực sự có chúng.
terdon

6

Sử dụng grepvới bashquá trình thay thế:

$ cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv)
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Để lưu đầu ra dưới dạng results.csv:

cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv) >results.csv
  • <()bashmô hình thay thế quá trình

  • grep -vFf test2.csv test1.csv sẽ tìm thấy các dòng duy nhất chỉ test1.csv

  • grep -vFf test1.csv test2.csv sẽ tìm thấy các dòng duy nhất chỉ test2.csv

  • Cuối cùng, chúng tôi đang tổng hợp kết quả bằng cách cat

Hoặc như Oli đề xuất , bạn cũng có thể sử dụng nhóm lệnh:

$ { grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv; }
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

Hoặc chỉ chạy từng cái một, vì cả hai đều viết cho STDOUT, cuối cùng chúng sẽ được thêm vào:

$ grep -vFf test2.csv test1.csv; grep -vFf test1.csv test2.csv
100,300,500,700
100,4,2,1,7
21,22,23,24,25
50,25,700,5

1
Tại sao cathai lệnh chuyển hướng? Tại sao không chạy cái này rồi cái kia? grep ... ; grep ...hoặc { grep ... ; grep ... ; }nếu bạn muốn làm một cái gì đó với đầu ra tập thể.
Oli

@Oli Cảm ơn..có một ý tưởng tuyệt vời..tôi không nghĩ về điều đó ..
heemayl

4

Nếu thứ tự của các hàng không liên quan, sử dụng awkhoặc perl:

awk '{seen[$0]++} END {for (i in seen) {if (seen[i] == 1) {print i}}}' 1.csv 2.csv

Sử dụng grepđể lấy các dòng chung và lọc chúng ra:

grep -hxvFf <(grep -Fxf 1.csv 2.csv) 1.csv 2.csv

Grep nội bộ nhận được các dòng chung, sau đó grep bên ngoài tìm thấy các dòng không khớp với các dòng chung này.


Lệnh awk của bạn chỉ thực hiện lại sort | uniq -u, đưa ra câu trả lời sai khi một tệp chứa các dòng trùng lặp. Đối với grep, tôi sẽ nói "bên trong" / "bên ngoài", không phải "bên trong" / "bên ngoài".
Peter Cordes

@PeterCordes có, nó là ai và bạn nói ai là kết quả sai?
muru

Sai theo nghĩa là nó không chính xác những gì câu hỏi yêu cầu, trong trường hợp góc đó. Nó có thể là những gì ai đó muốn, nhưng bạn nên chỉ ra sự khác biệt giữa những gì bạn awksẽ in và những gì comm -3diffcâu trả lời sẽ in.
Peter Cordes

@PeterCordes một lần nữa, bạn là ai để nói điều đó? Cho đến khi OP nói rằng đó là những gì họ muốn, tôi không quan tâm nếu đầu ra khác với comm -3. Tôi không thấy bất kỳ lý do tại sao tôi nên giải thích điều đó. Nếu bạn muốn chỉnh sửa trong một ghi chú, hãy thoải mái.
muru

OP cho biết anh muốn sự khác biệt. Đó không phải lúc nào cũng là những gì chương trình của bạn tạo ra. Một chương trình tạo ra cùng một đầu ra cho một testcase, nhưng không thỏa mãn mô tả như được viết cho tất cả các trường hợp, đòi hỏi phải có một cái đầu lên. Tôi ở đây để nói điều đó, và nó đúng bất kể tôi là ai hay bạn là ai. Tôi đã thêm một ghi chú.
Peter Cordes

4

Sử dụng các --*-line-format=...tùy chọn củadiff

Bạn có thể nói diffchính xác những gì bạn cần - giải thích dưới đây:

diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' f1.txt f2.txt

Có thể chỉ định đầu ra của diff theo cách rất chi tiết, tương tự như printfđịnh dạng số.

Các dòng từ tệp đầu tiên, test1.csvđược gọi là các dòng "cũ" và các dòng từ thứ hai test2.csv, là các dòng "mới". Điều đó có ý nghĩa khi diffđược sử dụng để xem những gì đã thay đổi trong một tập tin.

Các tùy chọn chúng ta cần là những tùy chọn để đặt định dạng cho dòng "cũ", dòng "mới" và dòng "không thay đổi".
Các định dạng chúng ta cần rất đơn giản:
Đối với các dòng thay đổi, mới và cũ, chúng tôi chỉ muốn xuất văn bản của các dòng. %Llà biểu tượng định dạng cho văn bản dòng.
Đối với các dòng không thay đổi, chúng tôi muốn hiển thị không có gì.

Với điều này, chúng ta có thể viết các tùy chọn như --old-line-format='%L'và kết hợp tất cả lại với nhau, sử dụng dữ liệu mẫu của bạn:

$ diff --old-line-format='%L' --new-line-format='%L' --unchanged-line-format='' test1.csv test2.csv
100,4,2,1,7
100,300,500,700
21,22,23,24,25
50,25,700,5


Ghi chú về hiệu suất

Bởi vì các tệp có kích thước khác nhau, hãy thử trao đổi các tệp đầu vào nếu nó không quan trọng, có thể là các hoạt động bên trong diffcó thể xử lý một cách tốt hơn so với cách khác. Tốt hơn là cần ít bộ nhớ hơn, hoặc tính toán ít hơn.

Có một tùy chọn tối ưu hóa để sử dụng diffvới các tệp lớn : --speed-large-files. Nó sử dụng các giả định về cấu trúc tệp, vì vậy không rõ liệu nó có giúp ích trong trường hợp của bạn hay không, nhưng đáng để thử.

Các tùy chọn định dạng được mô tả trong phần man diffdưới --LTYPE-line-format=LFMT.


3

Vì thứ tự không cần phải được bảo tồn, chỉ cần:

sort test1.csv test2.csv | uniq -u
  • sort test1.csv test2.csv: hợp nhất và sắp xếp test1.csvtest2.csv
  • uniq -u: chỉ in các dòng không trùng lặp

Điều đó không hoạt động nếu một tệp chứa một dòng hai lần, không xuất hiện trong tệp khác. Cả hai lần xuất hiện sẽ có diffkết quả.
Volker Siegel
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.