如何反转二进制文件的内容?


11

我正在解决一个难题,即找到了没有文件扩展名的数据文件。该file命令表明它是一个data file (application/octet-stream)。该hd命令显示GNP。在最后一行。因此,如果我反转此文件,则将得到.PNG格式的文件,我在各处搜索了内容,但没有找到解决方案来解释如何反转二进制文件的内容。

Answers:


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结合使用?我没有运气就尝试了所有类型的组合:(
尤利奥诺夫雷

这不是解决方案,因为它将镜像所有文件。
菲利普·德尔特伊

@PhilippeDelteil,镜像所有文件就是OP在这里要的?您还想要它做什么?
斯特凡Chazelas

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:::将字符串拆分为其字节组成部分
  • OaOarray下标该数组上的反向rder

1
zsh不是唯一可以处理二进制数据的外壳。
fpmurphy

2

这是使用反转二进制文件的一种方法ksh93。我留下了代码“ loose”,以使其更易于理解。

#!/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的转换)进行几次系统调用,因此也不适用于内存中不适合的文件。在我的机器,它在约10KB / s的处理文件
斯特凡Chazelas

请注意,read上面的第一个文件不应读取,因为它在文件末尾已完成。
斯特凡Chazelas

为了理解它为什么这么慢,我尝试在下面运行它,strace并且ksh93看起来表现很怪异,它在文件中的所有位置进行查找,并在那时读取大量内容。也许一个变种github.com/att/ast/issues/15
斯特凡Chazelas

@StéphaneChazelas。为什么它相对较慢没有什么神秘之处。在循环内,每次读取字节时都必须向后搜索。通过一次读写多个字节,可以很容易地将其减少20倍甚至更多。事物的写面可以类似地进行优化。许多其他技术可用来进一步加快速度。我将把这项工作留给您。
fpmurphy

尝试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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.