Misalkan saya memiliki file (sebut saja sample.txt) yang terlihat seperti ini:
Row1,10
Row2,20
Row3,30
Row4,40
Saya ingin dapat bekerja pada aliran dari file ini yang pada dasarnya adalah kombinasi berpasangan dari keempat baris (jadi kita harus berakhir dengan total 16). Sebagai contoh, saya mencari perintah streaming (yaitu efisien) di mana outputnya adalah:
Row1,10 Row1,10
Row1,10 Row2,20
Row1,10 Row3,30
Row1,10 Row4,40
Row2,20 Row1,10
Row1,20 Row2,20
...
Row4,40 Row4,40
Kasus penggunaan saya adalah bahwa saya ingin mengalirkan output ini ke perintah lain (seperti awk) untuk menghitung beberapa metrik tentang kombinasi berpasangan ini.
Saya memiliki cara untuk melakukan ini dalam awk tetapi kekhawatiran saya adalah bahwa saya menggunakan blok END {} berarti bahwa saya pada dasarnya menyimpan seluruh file dalam memori sebelum saya output. Kode contoh:
awk '{arr[$1]=$1} END{for (a in arr){ for (a2 in arr) { print arr[a] " " arr[a2]}}}' samples/rows.txt
Row3,30 Row3,30
Row3,30 Row4,40
Row3,30 Row1,10
Row3,30 Row2,20
Row4,40 Row3,30
Row4,40 Row4,40
Row4,40 Row1,10
Row4,40 Row2,20
Row1,10 Row3,30
Row1,10 Row4,40
Row1,10 Row1,10
Row1,10 Row2,20
Row2,20 Row3,30
Row2,20 Row4,40
Row2,20 Row1,10
Row2,20 Row2,20
Apakah ada cara streaming yang efisien untuk melakukan ini tanpa harus dasarnya menyimpan file dalam memori dan kemudian output di blok END?
sumber
Jawaban:
Berikut cara melakukannya dalam awk sehingga tidak perlu menyimpan seluruh file dalam sebuah array. Ini pada dasarnya adalah algoritma yang sama dengan terdon.
Jika Anda suka, Anda bahkan dapat memberikan beberapa nama file pada baris perintah dan itu akan memproses setiap file secara independen, menyatukan hasilnya bersama-sama.
Pada sistem saya, ini berjalan sekitar 2/3 waktu solusi perl terdon.
sumber
Saya tidak yakin ini lebih baik daripada melakukannya di memori, tetapi dengan
sed
yangr
keluar infile untuk setiap baris di infile dan yang lain di sisi lain dari pipa bergantianH
ruang lama dengan jalur input ...KELUARAN
Saya melakukan ini dengan cara lain. Itu menyimpan beberapa dalam memori - menyimpan string seperti:
... untuk setiap baris dalam file.
Ini sangat cepat. Ini
cat
file sebanyak yang ada baris dalam file ke file|pipe
. Di sisi lain pipa input yang digabungkan dengan file itu sendiri sebanyak ada garis dalam file.The
case
hal ini hanya untuk portabilitas -yash
danzsh
kedua add satu elemen untuk perpecahan, sementaramksh
danposh
kedua satu kalah.ksh
,dash
,busybox
, Danbash
semua perpecahan untuk persis seperti berbagai bidang karena ada nol seperti yang dicetak olehprintf
. Seperti yang ditulis di atas memberikan hasil yang sama untuk setiap shell yang disebutkan di atas pada mesin saya.Jika file tersebut sangat panjang, mungkin ada
$ARGMAX
masalah dengan terlalu banyak argumen yang perlu Anda perkenalkanxargs
atau serupa.Diberikan input yang sama yang saya gunakan sebelum output identik. Tapi, jika saya menjadi lebih besar ...
Itu menghasilkan file yang hampir identik dengan apa yang saya gunakan sebelumnya (tanpa 'Baris') - tetapi pada 1000 baris. Anda dapat melihat sendiri seberapa cepat:
Pada 1000 baris ada beberapa variasi kecil dalam kinerja antara shell -
bash
selalu yang paling lambat - tetapi karena satu-satunya pekerjaan yang mereka lakukan adalah menghasilkan string arg (1000 salinanfilename -
) efeknya minimal. Perbedaan kinerja antarazsh
- seperti di atas - danbash
100 detik di sini.Ini versi lain yang bisa digunakan untuk file dengan panjang berapa pun:
Ini membuat soft-link ke arg pertama
/tmp
dengan nama semi-acak sehingga tidak akan terpaku pada nama file yang aneh. Itu penting karenacat
argumen diberikan ke pipa melaluixargs
.cat
Output disimpan<&3
sementarased
p
meretas setiap baris dalam argumen pertama sebanyak ada baris dalam file itu - dan skripnya juga dimasukkan ke dalamnya melalui pipa. Sekali lagipaste
menggabungkan inputnya, tetapi kali ini hanya diperlukan dua argumen-
lagi untuk input standar dan nama tautannya/dev/fd/3
.Yang terakhir -
/dev/fd/[num]
tautan - harus bekerja pada sistem linux dan banyak lagi selain itu, tetapi jika itu tidak membuat pipa bernama denganmkfifo
dan menggunakan itu malah harus bekerja juga.Hal terakhir yang dilakukannya adalah
rm
tautan lunak yang dibuatnya sebelum keluar.Versi ini sebenarnya masih lebih cepat di sistem saya. Saya kira itu karena meskipun ia mengeksekusi lebih banyak aplikasi, ia mulai menyerahkan argumen mereka segera - padahal sebelum menumpuk semuanya terlebih dahulu.
sumber
ctrl+v; ctrl+j
untuk mendapatkan baris baru seperti yang saya lakukan.. ./file; fn_name
dalam kasus itu.Nah, Anda selalu bisa melakukannya di shell Anda:
Ini jauh lebih lambat daripada
awk
solusi Anda (pada komputer saya, butuh ~ 11 detik untuk 1000 baris, dibandingkan ~ 0,3 detikawk
), tetapi setidaknya tidak pernah menyimpan lebih dari beberapa baris dalam memori.Loop di atas berfungsi untuk data yang sangat sederhana yang Anda miliki dalam contoh Anda. Ini akan tersedak backslash dan akan memakan ruang trailing dan memimpin. Versi yang lebih kuat dari hal yang sama adalah:
Pilihan lain adalah menggunakan
perl
:Script di atas akan membaca setiap baris file input (
-ln
), menyimpannya sebagai$l
, bukasample.txt
lagi, dan cetak setiap baris bersama$l
. Hasilnya adalah semua kombinasi berpasangan sementara hanya 2 baris yang pernah disimpan dalam memori. Di sistem saya, hanya butuh sekitar0.6
detik pada 1000 baris.sumber
echo
mungkin menjadi masalah. Apa yang saya tulis (saya tambahkanprintf
sekarang) harus bekerja dengan mereka semua kan? Adapunwhile
loop, mengapa? Ada apa dengan iniwhile read f; do ..; done < file
? Tentunya Anda tidak menyarankanfor
loop! Apa alternatif lain?Dengan
zsh
:$^a
pada array mengaktifkan ekspansi brace-like (seperti dalam{elt1,elt2}
) untuk array.sumber
Anda dapat mengkompilasi kode c ++ ini untuk hasil yang cukup cepat.
Ini selesai dalam sekitar 0,19 - 0,27 detik pada file 1000 baris.
Saat ini membaca
10000
baris ke memori (untuk mempercepat pencetakan ke layar) yang jika Anda memiliki1000
karakter per baris akan menggunakan kurang dari10mb
memori yang saya tidak akan berpikir akan menjadi masalah. Anda dapat menghapus bagian itu sepenuhnya dan hanya mencetak langsung ke layar jika hal itu menyebabkan masalah.Anda dapat mengkompilasi menggunakan
g++ -o "NAME" "NAME.cpp"
Di mana
NAME
nama File untuk menyimpannya danNAME.cpp
merupakan file tempat kode ini disimpanCTEST.cpp:
Demonstrasi
sumber
Field 2 kosong dan sama untuk semua elemen dalam file.txt sehingga
join
akan menggabungkan setiap elemen dengan yang lainnya: sebenarnya menghitung produk Cartesian.sumber
Salah satu opsi dengan Python adalah memetakan memori file dan mengambil keuntungan dari fakta bahwa pustaka ekspresi reguler Python dapat bekerja secara langsung dengan file yang dipetakan memori. Meskipun ini memiliki tampilan menjalankan loop bersarang di atas file, pemetaan memori memastikan bahwa OS menghadirkan RAM fisik yang tersedia secara optimal
Alternatif solusi cepat dengan Python, meskipun efisiensi memori mungkin masih menjadi perhatian
sumber
Dalam bash, ksh juga bisa digunakan, hanya menggunakan shell bawaan:
Perhatikan bahwa sementara ini menyimpan seluruh file dalam memori dalam variabel shell, itu hanya membutuhkan akses baca tunggal untuk itu.
sumber
sed
larutan.Penjelasan:
sed 'r file2' file1
- Baca semua isi file file2 untuk setiap baris file1.1~i
berarti garis ke-1, kemudian garis 1 + i, 1 + 2 * i, 1 + 3 * i, dll. Oleh karena itu,1~$((line_num + 1)){h;d}
berartih
garis runcing lama ke buffer,d
hapus ruang pola, dan mulai siklus baru.'G;s/(.*)\n(.*)/\2 \1/'
- untuk semua baris, kecuali diambil pada langkah sebelumnya, lakukan selanjutnya:G
et line dari hold buffer dan tambahkan ke baris saat ini. Kemudian bertukar tempat garis. Apakahcurrent_line\nbuffer_line\n
, menjadibuffer_line\ncurrent_line\n
Keluaran
sumber