Leggi il file binario come stringa in Ruby


263

Ho bisogno di un modo semplice per prendere un file tar e convertirlo in una stringa (e viceversa). C'è un modo per farlo in Ruby? Il mio miglior tentativo è stato questo:

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

Ho pensato che sarebbe bastato per convertirlo in una stringa, ma quando provo a riscriverlo in questo modo ...

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

Non è lo stesso file. Fare ls -lmostra che i file sono di dimensioni diverse, anche se sono piuttosto vicini (e l'apertura del file rivela la maggior parte dei contenuti intatti). C'è un piccolo errore che sto facendo o un modo completamente diverso (ma praticabile) per farlo?


3
Questo è un file tar gzip (spero). Non ci sono "linee". Chiarisci cosa stai cercando di ottenere.
Brent.Longborough,

stai cercando di esaminare i dati compressi o i contenuti non compressi?
David Nehme,

quindi i caratteri in un flusso di dati compressi avranno all'incirca 1 su 256 possibilità di atterrare su "\ n" definendo la fine di una linea, e va bene se non si aspetta anche "\ r", vedi la mia risposta qui sotto
Purfideas

Questa domanda dovrebbe essere ribattezzata come "Converti file binario in stringa", IO.readaltrimenti sarebbe la risposta preferita.
Ian,

Risposte:


397

Innanzitutto, è necessario aprire il file come file binario. Quindi puoi leggere l'intero file in, con un solo comando.

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

Questo ti porterà l'intero file in una stringa.

Dopo quello, probabilmente lo vorrai file.close. Se non lo fai, filenon verrà chiuso fino a quando non verrà raccolto, quindi sarebbe un leggero spreco di risorse di sistema mentre è aperto.


22
Il flag binario è rilevante solo su Windows e questo lascia aperto il descrittore di file. File.read (...) è migliore.
Daniel Huckstep,

C'è qualcosa che non va in così tante persone che cercano questo e copiano incollandolo come una soluzione one-liner (come tante cose su StackOverflow)? Dopotutto, funziona e il nome di queste funzioni era solo una scelta arbitraria dei progettisti della biblioteca di rubini. Se solo avessimo un linguaggio con sinonimi ... che in qualche modo sa ancora esattamente cosa vogliamo in casi limite / istanze ambigue. Quindi vorrei solo contents = (contents of file "path to file.txt" as string).
masterxilo,

2
Questo dovrebbe essere fatto in begin {..open..} ensure {..close..} endblocchi
shadowbq

3
@ArianFaurtosh No, è un altro metodo per leggere il file - non significa che verrà trattato come un file eseguibile ed eseguibile! Sarebbe un orribile effetto collaterale per un semplice metodo di "lettura".
Matteo Leggi il

1
@ David non potresti semplicemente fare il seguente one-liner? contents = File.binread('path-to-file.tar.gz')Vedi apidock . Fileè una sottoclasse di IO.
Vas

244

Se hai bisogno della modalità binaria, dovrai farlo nel modo più duro:

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

In caso contrario, più corto e più dolce è:

s = IO.read(filename)

In ruby ​​1.9.3+, IO.read ti darà una stringa contrassegnata con la codifica in Encoding.default_external. Penso che (?) I byte saranno tutti come erano nel file, quindi non è esattamente "non binario-sicuro", ma dovrete etichettarlo con la codifica binaria se è quello che volete.
jrochkind,

Se l'essenza e la dolcezza sono essenziali, il trucco del simbolo della e commerciale indicas = File.open(filename, 'rb', &:read)
Epigene,

114

Per evitare di lasciare il file aperto, è meglio passare un blocco a File.open. In questo modo, il file verrà chiuso dopo l'esecuzione del blocco.

contents = File.open('path-to-file.tar.gz', 'rb') { |f| f.read }

10
Questa è una risposta migliore di David Nehme perché i descrittori di file sono una risorsa di sistema finita e esaurirli è un problema comune che può essere facilmente evitato.
Jeff McCune,

17

su os x questi sono gli stessi per me ... questo potrebbe forse essere "\ r" extra in windows?

in ogni caso potresti essere meglio con:

contents = File.read("e.tgz")
newFile = File.open("ee.tgz", "w")
newFile.write(contents)

Questa sembra la soluzione più semplice.
Dishcandanty,

17

che ne dici di un po 'di sicurezza aperto / chiuso.

string = File.open('file.txt', 'rb') { |file| file.read }

perché non un esplicito .close? Come nel file OP.close al termine?
Giosuè,

2
File.open () {| file | blocco} si chiude automaticamente al termine del blocco. ruby-doc.org/core-1.9.3/File.html#method-c-open
Alex

14
Questo è identico alla risposta di Aaron Hinni che è stata pubblicata nel 2008 (tranne che non usando il file OP e i nomi delle variabili) ...
Abe Voelker,

10

Ruby ha una lettura binaria

data = IO.binread(path/filaname)

o se inferiore a Ruby 1.9.2

data = IO.read(path/file)

7

Probabilmente puoi codificare il file tar in Base64. Base 64 fornisce una rappresentazione ASCII pura del file che è possibile memorizzare in un file di testo semplice. Quindi è possibile recuperare il file tar decodificando nuovamente il testo.

Fai qualcosa del tipo:

require 'base64'

file_contents = Base64.encode64(tar_file_data)

Dai un'occhiata ai Rubydocs Base64 per avere un'idea migliore.


Fantastico, sembra che funzionerà anche lui! Dovrò verificarlo se per qualche motivo la lettura dei contenuti binari non va bene.
Chris Bunch,

0

Se è possibile codificare il file tar tramite Base64 (e memorizzarlo in un file di testo semplice) è possibile utilizzare

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

o

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

per stampare ogni riga (di testo) nel cmd.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.