Как перевернуть содержимое двоичного файла?

11

Я решал задачу, в которой нашел файл данных без расширения. Команда fileпоказывает, что это data file (application/octet-stream). Команда hdпоказывает ВНП. в последней строке. Поэтому, если я переверну этот файл, то получу файл формата .PNG , я искал везде, но не нашел решения, объясняющего, как перевернуть содержимое двоичного файла.

Prvt_Yadav
источник

Ответы:

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 ? Я испробовал все виды комбинаций без удачи :(
Юлиан Онофрей
Это не решение, потому что оно будет отражать весь файл.
Филипп Delteil
@PhilippeDelteil, зеркальное отображение всего файла было тем, о чем здесь просит ОП? Что еще вы хотели бы это сделать?
Стефан
4

In zsh(единственная оболочка, которая может внутренне работать с двоичными данными (если вы не хотите учитывать подход кодирования k64 в 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: перевернуть Order на aRray, индексировать этот массив
Стефан Шазелас
источник
1
zshэто не единственная оболочка, которая может обрабатывать двоичные данные.
fpmurphy
2

Вот один из способов обращения двоичного файла с помощью 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
fpmurphy
источник
красивый. Пока это единственный ответ, который не предполагает сохранения всего файла в памяти. Однако он ужасно неэффективен в том смысле, что он делает несколько системных вызовов для каждого байта файла (и преобразований в / из base64), поэтому не подходит для файлов, которые не помещаются в память. На моей машине он обрабатывает файлы со скоростью около 10 КБ / с
Стефан Шазелас
Обратите внимание, что первое readвыше не должно ничего читать, как это делается в конце файла.
Стефан Шазелас
Пытаясь понять, почему это было так медленно, я попытался запустить его straceи, ksh93кажется, ведет себя очень странно, когда он ищет в файле все места и читает в больших количествах в то время. Может быть, вариант github.com/att/ast/issues/15
Стефан Шазелас
@ StéphaneChazelas. Нет загадки, почему это относительно медленно. Внутри цикла он должен искать в обратном направлении каждый раз, когда читает байт. Это можно легко уменьшить в 20 и более раз, считывая и записывая более одного байта за раз. Сторона записи вещей может быть аналогичным образом оптимизирована. Множество других методов доступны для дальнейшего ускорения вещей. Я оставлю это упражнение на ваше усмотрение.
fpmurphy
Попробуйте straceсценарий, чтобы понять, что я имею в виду. ksh93читает файлы тысячи раз. Например, перед чтением первого байта он ищет 64 КБ в конце файла, читает 64 КБ, затем ищет перед последним байтом, читает 1 байт и делает что-то подобное для каждого байта. Обратите внимание, что то, что вы можете делать с этими закодированными строками base64, ограничено, поэтому, если вы читаете более одного байта за раз, будет сложнее извлечь отдельные байты этого.
Стефан Шазелас
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должно быть одинаковым для всех решений, поскольку выходные данные должны быть одинаковыми.

user202729
источник
1
Я удалил свой perl. Я не знал, что в то время reverseможно было переворачивать и строки, поэтому выполнение этого разделения не имело особого смысла, и ваша версия стала намного лучше.
Стефан
1

Я попробовал следующее:

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

Идея состоит в том, чтобы заставить 'tac' использовать любой символ в качестве разделителя. Я попробовал это на двоичном файле, и это, казалось, работало, но любое подтверждение будет оценено.

Основным преимуществом является то, что он не загружает файл в память.

Bouteille
источник
Не работает для меня (здесь с GNU tac8.28), когда ввод содержит символы новой строки. printf '1\n2' | tac -rs . | od -vAn -tcвыходы \n 2 1вместо 2 \n 1. Вам также понадобятся LC_ALL=Cили .могут совпадать многобайтовые символы.
Стефан Шазелас
4
LC_ALL=C tac -rs $'.\\|\n'кажется, работает, хотя.
Стефан Шазелас