File teks saya yang besar (hingga 2 GiB) berisi sekitar 100 duplikat tepat dari setiap baris di dalamnya (tidak berguna dalam kasus saya, karena file tersebut adalah tabel data seperti CSV).
Yang saya butuhkan adalah menghapus semua pengulangan sementara (lebih disukai, tetapi ini dapat dikorbankan untuk meningkatkan kinerja yang signifikan) mempertahankan urutan urutan asli. Hasilnya, setiap baris harus unik. Jika ada 100 baris yang sama (biasanya duplikat tersebar di seluruh file dan tidak akan menjadi tetangga) hanya akan ada satu dari jenis yang tersisa.
Saya telah menulis sebuah program di Scala (anggap saja Java jika Anda tidak tahu tentang Scala) untuk mengimplementasikannya. Tapi mungkin ada alat-alat asli C-ditulis lebih cepat dapat melakukan ini lebih cepat?
UPDATE: awk '!seen[$0]++' filename
solusinya tampaknya bekerja dengan baik bagi saya selama file-file itu dekat 2 GiB atau lebih kecil tapi sekarang karena saya harus membersihkan file 8 GiB itu tidak berfungsi lagi. Tampaknya mengambil infinity pada Mac dengan 4 GiB RAM dan 64-bit Windows 7 PC dengan 4 GiB RAM dan 6 GiB swap hanya kehabisan memori. Dan saya tidak merasa antusias untuk mencobanya di Linux dengan RAM 4 GiB mengingat pengalaman ini.
sort -u
mungkin akan lebih cepat.Jawaban:
Sebuah
awk
solusi yang terlihat pada #bash (Freenode):sumber
awk
menggunakan pencarian 2 array (ditampilkan sebagai penjelasan yang diperluas dalam jawaban Gilles): 0m36.132s vs 0m49.958s .. untuk 50 juta baris .. Saya pikir kemacetan akan menjadi I / O, tetapi pencarian array ekstra adalah ... 1 juta elemen dalam array tampaknya membuat penyok yang cukup signifikan ...Ada metode sederhana (yang tidak bisa dikatakan jelas) menggunakan utilitas standar yang tidak memerlukan memori besar kecuali untuk menjalankan
sort
, yang dalam kebanyakan implementasi memiliki optimasi khusus untuk file besar (algoritma pengurutan eksternal yang baik). Keuntungan dari metode ini adalah bahwa ia hanya melewati semua baris di dalam utilitas tujuan khusus, tidak pernah di dalam bahasa yang ditafsirkan.Jika semua baris dimulai dengan karakter non-spasi, Anda dapat membuang beberapa opsi:
Untuk duplikasi dalam jumlah besar, metode yang hanya membutuhkan penyimpanan satu salinan dari setiap baris dalam memori akan berkinerja lebih baik. Dengan beberapa overhead interpretasi, ada naskah awk yang sangat ringkas untuk itu (sudah diposting oleh enzotib ):
Kurang ringkas:,
!seen[$0] {print} {seen[$0] += 1}
yaitu mencetak baris saat ini jika belum terlihat, kemudian menambahseen
penghitung untuk baris ini (variabel yang tidak diinisialisasi atau elemen array memiliki nilai numerik 0).Untuk antrean panjang, Anda dapat menghemat memori dengan hanya menyimpan checksum yang tidak spoofable (misalnya intisari kriptografi) dari masing-masing baris. Misalnya, menggunakan SHA-1, Anda hanya perlu 20 byte plus overhead konstan per baris. Tetapi menghitung cerna agak lambat; metode ini hanya akan menang jika Anda memiliki CPU yang cepat (terutama yang memiliki akselerator perangkat keras untuk menghitung pencernaan) dan tidak banyak memori relatif terhadap ukuran file dan garis yang cukup panjang. Tidak ada utilitas dasar yang memungkinkan Anda menghitung checksum untuk setiap baris; Anda harus menanggung overhead interpretasi Perl / Python / Ruby / ... atau menulis program yang dikompilasi khusus.
sumber
awk '!seen[$0]++'
, apakah ini berarti bahwa jika awk melihat 2 baris duplikat, itu akan menjaga yang pertama dan mengabaikan semua yang berikutnya? (Atau ini akan mempertahankan yang terakhir?)sort -u
mengubah urutan. Jawaban saya menunjukkan solusi yang menjaga urutan (urutan kejadian pertama, tepatnya).Perhatikan bahwa file output akan diurutkan.
sumber
awk
perintah dalam jawaban lain, tetapi secara konsep sederhana!sort -u
untuk menghapus duplikat selama penyortiran, bukan setelah. (Dan menghemat bandwidth memori) memipangnya ke program lain). Ini hanya lebih baik daripadaawk
versi jika Anda ingin output Anda diurutkan juga. (OP tentang pertanyaan ini ingin agar pemesanan aslinya dipertahankan , jadi ini adalah jawaban yang bagus untuk kasus penggunaan yang sedikit berbeda.)Dengan asumsi Anda mampu menyimpan sebanyak file de-duplikat dalam memori (jika data Anda memang digandakan oleh faktor 100, yang seharusnya sekitar 20MiB + overhead), Anda dapat melakukan ini dengan sangat mudah dengan Perl.
Ini menjaga pesanan juga.
Anda dapat mengekstraksi jumlah kemunculan setiap baris dari
%dup
hash jika diinginkan, sebagai bonus gratis tambahan.Jika Anda mau
awk
, ini juga harus dilakukan (logika yang sama dengan versi perl, urutan yang sama, data yang sama yang dikumpulkan dalamdup
variabel):sumber
uniq
melakukan itu sendiriKarena tidak ada jawaban lain yang disediakan dukungan inplace, berikut adalah salah satu:
sumber
GNU Awk 4.0.2
Anda dapat menggunakan
uniq
http://www.computerhope.com/unix/uuniq.htmuniq
melaporkan atau memfilter baris berulang dalam file.sumber
'uniq' does not detect repeated lines unless they are adjacent.
Jadi, Anda harus mengurutkannya terlebih dahulu dan kehilangan urutan baris yang bukan duplikat.Python One liners:
sumber
OrderedDict
Tidak ada jawaban di sini yang berfungsi untuk saya di Mac, jadi saya menulis skrip python sederhana yang berfungsi untuk saya. Saya mengabaikan spasi putih terkemuka / tertinggal dan juga tidak peduli tentang konsumsi memori.
Simpan di atas ke unique.py dan jalankan seperti ini:
sumber
Dengan bash 4, solusi pure-bash yang memanfaatkan array asosiatif dapat digunakan. Berikut ini sebuah contoh
sumber
read
loop untuk memproses file teks besar. bash harus membaca satu byte per saat untuk menghindari overshooting baris baru. Bash juga tidak terlalu cepat dalam pemrosesan teks secara umum dibandingkan dengan awk. Jika Anda menggunakan ini,read -ra
akan menghindari makan backslash di input Anda. Juga, jangan lupaunset llist
setelah loop, jika Anda menempatkan ini dalam fungsi shell atau menggunakannya secara interaktif.