Odczytaj plik binarny jako ciąg znaków w Ruby


263

Potrzebuję łatwego sposobu pobrania pliku tar i przekonwertowania go na ciąg znaków (i odwrotnie). Czy jest na to sposób w Ruby? Moja najlepsza próba była następująca:

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

Pomyślałem, że to wystarczy, aby przekonwertować go na ciąg, ale wtedy, gdy próbuję go zapisać z powrotem w ten sposób ...

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

To nie jest ten sam plik. Wykonanie ls -lpokazuje, że pliki mają różne rozmiary, chociaż są dość blisko (a otwarcie pliku ujawnia większość zawartości nienaruszonej). Czy popełniam niewielki błąd lub całkowicie inny (ale wykonalny) sposób na osiągnięcie tego?


3
To jest skompresowany plik tar (mam nadzieję). Nie ma „linii”. Proszę wyjaśnić, co próbujesz osiągnąć.
Brent.Longborough,

próbujesz spojrzeć na skompresowane dane lub nieskompresowaną zawartość?
David Nehme,

więc znaki w skompresowanym strumieniu danych będą miały mniej więcej 1 na 256 szans na wylądowanie na „\ n” definiującym końcu linii, i to jest w porządku, jeśli nie spodziewa się również „\ r”, patrz moja odpowiedź poniżej
Purfideas

To pytanie powinno zostać zmienione na „Konwertuj plik binarny na ciąg znaków”, ponieważ IO.readw przeciwnym razie byłaby preferowana odpowiedź.
Ian

Odpowiedzi:


397

Najpierw powinieneś otworzyć plik jako plik binarny. Następnie możesz odczytać cały plik za pomocą jednego polecenia.

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

Otrzymasz cały plik w ciągu.

Potem prawdopodobnie chcesz file.close. Jeśli tego nie zrobisz, filenie zostanie zamknięty, dopóki nie zostanie wyrzucony, więc byłoby to niewielkie marnowanie zasobów systemowych, gdy jest ono otwarte.


22
Flaga binarna jest ważna tylko w systemie Windows, co powoduje, że deskryptor pliku jest otwarty. File.read (...) jest lepszy.
Daniel Huckstep,

Czy jest coś złego w tym, że tylu ludzi szuka tego i kopiuje, wklejając jako rozwiązanie jednowarstwowe (tak jak wiele rzeczy na stosie) W końcu to działa, a nazwa tych funkcji była tylko arbitralnym wyborem projektantów biblioteki ruby. Gdybyśmy tylko mieli język z synonimami ... to wciąż w jakiś sposób dokładnie wie, czego chcemy w przypadkowych przypadkach / niejednoznacznych przypadkach. Wtedy bym tylko contents = (contents of file "path to file.txt" as string).
masterxilo,

2
Należy to zrobić w begin {..open..} ensure {..close..} endblokach
shadowbq

3
@ArianFaurtosh Nie, to kolejna metoda odczytu pliku - nie oznacza to, że zostanie potraktowany jako możliwy do wykonania i uruchomiony! Byłby to przerażający efekt uboczny prostej metody „odczytu”.
Mateusz

1
@David nie mógłbyś po prostu zrobić poniższej linijki? contents = File.binread('path-to-file.tar.gz')Zobacz apidock . Filejest podklasą IO.
vas

244

Jeśli potrzebujesz trybu binarnego, musisz to zrobić na własnej skórze:

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

Jeśli nie, krótszy i słodszy to:

s = IO.read(filename)

W Ruby 1.9.3+ IO.read da ci ciąg oznaczony kodowaniem w Encoding.default_external. Myślę, że (?) Wszystkie bajty będą takie, jakie były w pliku, więc nie jest to dokładnie „nie binarnie bezpieczne”, ale będziesz musiał oznaczyć go kodowaniem binarnym, jeśli tego chcesz.
jrochkind

Jeśli istotą jest krótkość i słodycz, sztuczka proc i symbol ampersand dajes = File.open(filename, 'rb', &:read)
Epigene

114

Aby uniknąć pozostawienia pliku otwartego, najlepiej przekazać blok do pliku File.open. W ten sposób plik zostanie zamknięty po wykonaniu bloku.

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

10
Jest to lepsza odpowiedź niż David Nehme, ponieważ deskryptory plików są skończonym zasobem systemowym, a ich wyczerpanie jest częstym problemem, którego można łatwo uniknąć.
Jeff McCune

17

na OS X są dla mnie takie same ... czy może to być dodatkowe „\ r” w systemie Windows?

w każdym razie możesz być lepszy z:

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

To wydaje się najprostszym rozwiązaniem.
Dishcandanty

17

co powiesz na pewne bezpieczeństwo otwarcia / zamknięcia.

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

dlaczego nie jawne .close? Tak jak w pliku OP.close po zakończeniu?
Joshua

2
File.open () {| file | blok} automatycznie zamyka się, gdy blok się kończy. ruby-doc.org/core-1.9.3/File.html#method-c-open
Alex

14
Jest to identyczne z odpowiedzią Aarona Hinniego, która została opublikowana w 2008 r. (Z wyjątkiem tego, że nie używała nazwy pliku i zmiennych OP) ...
Abe Voelker

10

Ruby ma odczyt binarny

data = IO.binread(path/filaname)

lub jeśli mniej niż Ruby 1.9.2

data = IO.read(path/file)

7

Prawdopodobnie możesz zakodować plik tar w Base64. Baza 64 daje czystą reprezentację ASCII pliku, który można przechowywać w zwykłym pliku tekstowym. Następnie możesz odzyskać plik tar dekodując tekst z powrotem.

Robisz coś takiego:

require 'base64'

file_contents = Base64.encode64(tar_file_data)

Spójrz na Rubydocs Base64, aby uzyskać lepszy pomysł.


Świetnie, wygląda na to, że to też zadziała! Będę musiał to sprawdzić, jeśli z jakiegoś powodu czytanie zawartości binarnej pójdzie nie tak.
Chris Bunch,

0

Jeśli możesz zakodować plik tar za pomocą Base64 (i przechowywać go w zwykłym pliku tekstowym), możesz użyć

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

lub

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

aby wydrukować każdą linię (tekstową) w cmd.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.