Baca file biner sebagai string di Ruby

263

Saya perlu cara mudah untuk mengambil file tar dan mengubahnya menjadi string (dan sebaliknya). Apakah ada cara untuk melakukan ini di Ruby? Upaya terbaik saya adalah ini:

file = File.open("path-to-file.tar.gz")
contents = ""
file.each {|line|
  contents << line
}

Saya pikir itu akan cukup untuk mengubahnya menjadi string, tetapi kemudian ketika saya mencoba untuk menulis kembali seperti ini ...

newFile = File.open("test.tar.gz", "w")
newFile.write(contents)

Itu bukan file yang sama. Melakukan ls -lmenunjukkan file memiliki ukuran yang berbeda, meskipun mereka cukup dekat (dan membuka file mengungkapkan sebagian besar konten utuh). Apakah ada kesalahan kecil yang saya buat atau cara yang sama sekali berbeda (tapi bisa diterapkan) untuk mencapai ini?

Chris Bunch
sumber
3
Itu file tar yang di-gzip (saya harap). Tidak ada "garis". Tolong jelaskan apa yang ingin Anda capai.
Brent.Longborough
apakah Anda mencoba melihat data terkompresi atau konten yang tidak terkompresi?
David Nehme
jadi karakter dalam aliran data terkompresi akan memiliki peluang kira-kira 1 dalam 256 untuk mendarat di "\ n" mendefinisikan akhir baris, dan tidak apa-apa jika tidak mengharapkan "\ r" juga, lihat jawaban saya di bawah ini
Purfideas
Pertanyaan ini harus diberi judul ulang "Konversi file biner ke string", karena IO.readakan menjadi jawaban yang lebih disukai.
Ian

Jawaban:

397

Pertama, Anda harus membuka file sebagai file biner. Kemudian Anda dapat membaca seluruh file dalam satu perintah.

file = File.open("path-to-file.tar.gz", "rb")
contents = file.read

Itu akan membuat Anda seluruh file dalam sebuah string.

Setelah itu, Anda mungkin ingin file.close. Jika Anda tidak melakukan itu, filetidak akan ditutup sampai pengumpulan sampah, jadi itu akan menjadi sedikit pemborosan sumber daya sistem saat terbuka.

David Nehme
sumber
22
Bendera biner hanya relevan pada Windows, dan ini membiarkan deskriptor file terbuka. File.read (...) lebih baik.
Daniel Huckstep
Apakah ada yang salah dengan begitu banyak orang mencari ini dan menyalinnya sebagai solusi satu-liner (seperti banyak hal di stackoverflow)? Bagaimanapun, itu berhasil, dan nama untuk fungsi-fungsi ini hanyalah pilihan sewenang-wenang dari perancang perpustakaan ruby. Kalau saja kita memiliki beberapa bahasa dengan sinonim ... yang entah bagaimana masih tahu persis apa yang kita inginkan dalam kasus tepi / contoh ambigu. Maka saya hanya akan contents = (contents of file "path to file.txt" as string).
masterxilo
2
Ini harus dilakukan dalam begin {..open..} ensure {..close..} endblok
shadowbq
3
@ArianFaurtosh Tidak, ini metode lain untuk membaca file - itu tidak berarti akan diperlakukan sebagai exectuable dan dijalankan! Itu akan menjadi efek samping yang mengerikan untuk metode 'membaca' sederhana.
Matius Baca
1
@ David tidak bisa Anda hanya melakukan satu-liner berikut? contents = File.binread('path-to-file.tar.gz')Lihat apidock . Fileadalah subkelas dari IO.
vas
244

Jika Anda memerlukan mode biner, Anda harus melakukannya dengan cara yang sulit:

s = File.open(filename, 'rb') { |f| f.read }

Jika tidak, lebih pendek dan lebih manis adalah:

s = IO.read(filename)

sumber
Di ruby ​​1.9.3+, IO.read akan memberi Anda sebuah string yang ditandai dengan penyandian di Encoding.default_external. Saya pikir (?) Byte semua akan seperti yang ada di file, jadi itu bukan "tidak biner-aman", tetapi Anda harus menandainya dengan pengkodean biner jika itu yang Anda inginkan.
jrochkind
Jika kependekan dan kemanisan adalah intisari, trik proc ampersand-symbol memberis = File.open(filename, 'rb', &:read)
Epigene 4'19
114

Untuk menghindari membiarkan file tetap terbuka, yang terbaik adalah meneruskan blok ke File.open. Dengan cara ini, file akan ditutup setelah blok dijalankan.

contents = File.open('path-to-file.tar.gz', 'rb') { |f| f.read }
Aaron Hinni
sumber
10
Ini adalah jawaban yang lebih baik daripada David Nehme karena file deskriptor adalah sumber daya sistem yang terbatas dan melelahkan mereka adalah masalah umum yang dapat dengan mudah dihindari.
Jeff McCune
17

pada os x ini sama bagi saya ... mungkinkah ini "ekstra" di windows?

dalam hal apa pun Anda mungkin lebih baik dengan:

contents = File.read("e.tgz")
newFile = File.open("ee.tgz", "w")
newFile.write(contents)
Purfideas
sumber
Ini sepertinya solusi paling sederhana.
Dishcandanty
17

bagaimana dengan keamanan buka / tutup.

string = File.open('file.txt', 'rb') { |file| file.read }
Alex
sumber
mengapa tidak .close eksplisit? Seperti di file OP.Tutup kapan selesai?
Joshua
2
File.open () {| file | blok} secara otomatis ditutup ketika blok berakhir. ruby-doc.org/core-1.9.3/File.html#method-c-open
Alex
14
Ini identik dengan jawaban Aaron Hinni yang diposting pada 2008 (kecuali tidak menggunakan file OP dan nama variabel) ...
Abe Voelker
10

Ruby membaca biner

data = IO.binread(path/filaname)

atau jika kurang dari Ruby 1.9.2

data = IO.read(path/file)
Bardzo
sumber
7

Anda mungkin bisa menyandikan file tar di Base64. Basis 64 akan memberi Anda representasi ASCII murni dari file yang dapat Anda simpan dalam file teks biasa. Kemudian Anda dapat mengambil file tar dengan mendekode teks kembali.

Anda melakukan sesuatu seperti:

require 'base64'

file_contents = Base64.encode64(tar_file_data)

Lihat di Base64 Rubydocs untuk mendapatkan ide yang lebih baik.


sumber
Hebat, ini sepertinya akan bekerja juga! Saya harus memeriksanya jika karena alasan tertentu membaca konten biner menjadi masam.
Chris Bunch
0

Jika Anda dapat menyandikan file tar dengan Base64 (dan menyimpannya dalam file teks biasa) yang dapat Anda gunakan

File.open("my_tar.txt").each {|line| puts line}

atau

File.new("name_file.txt", "r").each {|line| puts line}

untuk mencetak setiap baris (teks) dalam cmd.

Boris
sumber