¿Cómo revertir el contenido del archivo binario?


11

Estaba resolviendo un desafío donde encontré un archivo de datos sin extensión de archivo. El filecomando muestra que es un data file (application/octet-stream). El hdcomando muestra GNP. en la ultima linea. Entonces, si revierto este archivo, obtendré el archivo de formato .PNG , busqué en todas partes pero no encontré una solución que explicara cómo revertir el contenido de un archivo binario.

Respuestas:



4

En zsh(el único shell que puede manejar internamente datos binarios (a menos que desee considerar el enfoque de codificación base64 de ksh93 )):

zmodload zsh/mapfile
(LC_ALL=C; printf %s ${(s::Oa)mapfile[file.gnp]} > file.png)
  • LC_ALL=C: los caracteres son bytes
  • $mapfile[file.gnp]: contenido del file.gnparchivo
  • s::: divide la cadena en sus componentes de bytes
  • Oa: Order inverso en asubíndice de matriz que matriz

1
zshNo es el único shell que puede manejar datos binarios.
fpmurphy

2

Aquí hay una forma de invertir un archivo binario usando ksh93. He dejado el código "suelto" para que sea más fácil de entender.

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

bonito. Esa es la única respuesta hasta ahora que no implica almacenar todo el archivo en la memoria. Sin embargo, es terriblemente ineficiente ya que realiza varias llamadas al sistema para cada byte del archivo (y conversiones a / desde base64), por lo que tampoco sería adecuado para archivos que no caben en la memoria. En mi máquina, procesa archivos a aproximadamente 10 KB / s
Stéphane Chazelas

Tenga en cuenta que el primero readanterior no debería leer nada, ya que se hace al final del archivo.
Stéphane Chazelas

Tratando de entender por qué era tan lento, intenté ejecutarlo stracey ksh93parece que se comporta de manera muy extraña, donde busca en todo el lugar dentro del archivo y lee grandes cantidades en ese momento. Quizás una variante de github.com/att/ast/issues/15
Stéphane Chazelas

@ StéphaneChazelas. No hay misterio en cuanto a por qué es relativamente lento. Dentro del ciclo tiene que buscar hacia atrás cada vez que lee un byte. Esto puede reducirse significativamente en un factor de 20 o incluso más leyendo y escribiendo más de un byte a la vez. El lado de escritura de las cosas también se puede optimizar. Hay muchas otras técnicas disponibles para acelerar aún más las cosas. Dejaré ese ejercicio a usted.
fpmurphy

Prueba straceel guión para ver a qué me refiero. ksh93lee los archivos miles de veces. Por ejemplo, antes de leer el primer byte, busca 64KiB al final del archivo, lee 64KiB, luego busca antes del último byte y lee 1 byte y hace algo similar para cada byte. Tenga en cuenta que lo que puede hacer con esas cadenas codificadas en base64 es limitado, por lo que si lee más de un byte a la vez, será más difícil extraer los bytes individuales de eso.
Stéphane Chazelas

2

Con perl:

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

Prueba de rendimiento:

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

Resultado:

  • Probado localmente: mi solución es la más rápida, perl -0777 -Fla más lenta.
  • Probado en ¡ Pruébelo en línea! : mi solución es la más rápida, xxdes la más lenta.

Nota: el tiempo de diffejecución debe ser el mismo para todas las soluciones, ya que la salida debe ser la misma.


1
He eliminado mi perluno. En ese momento no me había dado cuenta de que también reversepodía invertir las cadenas, por lo que hacer esa división no tenía mucho sentido y su versión es mucho mejor.
Stéphane Chazelas

1

Intenté lo siguiente:

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

La idea es forzar 'tac' usando cualquier carácter como separador. Lo intenté en un archivo binario y parecía funcionar, pero cualquier confirmación sería apreciada.

La principal ventaja es que no carga el archivo en la memoria.


No funciona para mí (aquí con GNU tac8.28) cuando la entrada contiene caracteres de nueva línea. printf '1\n2' | tac -rs . | od -vAn -tcsalidas en \n 2 1lugar de 2 \n 1. También necesitaría LC_ALL=Co .podría coincidir con caracteres de varios bytes.
Stéphane Chazelas

44
LC_ALL=C tac -rs $'.\\|\n'parece funcionar sin embargo.
Stéphane Chazelas
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.