Come invertire il contenuto del file binario?


11

Stavo risolvendo una sfida in cui ho trovato un file di dati senza estensione. Il filecomando mostra che è a data file (application/octet-stream). Il hdcomando mostra GNP. nell'ultima riga. Quindi, se inverto questo file, otterrò il file in formato .PNG , ho cercato ovunque ma non ho trovato una soluzione che spiegasse come invertire il contenuto di un file binario.

Risposte:


11

Con xxd(da vim) e tac(da core GNU GNU, anche tail -rsu alcuni sistemi):

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

C'è un modo per combinarlo con vi.stackexchange.com/a/2237/10649 ? Ho provato tutti i tipi di combinazioni senza fortuna :(
Iulian Onofrei

Questa non è una soluzione perché rispecchierà tutto il file.
Philippe Delteil,

@PhilippeDelteil, il mirroring di tutto il file era ciò che l'OP chiede qui? Cos'altro vorresti che facesse?
Stéphane Chazelas,

4

In zsh(l'unica shell che può gestire internamente i dati binari (a meno che tu non voglia prendere in considerazione l'approccio di codifica base64 di ksh93 ):

zmodload zsh/mapfile
(LC_ALL=C; printf %s ${(s::Oa)mapfile[file.gnp]} > file.png)
  • LC_ALL=C: i caratteri sono byte
  • $mapfile[file.gnp]: contenuto del file.gnpfile
  • s::: divide la stringa nei suoi componenti byte
  • Oa: inversa Order su array sottoscrive quell'array

1
zshnon è l'unica shell in grado di gestire dati binari.
fpmurphy

2

Ecco un modo per invertire un file binario usando ksh93. Ho lasciato il codice "sciolto" per facilitarne la comprensione.

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

simpatico. Questa è l'unica risposta finora che non comporta la memorizzazione dell'intero file in memoria. Tuttavia, è terribilmente inefficiente in quanto effettua diverse chiamate di sistema per ogni byte del file (e conversioni da / verso base64), quindi non sarebbe adatto nemmeno per i file che non rientrano nella memoria. Sulla mia macchina, elabora i file a circa 10 KB / s
Stéphane Chazelas,

Si noti che il primo readsopra non dovrebbe leggere nulla come è fatto alla fine del file.
Stéphane Chazelas,

Cercando di capire perché fosse così lento, ho provato a eseguirlo stracee ksh93sembra che si stia comportando in modo molto strano, dove cerca ovunque nel file e legge grandi quantità in quel momento. Forse una variante di github.com/att/ast/issues/15
Stéphane Chazelas,

@ StéphaneChazelas. Nessun mistero sul perché sia ​​relativamente lento. All'interno del ciclo deve cercare indietro ogni volta che legge un byte. Questo può essere facilmente ridotto significativamente di un fattore 20 o anche di più leggendo e scrivendo più di un byte alla volta. Il lato di scrittura delle cose può essere similmente ottimizzato. Molte altre tecniche sono disponibili per accelerare ulteriormente le cose. Lascio a te questo esercizio.
fpmurphy

Prova stracela sceneggiatura per capire cosa intendo. ksh93legge i file migliaia di volte. Ad esempio, prima di leggere il primo byte, cerca 64 KiB alla fine del file, legge 64 KiB, quindi cerca prima dell'ultimo byte e legge 1 byte e fa qualcosa di simile per ogni byte. Nota che ciò che puoi fare con quelle stringhe con codifica Base64 è limitato, quindi se leggi più di un byte alla volta, sarà più difficile estrarne i singoli byte.
Stéphane Chazelas,

2

Con perl:

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

Test della prestazione:

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

Risultato:

  • Testato localmente: la mia soluzione è la più veloce, perl -0777 -Fè la più lenta.
  • Testato su Provalo online! : la mia soluzione è la più veloce, xxdè la più lenta.

Nota: i tempi di diffesecuzione dovrebbero essere gli stessi per tutte le soluzioni, poiché l'output dovrebbe essere lo stesso.


1
Ho cancellato il mio perl. In quel momento non avevo capito che avrei reversepotuto invertire anche le stringhe, quindi farlo non ha molto senso e la tua versione è molto migliore.
Stéphane Chazelas,

1

Ho provato quanto segue:

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

L'idea è di forzare 'tac' usando qualsiasi carattere come separatore. L'ho provato su un file binario e sembrava funzionare, ma ogni conferma sarebbe apprezzata.

Il vantaggio principale è che non carica il file in memoria.


Non funziona per me (qui con GNU tac8.28) quando l'input contiene caratteri di nuova riga. printf '1\n2' | tac -rs . | od -vAn -tcoutput \n 2 1anziché 2 \n 1. Avresti anche bisogno LC_ALL=Co .potresti abbinare caratteri multibyte.
Stéphane Chazelas,

4
LC_ALL=C tac -rs $'.\\|\n'sembra funzionare però.
Stéphane Chazelas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.