Ler arquivo binário como string no Ruby


263

Eu preciso de uma maneira fácil de pegar um arquivo tar e convertê-lo em uma string (e vice-versa). Existe uma maneira de fazer isso em Ruby? Minha melhor tentativa foi esta:

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

Eu pensei que seria o suficiente para convertê-lo em uma seqüência de caracteres, mas quando eu tento escrever de volta assim ...

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

Não é o mesmo arquivo. Doing ls -lmostra que os arquivos têm tamanhos diferentes, embora estejam bem próximos (e a abertura do arquivo revela a maior parte do conteúdo intacta). Há um pequeno erro que estou cometendo ou uma maneira totalmente diferente (mas viável) de fazer isso?


3
Esse é um arquivo tar compactado (espero). Não há "linhas". Por favor, esclareça o que você está tentando alcançar.
Brent.Longborough

você está tentando olhar para os dados compactados ou o conteúdo não compactado?
David Nehme 25/09/08

portanto, os caracteres em um fluxo de dados compactados terão aproximadamente 1 em 256 chances de chegar ao final \ "n definidor de uma linha, e tudo bem se não esperar" \ r "também, veja minha resposta abaixo
Purfideas

Esta pergunta deve ser intitulada como "Converter arquivo binário em string", pois IO.readseria a resposta preferida.
Ian

Respostas:


397

Primeiro, você deve abrir o arquivo como um arquivo binário. Então você pode ler o arquivo inteiro, em um comando.

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

Isso te dará o arquivo inteiro em uma string.

Depois disso, você provavelmente quer file.close. Se você não fizer isso, filenão será fechado até que seja coletado como lixo, portanto seria um pequeno desperdício de recursos do sistema enquanto estiver aberto.


22
O sinalizador binário é relevante apenas no Windows e isso deixa o descritor de arquivo aberto. File.read (...) é melhor.
Daniel Huckstep

Há algo de errado com tantas pessoas pesquisando e copiando, colando-o como uma solução de uma linha (como tantas coisas no stackoverflow)? Afinal, funciona, e o nome dessas funções era apenas uma escolha arbitrária dos designers da biblioteca ruby. Se ao menos tivéssemos alguma linguagem com sinônimos ... isso ainda sabe exatamente o que queremos em casos extremos / instâncias ambíguas. Então eu apenas contents = (contents of file "path to file.txt" as string).
Masterdilo

2
Isso deve ser feito em begin {..open..} ensure {..close..} endblocos
shadowbq

3
@ArianFaurtosh Não, é outro método de leitura do arquivo - isso não significa que será tratado como um exectável e executado! Isso seria um efeito colateral horrível para um método simples de 'leitura'.
Mateus Leia

1
@ David você não poderia simplesmente fazer o seguinte one-liner? contents = File.binread('path-to-file.tar.gz')Veja apidock . Fileé uma subclasse de IO.
vas

244

Se você precisar do modo binário, precisará fazer da maneira mais difícil:

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

Caso contrário, menor e mais doce é:

s = IO.read(filename)

No ruby ​​1.9.3+, IO.read fornecerá uma string marcada com a codificação em Encoding.default_external. Eu acho que (?) Os bytes serão todos como estavam no arquivo, então não é exatamente "não seguro para binários", mas você precisará marcá-lo com a codificação binária, se é isso que você deseja.
Jrochkind

Se falta e doçura é da essência, o truque comercial-símbolo proc dás = File.open(filename, 'rb', &:read)
Epigene

114

Para evitar deixar o arquivo aberto, é melhor passar um bloco para File.open. Dessa forma, o arquivo será fechado após a execução do bloco.

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

10
Essa é uma resposta melhor do que a de David Nehme, porque os descritores de arquivo são um recurso finito do sistema e esgotá-los é um problema comum que pode ser facilmente evitado.
Jeff McCune

17

no os x estes são os mesmos para mim ... isso poderia ser extra "\ r" no windows?

em qualquer caso, você pode ser melhor com:

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

Esta parece ser a solução mais simples.
Dishcandanty

17

que tal alguma segurança de abrir / fechar.

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

por que não um .close explícito? Como no arquivo OP.close quando terminar?
13132 Joshua

2
File.open () {| file | O bloco} fecha automaticamente quando o bloco termina. ruby-doc.org/core-1.9.3/File.html#method-c-open
Alex

14
Esta é idêntica à resposta de Aaron Hinni que foi publicado em 2008 (exceto não usar arquivo de OP e nomes de variáveis) ...
Abe Voelker

10

Ruby tem leitura binária

data = IO.binread(path/filaname)

ou se for menor que Ruby 1.9.2

data = IO.read(path/file)

7

Você provavelmente pode codificar o arquivo tar no Base64. A Base 64 fornecerá uma representação ASCII pura do arquivo que você pode armazenar em um arquivo de texto sem formatação. Em seguida, você pode recuperar o arquivo tar decodificando o texto novamente.

Você faz algo como:

require 'base64'

file_contents = Base64.encode64(tar_file_data)

Veja os Rubydocs Base64 para ter uma idéia melhor.


Ótimo, parece que vai funcionar também! Vou ter que verificar se, por algum motivo, a leitura do conteúdo binário azeda.
Chris Bunch

0

Se você pode codificar o arquivo tar pelo Base64 (e armazená-lo em um arquivo de texto sem formatação), poderá usar

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

ou

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

para imprimir cada linha (texto) no cmd.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.