Bagaimana cara mengekstrak sebagian file teks biasa berukuran besar?

19

Saya punya file zip dengan ukuran 1,5 GB.

Isinya adalah satu file teks biasa besar yang konyol (60 GB) dan saat ini saya tidak memiliki cukup ruang pada disk saya untuk mengekstraksi semuanya juga tidak ingin mengekstrak semuanya, bahkan jika saya punya.

Sedangkan untuk kasus penggunaan saya, akan cukup jika saya dapat memeriksa bagian dari konten.

Karenanya saya ingin membuka zip file sebagai aliran dan mengakses berbagai file (seperti dapat melalui kepala dan ekor pada file teks normal).

Baik dengan memori (mis. Ekstrak maks 100kb mulai dari tanda 32GB) atau dengan garis (beri saya garis teks biasa 3700-3900).

Apakah ada cara untuk mencapainya?

k0pernikus
sumber
1
Sayangnya tidak mungkin untuk mencari file individual di dalam zip. Jadi soloution apa pun akan melibatkan membaca file hingga titik Anda tertarik.
plugwash
5
@plugwash Seperti yang saya pahami pertanyaannya, tujuannya bukan untuk menghindari membaca file zip (atau bahkan file yang didekompresi), tetapi hanya untuk menghindari menyimpan seluruh file yang didekompresi dalam memori atau pada disk. Pada dasarnya, perlakukan file yang didekompresi sebagai stream .
ShreevatsaR

Jawaban:

28

Catatan yang gzipdapat mengekstrak zipfile (setidaknya entri pertama dalam zipfile). Jadi jika hanya ada satu file besar di arsip itu, Anda dapat melakukannya:

gunzip < file.zip | tail -n +3000 | head -n 20

Untuk mengekstrak 20 baris dimulai dengan 3000 baris misalnya.

Atau:

gunzip < file.zip | tail -c +3000 | head -c 20

Untuk hal yang sama dengan byte (dengan asumsi headimplementasi yang mendukung -c).

Untuk setiap anggota yang sewenang-wenang dalam arsip, dengan cara Unixy:

bsdtar xOf file.zip file-to-extract | tail... | head...

Dengan headbawaan ksh93(seperti saat /opt/ast/binada di depan $PATH), Anda juga dapat melakukan:

.... | head     -s 2999      -c 20
.... | head --skip=2999 --bytes=20

Perhatikan bahwa dalam setiap kasus gzip/ bsdtar/ unzipakan selalu perlu membuka kompresi (dan membuang di sini) seluruh bagian file yang mengarah ke bagian yang ingin Anda ekstrak. Begitulah cara kerja algoritma kompresi.

Stéphane Chazelas
sumber
Jika gzipbisa mengatasinya, akan yang lain "z sadar" utilitas ( zcat, zless, dll) juga bekerja?
ivanivan
@ivanivan, pada sistem di mana mereka didasarkan gzip(umumnya benar zless, tidak harus zcatyang pada beberapa sistem masih membaca .Zfile saja), ya.
Stéphane Chazelas
14

Satu solusi menggunakan unzip -p dan dd, misalnya untuk mengekstrak 10kb dengan 1000 blok offset:

$ unzip -p my.zip | dd ibs=1024 count=10 skip=1000 > /tmp/out

Catatan: Saya tidak mencoba ini dengan data yang sangat besar ...

tonioc
sumber
Dalam kasus umum lebih dari satu kali file di dalam arsip tunggal yang dapat digunakan unzip -l ARCHIVEuntuk mendaftar konten arsip dan unzip -p ARCHIVE PATHuntuk mengekstraksi konten objek tunggal PATHke stdout.
David Foerster
3
Umumnya, menggunakan ddpada pipa dengan hitungan atau melewatkan tidak dapat diandalkan karena akan melakukan itu banyak read()s dari hingga 1024 byte. Jadi itu hanya dijamin untuk bekerja dengan baik jika unzipmenulis ke pipa dalam potongan yang ukurannya adalah kelipatan 1024.
Stéphane Chazelas
4

Jika Anda memiliki kendali atas pembuatan file zip besar itu, mengapa tidak mempertimbangkan menggunakan kombinasi dari gzipdanzless ?

Ini akan memungkinkan Anda untuk menggunakan zless sebagai pager dan melihat isi file tanpa harus repot dengan ekstraksi.

Jika Anda tidak dapat mengubah format kompresi maka ini jelas tidak akan berhasil. Jika demikian, saya merasa zlessagak nyaman.

111 ---
sumber
1
Bukan saya. Saya mengunduh file zip yang disediakan oleh perusahaan eksternal.
k0pernikus
3

Untuk melihat baris tertentu dari file, pipa output ke editor aliran Unix, sed . Ini dapat memproses aliran data besar secara sewenang-wenang, sehingga Anda bahkan dapat menggunakannya untuk mengubah data. Untuk melihat baris 3700-3900 saat Anda bertanya, jalankan yang berikut ini.

unzip -p file.zip | sed -n 3700,3900p
Diomidis Spinellis
sumber
7
sed -n 3700,3900pakan terus membaca hingga akhir file. Lebih baik digunakan sed '3700,$!d;3900q'untuk menghindari itu, atau bahkan secara umum lebih efisien:tail -n +3700 | head -n 201
Stéphane Chazelas
3

Saya bertanya-tanya apakah mungkin untuk melakukan sesuatu yang lebih efisien daripada mendekompresi dari awal file sampai ke titik. Tampaknya jawabannya adalah tidak. Namun, pada beberapa CPU (Skylake) zcat | tailtidak meningkatkan CPU hingga kecepatan jam penuh. Lihat di bawah. Decoder kustom dapat menghindari masalah itu dan menghemat panggilan sistem penulisan pipa, dan mungkin ~ 10% lebih cepat. (Atau ~ 60% lebih cepat di Skylake jika Anda tidak mengubah pengaturan manajemen daya).


Yang terbaik yang dapat Anda lakukan dengan zlib yang disesuaikan dengan a skipbytes fungsi adalah mengurai simbol dalam blok kompresi untuk sampai ke akhir tanpa melakukan pekerjaan merekonstruksi blok dekompresi. Ini bisa lebih cepat secara signifikan (mungkin setidaknya 2x) daripada memanggil fungsi decode reguler zlib untuk menimpa buffer yang sama dan bergerak maju dalam file. Tapi saya tidak tahu apakah ada yang menulis fungsi seperti itu. (Dan saya pikir ini tidak benar-benar berfungsi kecuali file itu ditulis khusus untuk memungkinkan decoder untuk memulai kembali pada blok tertentu).

Saya berharap ada cara untuk melewati blok Deflate tanpa memecahkan kode mereka, karena itu akan jauh lebih cepat. Pohon Huffman dikirim pada awal setiap blok, sehingga Anda dapat memecahkan kode dari awal setiap blok (saya pikir). Oh, saya pikir negara decoder lebih dari pohon Huffman, itu juga 32kiB data yang diterjemahkan sebelumnya, dan ini tidak diatur ulang / dilupakan melintasi batas blok secara default. Byte yang sama dapat terus dirujuk berulang kali, jadi mungkin hanya muncul sekali dalam satu file terkompresi raksasa. (mis. dalam file log, nama host mungkin tetap "panas" dalam kamus kompresi sepanjang waktu, dan setiap contoh merujuk pada yang sebelumnya, bukan yang pertama).

The zlibpengguna mengatakan Anda harus menggunakan Z_FULL_FLUSHsaat memanggil deflatejika Anda ingin aliran dikompresi menjadi seekable ke titik itu. Ini "me-reset kondisi kompresi", jadi saya pikir tanpa itu, referensi mundur dapat masuk ke blok sebelumnya. Jadi kecuali file zip Anda ditulis dengan blok full-flush sesekali (seperti setiap 1G atau sesuatu akan memiliki dampak yang dapat diabaikan pada kompresi), saya pikir Anda harus melakukan lebih banyak pekerjaan pengodean ulang ke titik yang Anda inginkan daripada yang saya awalnya berpikir. Saya kira Anda mungkin tidak dapat memulai di awal blok apa pun.


Sisa dari ini ditulis ketika saya berpikir mungkin untuk menemukan awal dari blok yang berisi byte pertama yang Anda inginkan, dan decode dari sana.

Namun sayangnya, awal blok Deflate tidak menunjukkan berapa lama , untuk blok terkompresi. Data yang tidak dapat dikompres dapat dikodekan dengan tipe blok terkompresi yang memiliki ukuran 16-bit dalam byte di depan, tetapi blok terkompresi tidak: RFC 1951 menggambarkan format yang cukup mudah dibaca . Blok dengan pengkodean Huffman dinamis memiliki pohon di bagian depan blok (sehingga decompressor tidak harus mencari dalam aliran), sehingga kompresor harus menyimpan seluruh (terkompresi) blok dalam memori sebelum menulisnya.

Jarak referensi mundur maksimum hanya 32kiB, sehingga kompresor tidak perlu menyimpan banyak data yang tidak terkompresi dalam memori, tetapi itu tidak membatasi ukuran blok. Panjang blok bisa beberapa megabyte. (Ini cukup besar untuk disk mencari nilai bahkan pada drive magnetik, vs. membaca sekuensial ke dalam memori dan hanya melewatkan data dalam RAM, jika mungkin untuk menemukan akhir dari blok saat ini tanpa menguraikannya).

zlib membuat blok selama mungkin: Menurut Marc Adler , zlib hanya memulai blok baru ketika buffer simbol terisi, yang dengan pengaturan default adalah 16.383 simbol (literal atau korek api)


Saya gzip output seq(yang sangat berlebihan dan dengan demikian mungkin bukan tes yang bagus), tetapi pv < /tmp/seq1G.gz | gzip -d | tail -c $((1024*1024*1000)) | wc -chanya berjalan pada ~ 62 MiB / s data terkompresi pada Skylake i7-6700k di 3,9GHz, dengan DDR4-2666 RAM. Itu 246MiB / s dari data yang terkompresi, yang merupakan perubahan besar dibandingkan dengan memcpykecepatan ~ 12 GiB / s untuk ukuran blok yang terlalu besar untuk disimpan dalam cache.

(Dengan energy_performance_preferenceset ke default balance_powersebagai gantinya balance_performance, gubernur CPU internal Skylake memutuskan untuk hanya berjalan pada 2.7GHz, ~ 43 MiB / s data terkompresi. Saya gunakan sudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'untuk mengubah itu. Mungkin panggilan sistem yang sering seperti itu tidak terlihat seperti terikat CPU nyata bekerja ke unit manajemen daya.)

TL: DR: zcat | tail -cadalah CPU yang terikat bahkan pada CPU yang cepat, kecuali jika Anda memiliki disk yang sangat lambat. gzip menggunakan 100% CPU yang digunakannya (dan menjalankan 1,81 instruksi per jam, menurut perf), dan tailmenggunakan 0,162 CPU yang digunakannya (0,58 IPC). Sistem itu sebagian besar menganggur.

Saya menggunakan Linux 4.14.11-1-ARCH, yang memiliki KPTI diaktifkan secara default untuk bekerja di sekitar Meltdown, jadi semua writepanggilan sistem di gziplebih mahal daripada sebelumnya: /


Memiliki pencarian bawaan untuk unzipatau zcat(tetapi masih menggunakan zlibfungsi decode biasa ) akan menyimpan semua pipa tersebut, dan akan membuat CPU Skylake berjalan pada kecepatan jam penuh. (Ini downclocking untuk beberapa jenis beban adalah unik untuk Intel Skylake dan kemudian, yang memiliki beban pengambilan keputusan frekuensi CPU dari OS, karena mereka memiliki lebih banyak data tentang apa yang CPU lakukan, dan dapat meningkatkan / menurunkan lebih cepat. Ini adalah biasanya baik, tetapi di sini mengarah ke Skylake yang tidak mencapai kecepatan penuh dengan pengaturan gubernur yang lebih konservatif).

Tidak ada panggilan sistem, hanya menulis ulang buffer yang sesuai dengan cache L2 sampai Anda mencapai posisi byte awal yang Anda inginkan, mungkin akan membuat perbedaan setidaknya beberapa%. Mungkin bahkan 10%, tapi saya hanya membuat angka di sini. Saya belum memprofilkan zlibdetail untuk melihat seberapa besar jejak cache yang dimilikinya, dan seberapa banyak TLB flush (dan dengan demikian uop-cache flush) pada setiap panggilan sistem sakit dengan KPTI diaktifkan.


Ada beberapa proyek perangkat lunak yang menambahkan indeks pencarian ke format file gzip . Ini tidak membantu Anda jika Anda tidak dapat membuat siapa pun menghasilkan file terkompresi yang dapat dicari untuk Anda, tetapi pembaca lain di masa mendatang mungkin mendapat manfaat.

Agaknya tak satu pun dari proyek-proyek ini memiliki fungsi decode yang tahu bagaimana untuk melewati melalui aliran Deflate tanpa indeks, karena mereka hanya dirancang untuk bekerja ketika indeks adalah tersedia.

Peter Cordes
sumber
1

Anda dapat membuka file zip dalam sesi python, menggunakan zf = zipfile.ZipFile(filename, 'r', allowZip64=True)dan setelah dibuka Anda dapat membuka, untuk membaca, file apa pun di dalam arsip zip dan membaca baris, dll., Dari itu seolah-olah itu adalah file normal.

Steve Barnes
sumber