Mengganti string dalam file yang sangat besar

10

Saya memiliki serangkaian url yang sangat panjang tanpa karakter pemisah, dalam format yang sama seperti di bawah ini:

http://example.comhttp://example.nethttp://example.orghttp://etc...

Saya ingin setiap URL berada di baris baru. Saya mencoba melakukan ini dengan mengganti semua contoh "http: //" dengan "\ nhttp: //" menggunakan sed

sed 's_http://_\nhttp://_g' urls.txt

tetapi kesalahan segmentasi terjadi (pelanggaran memori). Saya hanya dapat menduga bahwa ukuran file yang sebenarnya (lebih dari 100GB) menyebabkan jumlah melebihi batas tertentu.

Saya dapat membagi file menjadi beberapa file yang lebih kecil untuk diproses, tetapi semua contoh "http: //" harus tetap utuh.

Apakah ada cara yang lebih baik untuk melakukan ini?

C Sawyer
sumber
Saya pikir sed tidak suka 100GB tanpa ujung garis karena mencoba membaca satu baris di buffer-nya.
jippie
pemisahan (terlepas dari "di mana" pemotongan terjadi), pemrosesan, kemudian pemasangan kembali harus memberikan hasil yang benar.
enzotib
3
Jika Anda benar-benar memiliki file teks 100GB yang berisi satu baris panjang, maka Anda lebih baik menulis program C cepat untuk melakukan pekerjaan itu.
fpmurphy

Jawaban:

11

Dengan awkAnda dapat menghindari membaca sejumlah besar teks sekaligus:

awk -vRS='http://' -vORS='\nhttp://' 1 urls.txt > urlsperline.txt

Keberhasilan mungkin tergantung pada awkimplementasi yang digunakan . Misalnya gawkberfungsi dengan baik, tetapi mawkcrash.

manatwork
sumber
6

Ini akan melakukan pekerjaan:

perl -pe 'BEGIN { $/ = "//" } s!(?=http://\z)!\n!' urls.txt

Dengan menetapkan $ / , saya telah mengubah definisi sebuah baris sehingga berakhir dengan //alih - alih sebuah baris baru. Ini membuat Perl membaca satu URL setiap kali. Tidak mungkin URL berisi //kecuali setelah skema, tetapi tidak apa-apa jika ada, regex akan mencegahnya menambahkan baris baru palsu.

Jika Anda ingin menghindari menambahkan baris kosong sebelum URL pertama:

perl -pe 'BEGIN { $/ = "//"; print scalar <> } s!(?=http://\z)!\n!' urls.txt

Anda dapat mencoba membuat tolok ukur untuk melihat apakah s!http://\z!\nhttp://!lebih cepat. Mereka setara. Perhatikan bahwa /gbendera tidak diperlukan pada substitusi, karena hanya ada satu pertandingan per "baris".

cjm
sumber
Apakah mesin perl regexp oke dengan garis multi-gigabyte?
Alexios
2
@ Alexios, mungkin tidak, tetapi tidak harus begitu. Karena saya berubah $/, itu hanya akan berurusan dengan satu URL pada suatu waktu.
cjm
Ah, saya melihat apa yang Anda lakukan di sana. Sudah lama sejak tahun 90-an, dan saya harus melakukannya man perlvar, tetapi masuk akal seperti itu.
Alexios
Linux memungkinkan url untuk menyematkan banyak garis miring, sehingga kode ini bisa gagal jika Anda memilikinya. Pengujian untuk seluruh string, http dan semuanya, tidak akan memiliki masalah ini.
Joe
@ Jo, saya sedang menguji untuk http:bagian di regex. Ini akan memeriksa setiap //, tetapi tidak akan menambah baris baru kecuali jika ditemukan http://.
cjm
5
  1. Ubah semua kemunculan a :dengan baris baru, untuk memotong file.
  2. Menggantikan
    • http di akhir baris dengan
    • baris baru diikuti oleh http:dan tambahkan baris berikutnya
  3. Ulangi sekali, sehingga garis genap dan genap diperbarui

Langkah-langkah ini terlihat seperti:

tr ':' '\n' | sed -e '/http$/{N;s/http\n/\nhttp:/}' | sed -e '/http$/{N;s/http\n/\nhttp:/}'
  1. Periksa apakah ada garis yang tidak dimulai http://, cetak nomor baris. Ini hanya akan terjadi jika a: ada di suatu tempat di URL selain setelah http.

    grep -nv '^http://'

jippie
sumber