Wie kann man den Inhalt einer Binärdatei umkehren?


11

Ich habe eine Herausforderung gelöst, bei der ich eine Datendatei ohne Dateierweiterung gefunden habe. Der fileBefehl zeigt, dass es sich um eine handelt data file (application/octet-stream). Der hdBefehl zeigt BSP. in der letzten Zeile. Wenn ich diese Datei umkehre , erhalte ich die Datei im PNG- Format. Ich habe überall gesucht, aber keine Lösung gefunden, die erklärt, wie der Inhalt einer Binärdatei umgekehrt werden kann.

Antworten:


11

Mit xxd(von vim) und tac(von GNU-Coreutils, auch tail -rauf einigen Systemen):

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

Gibt es eine Möglichkeit, dies mit vi.stackexchange.com/a/2237/10649 zu kombinieren ? Ich habe alle Arten von Kombinationen ohne Glück
ausprobiert

Dies ist keine Lösung, da die gesamte Datei gespiegelt wird.
Philippe Delteil

@PhilippeDelteil, das Spiegeln der gesamten Datei war das, was das OP hier verlangt? Was soll es sonst noch tun?
Stéphane Chazelas

4

In zsh(die einzige Shell, die intern mit Binärdaten umgehen kann (es sei denn, Sie möchten den Base64-Codierungsansatz von ksh93 in Betracht ziehen )):

zmodload zsh/mapfile
(LC_ALL=C; printf %s ${(s::Oa)mapfile[file.gnp]} > file.png)
  • LC_ALL=C: Zeichen sind Bytes
  • $mapfile[file.gnp]: Inhalt der file.gnpDatei
  • s::: Teilen Sie den String in seine Byte-Bestandteile auf
  • Oa: Reverse ORder auf aRray-Index dieses Arrays

1
zshist nicht die einzige Shell, die Binärdaten verarbeiten kann.
fpmurphy

2

Hier ist eine Möglichkeit, eine Binärdatei mit umzukehren ksh93. Ich habe den Code "lose" gelassen, um das Verständnis zu erleichtern.

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

nett. Dies ist die einzige Antwort, bei der nicht die gesamte Datei im Speicher gespeichert wird. Es ist jedoch insofern furchtbar ineffizient, als es mehrere Systemaufrufe für jedes Byte der Datei (und Konvertierungen in / von base64) ausführt und daher auch nicht für Dateien geeignet ist, die nicht in den Speicher passen. Auf meinem Computer werden Dateien mit ca. 10 KB / s verarbeitet
Stéphane Chazelas

Beachten Sie, dass der erste readoben nichts lesen sollte, da dies am Ende der Datei erfolgt.
Stéphane Chazelas

Der Versuch , zu verstehen , warum es so langsam war, habe ich versucht , es unter fließendem straceund ksh93scheint sehr seltsam zu verhalten, wo es überall in der Datei sucht und liest zur Zeit große Mengen. Vielleicht eine Variante von github.com/att/ast/issues/15
Stéphane Chazelas

@ StéphaneChazelas. Kein Rätsel, warum es relativ langsam ist. Innerhalb der Schleife muss jedes Mal, wenn ein Byte gelesen wird, rückwärts gesucht werden. Dies kann leicht um den Faktor 20 oder sogar mehr erheblich reduziert werden, indem mehr als ein Byte gleichzeitig gelesen und geschrieben wird. Die Schreibseite der Dinge kann auf ähnliche Weise optimiert werden. Viele andere Techniken stehen zur Verfügung, um die Dinge weiter zu beschleunigen. Ich werde diese Übung Ihnen überlassen.
fpmurphy

Probieren Sie stracedas Skript aus, um zu sehen, was ich meine. ksh93liest die Dateien tausende Male. Beispielsweise sucht es vor dem Lesen des ersten Bytes 64 KB am Ende der Datei, liest 64 KB, sucht dann vor dem letzten Byte und liest 1 Byte und führt für jedes Byte etwas Ähnliches aus. Beachten Sie, dass das, was Sie mit diesen Base64-codierten Zeichenfolgen tun können, begrenzt ist. Wenn Sie also mehr als ein Byte gleichzeitig lesen, wird es schwieriger, die einzelnen Bytes davon zu extrahieren.
Stéphane Chazelas

1

Ich habe folgendes versucht:

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

Die Idee ist, 'tac' mit einem beliebigen Zeichen als Trennzeichen zu erzwingen. Ich habe das mit einer Binärdatei versucht und es schien zu funktionieren, aber jede Bestätigung wäre willkommen.

Hauptvorteil ist, dass keine Datei in den Speicher geladen wird.


Funktioniert bei mir nicht (hier mit GNU tac8.28), wenn die Eingabe Zeilenumbrüche enthält. printf '1\n2' | tac -rs . | od -vAn -tcAusgänge \n 2 1statt 2 \n 1. Sie würden auch Multi-Byte-Zeichen benötigen LC_ALL=Coder .könnten übereinstimmen.
Stéphane Chazelas

4
LC_ALL=C tac -rs $'.\\|\n'scheint aber zu funktionieren.
Stéphane Chazelas

1

Mit Perl:

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

Leistungstest:

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

Ergebnis:

  • Lokal getestet: Meine Lösung ist die schnellste, perl -0777 -Fdie langsamste.
  • Getestet am Online ausprobieren ! : Meine Lösung ist die schnellste, xxdist die langsamste.

Hinweis: Die diffZeitabläufe sollten für alle Lösungen gleich sein, da die Ausgabe gleich sein sollte.


1
Ich habe meinen gelöscht perl. Ich hatte damals noch nicht bemerkt reverse, dass man auch Strings umkehren kann, daher machte das Aufteilen keinen Sinn und Ihre Version ist viel besser.
Stéphane Chazelas
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.