Читати двійковий файл як рядок у Ruby


263

Мені потрібен простий спосіб взяти файл tar і перетворити його в рядок (і навпаки). Чи є спосіб це зробити в Рубі? Моя найкраща спроба:

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
Це gzipped файл tar (я сподіваюся). Немає "ліній". Поясніть, чого ви намагаєтесь досягти.
Brent.Longborough

ви намагаєтесь переглянути стислі дані або нестиснений вміст?
Девід Неме

тому символи в стисненому потоці даних матимуть приблизно 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, і дескриптор файлу залишає відкритим. Файл.read (...) краще.
Даніель Хакстеп

Чи є щось погано в тому, що стільки людей переглядають це і копіюють вставку як однолінійне рішення (як і стільки речей у stackoverflow)? Зрештою, це працює, і назва цих функцій була лише довільним вибором дизайнерів рубінової бібліотеки. Якби у нас була якась мова з синонімами ... яка все ще якось точно знає, чого ми хочемо у кращих випадках / неоднозначних випадках. Тоді я б просто contents = (contents of file "path to file.txt" as string).
masterxilo

2
Це слід робити begin {..open..} ensure {..close..} endблоками
shadowbq

3
@ArianFaurtosh Ні, це ще один метод читання файлу - це не означає, що він буде розглядатися як виконаний і запускається! Це було б жахливим побічним ефектом для простого методу "читання".
Матвій

1
@David Ви не могли просто зробити наступний однокласник? contents = File.binread('path-to-file.tar.gz')Дивіться апідок . Fileє підкласом IO.
vas

244

Якщо вам потрібен бінарний режим, вам потрібно буде зробити це важким способом:

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

Якщо ні, то коротше і солодше:

s = IO.read(filename)

У рубіні 1.9.3+ IO.read надасть вам рядок, позначений кодуванням у Encoding.default_external. Я думаю, що (?) Байти будуть такими, якими вони були у файлі, тож це не зовсім "не бінарно-безпечно", але вам доведеться позначати його двійковим кодуванням, якщо саме цього ви хочете.
jrochkind

Якщо нестача та солодкість є сутнісними, трюк "амперсанд-символ" дає трюкs = File.open(filename, 'rb', &:read)
Епіген

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)

Це здається найпростішим рішенням.
Dishcandanty

17

Як щодо безпеки на відкритому / закритому рівні.

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

чому не явний .close? Як, наприклад, у файлі OP.close після завершення?
Джошуа

2
File.open () {| файл | блок} автоматично закривається, коли блок припиняється. ruby-doc.org/core-1.9.3/File.html#method-c-open
Олексій

14
Це ідентично відповіді Аарона Хінні, яка була опублікована в 2008 році (за винятком використання файлів і назв змінних OP) ...
Abe Voelker

10

У Ruby є двійкове читання

data = IO.binread(path/filaname)

або якщо менше Рубі 1.9.2

data = IO.read(path/file)

7

Можливо, ви можете кодувати файл tar на основі Base64. База 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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.