Bagaimana cara membalikkan isi file biner?

11

Saya sedang memecahkan tantangan di mana saya menemukan file data tanpa ekstensi file. The fileperintah menunjukkan bahwa itu adalah data file (application/octet-stream). The hdperintah menunjukkan GNP. di baris terakhir. Jadi jika saya membalikkan file ini maka saya akan mendapatkan file berformat .PNG , saya mencari kemana-mana tetapi saya tidak menemukan solusi yang menjelaskan bagaimana cara membalikkan konten file biner.

Prvt_Yadav
sumber

Jawaban:

11

Dengan xxd(dari vim) dan tac(dari GNU coreutils, juga tail -rpada beberapa sistem):

< file.gnp xxd -p -c1 | tac | xxd -p -r > file.png
Stéphane Chazelas
sumber
Apakah ada cara untuk menggabungkan ini dengan vi.stackexchange.com/a/2237/10649 ? Saya mencoba semua jenis kombinasi tanpa keberuntungan :(
Iulian Onofrei
Ini bukan solusi karena akan mencerminkan semua file.
Philippe Delteil
@ PhilippeDelteil, mirroring semua file itu apa yang diminta OP di sini? Apa lagi yang ingin Anda lakukan?
Stéphane Chazelas
4

Dalam zsh(satu-satunya shell yang secara internal dapat menangani data biner (kecuali Anda ingin mempertimbangkan pendekatan pengkodean base64 ksh93 )):

zmodload zsh/mapfile
(LC_ALL=C; printf %s ${(s::Oa)mapfile[file.gnp]} > file.png)
  • LC_ALL=C: karakter adalah byte
  • $mapfile[file.gnp]: isi file.gnpfile
  • s::: pisahkan string menjadi konstituen byte-nya
  • Oa: membalikkan Order pada asubscript rray array itu
Stéphane Chazelas
sumber
1
zshbukan satu-satunya shell yang dapat menangani data biner.
fpmurphy
2

Berikut adalah salah satu cara membalikkan file biner menggunakan ksh93. Saya telah meninggalkan kode "longgar" untuk membuatnya lebih mudah dimengerti.

#!/bin/ksh93

typeset -b byte

redirect 3< image.gpj || exit 1

eof=$(3<#((EOF)))

read -r -u 3 -N 1 byte
printf "%B" byte > image.jpg
3<#((CUR - 1))

while (( $(3<#) > 0 ))
do
    read -r -u 3 -N 1 byte
    printf "%B" byte >> image.jpg
    3<#((CUR - 2))
done

read -r -u 3 -N 1 byte
printf "%B" byte >> image.jpg

redirect 3<&- || echo 'cannot close FD 3'

exit 0
fpmurphy
sumber
bagus. Itulah satu-satunya jawaban sejauh ini yang tidak melibatkan menyimpan seluruh file dalam memori. Namun, ini sangat tidak efisien karena membuat beberapa panggilan sistem untuk setiap byte file (dan konversi ke / dari base64), jadi tidak akan cocok untuk file yang tidak sesuai dengan memori juga. Di mesin saya, ia memproses file sekitar 10KB / s
Stéphane Chazelas
Perhatikan bahwa yang pertama di readatas seharusnya tidak membaca apa-apa karena dilakukan di akhir file.
Stéphane Chazelas
Mencoba memahami mengapa ini sangat lambat, saya mencoba menjalankannya di bawah stracedan ksh93tampaknya berperilaku sangat aneh, di mana ia mencari di semua tempat di dalam file dan membaca dalam jumlah besar pada saat itu. Mungkin varian dari github.com/att/ast/issues/15
Stéphane Chazelas
@ StéphaneChazelas. Tidak ada misteri mengapa itu relatif lambat. Dalam loop itu harus mencari mundur setiap kali membaca byte. Ini dapat dengan mudah dikurangi secara signifikan dengan faktor 20 atau bahkan lebih dengan membaca dan menulis lebih dari satu byte pada suatu waktu. Sisi penulisan hal-hal yang sama dapat dioptimalkan. Banyak teknik lain yang tersedia untuk mempercepat segalanya. Saya akan menyerahkan latihan itu kepada Anda.
fpmurphy
Cobalah straceskrip untuk melihat apa yang saya maksud. ksh93membaca file ribuan kali lipat. Misalnya, sebelum membaca byte pertama, ia mencari 64KiB dari akhir file, membaca 64KiB, lalu mencari sebelum byte terakhir dan membaca 1 byte dan melakukan sesuatu yang serupa untuk setiap byte. Perhatikan bahwa apa yang dapat Anda lakukan dengan string yang disandikan base64 terbatas, jadi jika Anda membaca lebih dari satu byte pada suatu waktu, akan lebih sulit untuk mengekstraksi masing-masing byte dari itu.
Stéphane Chazelas
2

Dengan perl:

perl -0777pe '$_=reverse $_'  [input_file]

Uji kinerja:

dd if=/dev/urandom of=/tmp/a bs=1M count=1
LC_ALL=C tac -rs $'.\\|\n' /tmp/a > /tmp/r

time perl -0777pe '$_=reverse $_' /tmp/a         | diff -q - /tmp/r
time xxd -p -c1 /tmp/a | tac | xxd -p -r         | diff -q - /tmp/r
time perl -0777 -F -ape '$_=reverse@F' /tmp/a    | diff -q - /tmp/r
time LC_ALL=C tac -rs $'.\\|\n' /tmp/a           | diff -q - /tmp/r

Hasil:

  • Diuji secara lokal: solusi saya adalah yang tercepat, perl -0777 -Fpaling lambat.
  • Diuji pada Coba online! : solusi saya adalah yang tercepat, xxdadalah yang paling lambat.

Catatan: waktu yang diffberjalan harus sama untuk semua solusi, karena outputnya harus sama.

pengguna202729
sumber
1
Saya sudah menghapus perlsatu. Saya tidak menyadari pada saat itu reversedapat membalikkan string juga, jadi melakukan pemisahan itu tidak masuk akal dan versi Anda jauh lebih baik.
Stéphane Chazelas
1

Saya mencoba yang berikut ini:

tac -rs '.' input.gnp > output.png

Idenya adalah untuk memaksa 'tac' menggunakan karakter apa saja sebagai pemisah. Saya mencoba itu pada file biner dan tampaknya berfungsi tetapi konfirmasi apa pun akan dihargai.

Keuntungan utama adalah tidak memuat file ke dalam memori.

Bouteille
sumber
Tidak berfungsi untuk saya (di sini dengan GNU tac8.28) ketika input berisi karakter baris baru. printf '1\n2' | tac -rs . | od -vAn -tcoutput \n 2 1bukan 2 \n 1. Anda juga perlu LC_ALL=Catau .bisa mencocokkan karakter multi-byte.
Stéphane Chazelas
4
LC_ALL=C tac -rs $'.\\|\n'tampaknya bekerja.
Stéphane Chazelas