Читать бинарный файл как строку в Ruby

263

Мне нужен простой способ взять файл tar и преобразовать его в строку (и наоборот). Есть ли способ сделать это в Ruby? Моя лучшая попытка была такая:

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

Я думал, что этого будет достаточно, чтобы преобразовать его в строку, но потом, когда я попытаюсь записать его обратно, как это ...

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

Это не тот же файл. Выполнение ls -lпоказывает, что файлы имеют разные размеры, хотя они довольно близки (и открытие файла показывает большую часть содержимого без изменений). Есть ли небольшая ошибка, которую я делаю, или совершенно другой (но выполнимый) способ сделать это?

Крис Банч
источник
3
Это сжатый tar-файл (надеюсь). Там нет "линий". Просьба уточнить, чего вы пытаетесь достичь.
Брент.Лонгборо
Вы пытаетесь посмотреть на сжатые данные или несжатый контент?
Дэвид Неем
так что символы в сжатом потоке данных будут иметь примерно 1 из 256 шансов попасть на "\ n", определяющий конец строки, и это нормально, если он также не ожидает "\ r", см. мой ответ ниже
Purfideas
Этот вопрос должен быть переименован в «Преобразовать двоичный файл в строку», так IO.readкак в противном случае предпочтительным будет ответ.
Ян

Ответы:

397

Во-первых, вы должны открыть файл в виде двоичного файла. Затем вы можете прочитать весь файл, в одной команде.

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

Это даст вам весь файл в строке.

После этого вы, вероятно, захотите file.close. Если вы этого не сделаете, fileего не закроют до тех пор, пока он не будет собран сборщиком мусора, поэтому при открытой работе это будет небольшой тратой системных ресурсов.

Дэвид Неем
источник
22
Бинарный флаг имеет отношение только к Windows, и это оставляет дескриптор файла открытым. File.read (...) лучше.
Даниэль Хакстеп
Есть ли что-то не так с таким количеством людей, которые ищут это и копируют, вставляя его как однострочное решение (как многие вещи в stackoverflow)? В конце концов, это работает, и название для этих функций было просто произвольным выбором разработчиков библиотеки ruby. Если бы у нас был какой-то язык с синонимами ... который все же каким-то образом точно знает, чего мы хотим в крайних случаях / неоднозначных случаях. Тогда я бы просто contents = (contents of file "path to file.txt" as string).
masterxilo
2
Это должно быть сделано в begin {..open..} ensure {..close..} endблоках
shadowbq
3
@ArianFaurtosh Нет, это еще один метод чтения файла - это не значит, что он будет рассматриваться как исполняемый и запущенный! Это было бы ужасающим побочным эффектом для простого метода чтения.
Мэтью Прочитал
1
@ Дэвид не мог бы ты просто сделать следующий однострочный? contents = File.binread('path-to-file.tar.gz')Смотрите апидок . Fileподкласс IO.
Vas
244

Если вам нужен двоичный режим, вам нужно сделать это сложным путем:

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

Если нет, то короче и слаще

s = IO.read(filename)

источник
В ruby ​​1.9.3+ IO.read выдаст вам строку, помеченную кодировкой в ​​Encoding.default_external. Я думаю (?) Все байты будут такими, какими они были в файле, так что это не совсем "не бинарно-безопасно", но вам придется пометить его двоичной кодировкой, если вы этого хотите.
Джрочкинд,
Если важны краткость и сладость, трюк с символом амперсанда даетs = File.open(filename, 'rb', &:read)
Epigene
114

Чтобы не оставлять файл открытым, лучше всего передать блок в File.open. Таким образом, файл будет закрыт после выполнения блока.

contents = File.open('path-to-file.tar.gz', 'rb') { |f| f.read }
Аарон Хинни
источник
10
Это лучший ответ, чем у Дэвида Нейма, потому что файловые дескрипторы являются конечным системным ресурсом, и исчерпание их является распространенной проблемой, которую легко избежать.
Джефф МакКьюн
17

на OS X это то же самое для меня ... это может быть дополнительный "\ R" в Windows?

в любом случае вам может быть лучше с:

contents = File.read("e.tgz")
newFile = File.open("ee.tgz", "w")
newFile.write(contents)
Purfideas
источник
Это кажется самым простым решением.
Dishcandanty
17

как насчет безопасности открытия / закрытия.

string = File.open('file.txt', 'rb') { |file| file.read }
Alex
источник
почему не явный .close? Например, в файле OP.close, когда это будет сделано?
Джошуа
2
File.open () {| file | block} автоматически закрывается, когда блок заканчивается. ruby-doc.org/core-1.9.3/File.html#method-c-open
Алекс
14
Это совпадает с ответом Аарона Хинни, который был опубликован в 2008 году (за исключением того, что не использовались имена файлов и переменных OP) ...
Абэ Фолькер,
10

Рубин имеет бинарное чтение

data = IO.binread(path/filaname)

или если меньше, чем Ruby 1.9.2

data = IO.read(path/file)
Bardzo
источник
7

Вы, вероятно, можете кодировать файл tar в Base64. Base 64 даст вам чистое ASCII-представление файла, который вы можете сохранить в виде простого текстового файла. Затем вы можете получить файл tar, расшифровав текст обратно.

Вы делаете что-то вроде:

require 'base64'

file_contents = Base64.encode64(tar_file_data)

Взгляните на Base64 Rubydocs, чтобы получить лучшее представление.


источник
Отлично, похоже, это тоже сработает! Я должен проверить это, если по какой-то причине чтение двоичного содержимого идет плохо.
Крис Банч
0

Если вы можете закодировать файл tar с помощью Base64 (и сохранить его в виде простого текстового файла), вы можете использовать

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

или

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

распечатать каждую (текстовую) строку в cmd.

Борис
источник