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?

Chris Bunch
fonte
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.

David Nehme
fonte
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)

fonte
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 }
Aaron Hinni
fonte
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)
Purfideas
fonte
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 }
Alex
fonte
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)
bardzo
fonte
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.


fonte
Ó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.

Boris
fonte