Perbedaan dua file besar

14

Saya memiliki "test1.csv" dan isinya

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

dan test2.csv dan itu berisi

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

sekarang

diff test2.csv test1.csv > result.csv

berbeda dari

diff test1.csv test2.csv > result.csv

Saya tidak tahu yang mana urutan yang benar tetapi saya menginginkan yang lain, kedua perintah di atas akan menampilkan sesuatu seperti

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

Saya ingin hanya menghasilkan perbedaan, sehingga results.csv akan terlihat seperti ini

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

Saya mencoba diff -qdan diff -stetapi mereka tidak melakukan trik. Ketertiban tidak masalah, yang penting adalah aku hanya ingin melihat perbedaannya, tidak ada> atau <atau ruang kosong.

grep -FvF melakukan trik pada file yang lebih kecil bukan pada file besar

file pertama berisi lebih dari 5 juta baris, file kedua berisi 1300.

jadi results.csv akan menghasilkan ~ 4.998.700 baris

Saya juga mencoba grep -F -x -v -f yang tidak berhasil.

Lynob
sumber
1
@Tim saya melihat tautan Anda dan saya adalah anggota lama sehingga saya tahu aturannya tetapi ceroboh, maaf :) sedang mengeditnya, dan saya melihat sembulan bahwa tulisan itu diedit sehingga Anda melakukan pekerjaan untuk saya dan saya terima kasih pak.
Lynob
50,25,125,310umum untuk kedua file .. Anda harus menghapus itu dari output yang Anda inginkan ..
heemayl
Haruskah pesanan dipertahankan?
kos
1
semacam tergantung pada apa yang ingin Anda lakukan dengan informasi, diff, IMO, adalah untuk membuat tambalan. Bagaimanapun, saya sekarang yakin dengan alat terbaik Anda, diff, grep, awk, atau perl.
Panther

Jawaban:

20

Kedengarannya seperti pekerjaan untuk 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

Sebagaimana dijelaskan dalam 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)

Jadi, -3berarti hanya baris yang unik untuk salah satu file yang akan dicetak. Namun, itu diberi indentasi sesuai dengan file tempat mereka ditemukan. Untuk menghapus tab, gunakan:

$ 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

Dalam hal ini, Anda bahkan tidak perlu mengurutkan file dan Anda dapat menyederhanakan di atas untuk:

comm -3 test1.csv test2.csv | tr -d '\t' > difference.csv
terdon
sumber
Anda belum tertipu oleh spasi setelah 200,[...]baris ya? :)
kos
@kos tidak, saya menghapus spasi tambahan dari file terlebih dahulu. Saya berasumsi file OP tidak benar-benar memilikinya.
terdon
6

Menggunakan grepdengan bashsubstitusi proses:

$ 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

Untuk menyimpan output sebagai results.csv:

cat <(grep -vFf test2.csv test1.csv) <(grep -vFf test1.csv test2.csv) >results.csv
  • <()adalah bashpola substitusi proses

  • grep -vFf test2.csv test1.csv akan menemukan garis yang unik hanya test1.csv

  • grep -vFf test1.csv test2.csv akan menemukan garis yang unik hanya test2.csv

  • Akhirnya kami menyimpulkan hasilnya cat

Atau seperti yang disarankan Oli , Anda dapat menggunakan pengelompokan perintah juga:

$ { 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

Atau jalankan satu demi satu, karena keduanya menulis ke STDOUT, mereka akhirnya akan ditambahkan:

$ 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
heemayl
sumber
1
Mengapa catdua perintah dialihkan? Mengapa tidak menjalankan satu saja lalu yang lainnya? grep ... ; grep ...atau { grep ... ; grep ... ; }jika Anda ingin melakukan sesuatu dengan hasil kolektif.
Oli
@Oli Terima kasih..Itu ide yang bagus..aku tidak memikirkan itu ..
heemayl
4

Jika urutan baris tidak relevan, gunakan awkatau perl:

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

Gunakan grepuntuk mendapatkan garis yang umum dan memfilternya:

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

Grep internal mendapatkan garis yang sama, kemudian grep eksternal menemukan garis yang tidak cocok dengan garis umum ini.

muru
sumber
Perintah awk Anda baru saja mengimplementasikan kembali sort | uniq -u, yang memberikan jawaban yang salah ketika satu file berisi duplikat baris. Untuk grep, saya akan mengatakan "dalam" / "luar", bukan "internal" / "eksternal".
Peter Cordes
@PeterCordes ya, ya dan siapa Anda mengatakan itu hasil yang salah?
muru
Salah dalam arti bahwa itu bukan pertanyaan yang diminta, dalam kasus sudut itu. Mungkin apa yang diinginkan seseorang, tetapi Anda harus menunjukkan perbedaan antara apa yang Anda awkakan mencetak dan apa comm -3dan diffjawaban akan mencetak.
Peter Cordes
@PeterCordes lagi, siapa yang akan Anda katakan itu? Sampai OP mengatakan itu yang mereka inginkan, saya tidak peduli apakah outputnya berbeda dari itu comm -3. Saya tidak melihat alasan mengapa saya harus menjelaskan itu. Jika Anda ingin mengedit dalam catatan, jangan ragu.
muru
OP mengatakan dia menginginkan perbedaan. Itu tidak selalu yang dihasilkan oleh program Anda. Sebuah program yang menghasilkan output yang sama untuk satu testcase, tetapi tidak memenuhi deskripsi seperti yang ditulis untuk semua kasus, memerlukan kepala di atas. Saya di sini untuk mengatakan itu, dan itu benar terlepas dari siapa saya atau siapa Anda. Saya menambahkan catatan.
Peter Cordes
4

Gunakan --*-line-format=...opsidiff

Anda dapat mengetahui dengan difftepat apa yang Anda butuhkan - dijelaskan di bawah:

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

Dimungkinkan untuk menentukan keluaran diff dengan cara yang sangat terperinci, mirip dengan printfformat angka.

Baris dari file pertama, test1.csvdisebut baris "lama", dan baris dari file kedua test2.csv, adalah "baru". Itu masuk akal ketika diffdigunakan untuk melihat apa yang berubah dalam file.

Opsi yang kita butuhkan adalah yang mengatur format untuk baris "lama", "baru", dan "tidak berubah".
Format yang kami butuhkan sangat sederhana:
Untuk baris yang diubah, baru dan lama, kami hanya ingin menampilkan teks dari baris tersebut. %Ladalah simbol format untuk teks baris.
Untuk garis yang tidak berubah, kami tidak ingin menunjukkan apa pun.

Dengan ini, kita dapat menulis opsi seperti --old-line-format='%L', dan menggabungkan semuanya, menggunakan contoh data Anda:

$ 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


Catatan tentang kinerja

Karena file memiliki ukuran yang berbeda, cobalah untuk bertukar file input jika tidak masalah, bisa jadi itu cara kerja dalam diff dapat menangani satu cara lebih baik daripada yang lain. Lebih baik membutuhkan lebih sedikit memori, atau lebih sedikit komputasi.

Ada opsi pengoptimalan untuk digunakan diff dengan file besar: --speed-large-files. Ini menggunakan asumsi tentang struktur file, jadi tidak jelas apakah itu membantu dalam kasus Anda, tetapi layak untuk dicoba.

Opsi format dijelaskan di man diffbawah --LTYPE-line-format=LFMT.

Volker Siegel
sumber
3

Karena pesanan tidak perlu dipertahankan, cukup:

sort test1.csv test2.csv | uniq -u
  • sort test1.csv test2.csv: gabungan dan macamnya test1.csvdantest2.csv
  • uniq -u: hanya mencetak garis yang tidak memiliki duplikat
kos
sumber
Itu tidak berfungsi jika satu file berisi satu baris dua kali, itu tidak muncul di file lain. Kedua kejadian akan diffmenghasilkan.
Volker Siegel