バイナリファイルの内容を元に戻すには?


11

ファイル拡張子のないデータファイルを見つけたという課題を解決しました。fileそれは、コマンド・ショーdata file (application/octet-stream)hdコマンドが表示さGNPを。最後の行に。したがって、このファイルを元に戻すと、.PNG形式のファイルが表示され、どこでも検索しましたが、バイナリファイルの内容を元に戻す方法を説明する解決策が見つかりませんでした。

回答:


11

xxd(からvim)とtac(GNUのcoreutilsのから、またtail -rいくつかのシステムで):

< file.gnp xxd -p -c1 | tac | xxd -p -r > file.png

これをvi.stackexchange.com/a/2237/10649と組み合わせる方法はありますか?私は運のないすべての種類の組み合わせを試しました:(
Iulian Onofrei

すべてのファイルをミラーリングするため、これは解決策ではありません。
フィリップデルテイユ

@PhilippeDelteil、すべてのファイルをミラーリングすることは、OPがここで求めていたものですか?他に何をしたいですか?
ステファンシャゼル

4

zsh(あなたが考慮したい場合を除き、内部バイナリデータ(に対処することができる唯一のシェルは、ksh93のbase64でエンコードアプローチを)):

zmodload zsh/mapfile
(LC_ALL=C; printf %s ${(s::Oa)mapfile[file.gnp]} > file.png)
  • LC_ALL=C:文字はバイトです
  • $mapfile[file.gnp]file.gnpファイルの内容
  • s:::文字列をバイト構成要素に分割します
  • Oa:逆OにRDER array添字その配列

1
zshバイナリデータを処理できるシェルはこれだけではありません。
fpmurphy

2

以下は、を使用してバイナリファイルを反転する1つの方法ですksh93。わかりやすくするために、コードを「緩い」ままにしました。

#!/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

いいね これは、これまでのところ、ファイル全体をメモリに格納することを含まない唯一の答えです。ただし、ファイルの各バイト(およびbase64との間の変換)に対して複数のシステムコールを行うという点で非常に非効率的であるため、メモリに収まらないファイルにも適していません。私のマシンでは、それはおよそ10キロバイト/秒でファイルを処理
ステファンChazelas

read上記の最初のコードは、ファイルの最後で行われるため、何も読み取らないことに注意してください。
ステファンChazelas

それはとても遅かった理由を理解しようとすると、私は下でそれを実行してみましたstraceし、ksh93それがファイル内の場所ですべてを追求し、一度に大量に読み込むところ、非常に不気味に動作しているようです。たぶんバリアントgithub.com/att/ast/issues/15
ステファンChazelas

@StéphaneChazelas。なぜそれが比較的遅いのかについての謎はありません。ループ内では、バイトを読み取るたびに逆方向にシークする必要があります。これは、一度に複数のバイトを読み書きすることで、20倍以上に簡単に大幅に削減できます。書き込み側も同様に最適化できます。さらにスピードアップするために、他にもたくさんのテクニックがあります。その演習はあなたにお任せします。
fpmurphy 2018年

straceスクリプトを試して、私の意味を確認してください。ksh93何千回もファイルを読み取ります。たとえば、最初のバイトを読み取る前に、ファイルの終わりから64KiBをシークし、64KiBを読み取ってから、最後のバイトの前をシークして1バイトを読み取り、バイトごとに同様の処理を行います。これらのbase64エンコードされた文字列で実行できることは制限されているため、一度に複数のバイトを読み取る場合、その個々のバイトを抽出することはより困難になります。
ステファンChazelas

2

perlの場合:

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

性能テスト:

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

結果:

  • ローカルでテスト済み:私のソリューションは最も速く、perl -0777 -F最も遅いです。
  • オンライン試してみてください!:私のソリューションは最も速く、xxd最も遅いです。

注:diff出力は同じであるため、実行時間はすべてのソリューションで同じでなければなりません。


1
削除しましたperl。当時reverseは文字列を逆にすることもできなかったので、分割を行うことはあまり意味がなく、バージョンははるかに優れています。
ステファンChazelas

1

私は次を試しました:

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

アイデアは、セパレータとして任意の文字を使用して「tac」を強制することです。私はそれをバイナリファイルで試してみましたが、動作するように見えましたが、確認をお願いします。

主な利点は、ファイルをメモリにロードしないことです。


tac入力に改行文字が含まれている場合、私(ここではGNU 8.28)では機能しません。printf '1\n2' | tac -rs . | od -vAn -tc\n 2 1代わりに出力します2 \n 1。また、マルチバイト文字が必要LC_ALL=Cまたは.一致する可能性があります。
ステファンChazelas

4
LC_ALL=C tac -rs $'.\\|\n'でも動作するようです。
ステファンChazelas
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.