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?

Chris Bunch
źródło
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.

David Nehme
źródło
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)

źródło
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 }
Aaron Hinni
źródło
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)
Purfideas
źródło
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 }
Alex
źródło
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)
bardzo
źródło
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ł.


źródło
Ś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.

Boris
źródło