Lire le fichier binaire en tant que chaîne dans Ruby

263

J'ai besoin d'un moyen facile de prendre un fichier tar et de le convertir en chaîne (et vice versa). Existe-t-il un moyen de le faire en Ruby? Ma meilleure tentative était la suivante:

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

J'ai pensé que ce serait suffisant pour le convertir en chaîne, mais quand j'essaie de le réécrire comme ça ...

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

Ce n'est pas le même fichier. Faire ls -lmontre que les fichiers sont de tailles différentes, bien qu'ils soient assez proches (et l'ouverture du fichier révèle la plupart du contenu intact). Y a-t-il une petite erreur que je fais ou une manière entièrement différente (mais réalisable) d'accomplir cela?

Chris Bunch
la source
3
C'est un fichier tar compressé (j'espère). Il n'y a pas de "lignes". Veuillez clarifier ce que vous essayez de réaliser.
Brent.Longborough
essayez-vous de regarder les données compressées ou le contenu non compressé?
David Nehme
donc les caractères dans un flux de données compressé auront environ 1 chance sur 256 d'atterrir sur "\ n" définissant la fin d'une ligne, et ce n'est pas
grave
Cette question devrait être renommée "Convertir le fichier binaire en chaîne", car ce IO.readserait la réponse préférée sinon.
Ian

Réponses:

397

Tout d'abord, vous devez ouvrir le fichier en tant que fichier binaire. Vous pouvez ensuite lire l'intégralité du fichier dans une seule commande.

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

Cela vous donnera le fichier entier dans une chaîne.

Après cela, vous voudrez probablement file.close. Si vous ne le faites pas, fileil ne sera pas fermé jusqu'à ce qu'il soit récupéré, ce serait donc un léger gaspillage de ressources système pendant qu'il est ouvert.

David Nehme
la source
22
L'indicateur binaire n'est pertinent que sous Windows, ce qui laisse le descripteur de fichier ouvert. File.read (...) est meilleur.
Daniel Huckstep
Y a-t-il quelque chose qui ne va pas avec autant de personnes qui recherchent ceci et copient le collent comme une solution à une ligne (comme tant de choses sur stackoverflow)? Après tout, cela fonctionne, et le nom de ces fonctions n'était qu'un choix arbitraire des concepteurs de la bibliothèque ruby. Si seulement nous avions un langage avec des synonymes ... qui sait toujours en quelque sorte exactement ce que nous voulons dans les cas limites / instances ambiguës. Alors je voudrais juste contents = (contents of file "path to file.txt" as string).
masterxilo
2
Cela devrait être fait en begin {..open..} ensure {..close..} endblocs
shadowbq
3
@ArianFaurtosh Non, c'est une autre méthode de lecture du fichier - cela ne signifie pas qu'il sera traité comme un exécutable et exécuté! Ce serait un effet secondaire horrible pour une simple méthode de «lecture».
Matthieu lu
1
@David ne pourriez-vous pas simplement faire le one-liner suivant? contents = File.binread('path-to-file.tar.gz')Voir apidock . Fileest une sous-classe de IO.
vas
244

Si vous avez besoin du mode binaire, vous devrez le faire à la dure:

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

Sinon, plus court et plus doux est:

s = IO.read(filename)

la source
Dans ruby ​​1.9.3+, IO.read vous donnera une chaîne marquée avec l'encodage dans Encoding.default_external. Je pense que (?) Les octets seront tous comme ils étaient dans le fichier, donc ce n'est pas exactement "pas sûr pour les binaires", mais vous devrez le marquer avec l'encodage binaire si c'est ce que vous voulez.
jrochkind
Si la brièveté et la douceur sont essentielles, l'astuce proc esperluette-symbole donnes = File.open(filename, 'rb', &:read)
Epigene
114

Pour éviter de laisser le fichier ouvert, il est préférable de passer un bloc à File.open. De cette façon, le fichier sera fermé après l'exécution du bloc.

contents = File.open('path-to-file.tar.gz', 'rb') { |f| f.read }
Aaron Hinni
la source
10
C'est une meilleure réponse que celle de David Nehme car les descripteurs de fichiers sont une ressource système finie et les épuiser est un problème courant qui peut facilement être évité.
Jeff McCune
17

sous os x, ce sont les mêmes pour moi ... cela pourrait-il être "\ r" supplémentaire dans Windows?

en tout cas vous pouvez être mieux avec:

contents = File.read("e.tgz")
newFile = File.open("ee.tgz", "w")
newFile.write(contents)
Purfideas
la source
Cela semble être la solution la plus simple.
Dishcandanty
17

que diriez-vous d'une certaine sécurité d'ouverture / fermeture.

string = File.open('file.txt', 'rb') { |file| file.read }
Alex
la source
pourquoi pas un .close explicite? Comme dans le fichier OP.close une fois terminé?
Joshua
2
File.open () {| fichier | block} se ferme automatiquement à la fin du bloc. ruby-doc.org/core-1.9.3/File.html#method-c-open
Alex
14
Ceci est identique à la réponse d'Aaron Hinni qui a été publiée en 2008 (sauf en n'utilisant pas le fichier OP et les noms de variables) ...
Abe Voelker
10

Ruby a une lecture binaire

data = IO.binread(path/filaname)

ou si moins que Ruby 1.9.2

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

Vous pouvez probablement encoder le fichier tar en Base64. Base 64 vous donnera une représentation ASCII pure du fichier que vous pouvez stocker dans un fichier texte brut. Ensuite, vous pouvez récupérer le fichier tar en décodant le texte.

Vous faites quelque chose comme:

require 'base64'

file_contents = Base64.encode64(tar_file_data)

Jetez un œil aux Rubydocs Base64 pour avoir une meilleure idée.


la source
Super, on dirait que ça marchera aussi! Je vais devoir vérifier si pour une raison quelconque la lecture du contenu binaire devient aigre.
Chris Bunch le
0

Si vous pouvez encoder le fichier tar en Base64 (et le stocker dans un fichier texte brut), vous pouvez utiliser

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

ou

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

pour imprimer chaque ligne (texte) dans le cmd.

Boris
la source