Leer archivo binario como cadena en Ruby

263

Necesito una manera fácil de tomar un archivo tar y convertirlo en una cadena (y viceversa). ¿Hay alguna manera de hacer esto en Ruby? Mi mejor intento fue este:

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

Pensé que sería suficiente para convertirlo en una cadena, pero cuando trato de escribirlo de nuevo de esta manera ...

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

No es el mismo archivo. Hacer ls -lmuestra que los archivos son de diferentes tamaños, aunque están bastante cerca (y abrir el archivo revela la mayor parte del contenido intacto). ¿Hay un pequeño error que estoy cometiendo o una forma completamente diferente (pero viable) de lograr esto?

Chris Bunch
fuente
3
Ese es un archivo tar comprimido (espero). No hay "líneas". Por favor aclare lo que está tratando de lograr.
Brent.Longborough
¿Estás tratando de ver los datos comprimidos o el contenido sin comprimir?
David Nehme
así que los caracteres en un flujo de datos comprimido tendrán aproximadamente 1 en 256 posibilidades de aterrizar en "\ n" definiendo el final de una línea, y eso está bien si no espera "\ r" también, vea mi respuesta a continuación
Purfideas
Esta pregunta debe volver a titularse como "Convertir archivo binario a cadena", ya que de lo IO.readcontrario sería la respuesta preferida.
Ian

Respuestas:

397

Primero, debe abrir el archivo como un archivo binario. Luego puede leer el archivo completo en un solo comando.

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

Eso te dará todo el archivo en una cadena.

Después de eso, probablemente quieras hacerlo file.close. Si no lo hace, fileno se cerrará hasta que se recolecte basura, por lo que sería un pequeño desperdicio de recursos del sistema mientras está abierto.

David Nehme
fuente
22
El indicador binario solo es relevante en Windows, y esto deja abierto el descriptor de archivo. File.read (...) es mejor.
Daniel Huckstep
¿Hay algo malo en que tanta gente busque esto y copie y pegue como una solución única (como tantas cosas en stackoverflow)? Después de todo, funciona, y el nombre de estas funciones fue solo una elección arbitraria de los diseñadores de la biblioteca ruby. Si tan solo tuviéramos un lenguaje con sinónimos ... que de alguna manera sepa exactamente lo que queremos en casos extremos / instancias ambiguas. Entonces solo lo haría contents = (contents of file "path to file.txt" as string).
masterxilo
2
Esto debe hacerse en begin {..open..} ensure {..close..} endbloques
shadowbq
3
@ArianFaurtosh No, es otro método para leer el archivo, ¡no significa que se lo tratará como ejecutable y ejecutable! Eso sería un efecto secundario horrible para un método simple de "lectura".
Mateo leyó el
1
@David, ¿no podrías simplemente hacer la siguiente frase? contents = File.binread('path-to-file.tar.gz')Ver apidock . Filees una subclase de IO.
vas
244

Si necesita el modo binario, deberá hacerlo de la manera difícil:

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

Si no, más corto y más dulce es:

s = IO.read(filename)

fuente
En ruby ​​1.9.3+, IO.read le dará una cadena marcada con la codificación en Encoding.default_external. Creo que (?) Los bytes serán todos como estaban en el archivo, por lo que no es exactamente "no seguro para binarios", pero tendrá que etiquetarlo con la codificación binaria si eso es lo que desea.
jrochkind
Si la brevedad y la dulzura son la esencia, el truco de proc. De símbolos y símbolos comerciales das = File.open(filename, 'rb', &:read)
Epigene
114

Para evitar dejar el archivo abierto, es mejor pasar un bloque a File.open. De esta manera, el archivo se cerrará después de que se ejecute el bloque.

contents = File.open('path-to-file.tar.gz', 'rb') { |f| f.read }
Aaron Hinni
fuente
10
Esta es una mejor respuesta que la de David Nehme porque los descriptores de archivos son un recurso finito del sistema y agotarlos es un problema común que se puede evitar fácilmente.
Jeff McCune
17

en os x estos son los mismos para mí ... ¿podría esto ser "\ r" adicional en Windows?

en cualquier caso puede ser mejor con:

contents = File.read("e.tgz")
newFile = File.open("ee.tgz", "w")
newFile.write(contents)
Purfideas
fuente
Esta parece ser la solución más simple.
Dishcandanty
17

¿qué tal un poco de seguridad abierta / cerrada.

string = File.open('file.txt', 'rb') { |file| file.read }
Alex
fuente
¿Por qué no un cierre explícito? Como en el archivo OP. ¿Cerrar cuando se hace?
Joshua
2
File.open () {| file | bloque} se cierra automáticamente cuando el bloque termina. ruby-doc.org/core-1.9.3/File.html#method-c-open
Alex
14
Esto es idéntico a la respuesta de Aaron Hinni que se publicó en 2008 (excepto que no utiliza el archivo OP y los nombres de variables) ...
Abe Voelker
10

Ruby tiene lectura binaria

data = IO.binread(path/filaname)

o si es menor que Ruby 1.9.2

data = IO.read(path/file)
bardzo
fuente
7

Probablemente pueda codificar el archivo tar en Base64. Base 64 le dará una representación ASCII pura del archivo que puede almacenar en un archivo de texto sin formato. Luego puede recuperar el archivo tar decodificando el texto nuevamente.

Haces algo como:

require 'base64'

file_contents = Base64.encode64(tar_file_data)

Eche un vistazo a los Rubydocs Base64 para tener una mejor idea.


fuente
¡Genial, parece que también funcionará! Tendré que echarle un vistazo si, por alguna razón, leer el contenido binario se agria.
Chris Bunch
0

Si puede codificar el archivo tar con Base64 (y almacenarlo en un archivo de texto sin formato) puede usar

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

o

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

para imprimir cada línea (texto) en el cmd.

Boris
fuente