Як повернути вміст двійкового файлу?


11

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

Відповіді:


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, дзеркальне відображення всього файлу було те, про що тут задається ОП? Що б ти хотів це зробити?
Стефан Шазелас

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::: розділіть рядок на його складові байти
  • Oa: зворотний Order на aпідпрограмі rray цього масиву

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

приємно. Це єдина відповідь на даний момент, яка не передбачає збереження всього файлу в пам'яті. Однак це надзвичайно неефективно, оскільки він робить кілька системних викликів на кожен байт файлу (і перетворення в / з base64), тому не підходить для файлів, які також не вміщуються в пам'яті. На моїй машині він обробляє файли зі швидкістю близько 10 КБ / с
Stéphane Chazelas

Зауважте, що перший readвище нічого не повинен читати, як це робиться в кінці файлу.
Стефан Шазелас

Намагаючись зрозуміти, чому це було так повільно, я спробував запустити його straceі, ksh93здається, веде себе дуже дивно, де він шукає в усьому місці файлу і читає великі суми в той час. Можливо варіант github.com/att/ast/isissue/15
Stéphane Chazelas

@ StéphaneChazelas. Ніякої загадки, чому це відносно повільно. У циклі він повинен шукати назад кожного разу, коли читає байт. Це легко можна значно зменшити на коефіцієнт 20 або навіть більше, читаючи і записуючи більше одного байта за раз. Сторона запису речей може аналогічно оптимізуватися. Для подальшого прискорення роботи доступно багато інших методик. Я залишу цю вправу вам.
fpmurphy

Спробуйте straceза сценарієм, щоб побачити, що я маю на увазі. ksh93читає файли тисячі разів. Наприклад, перед тим, як прочитати перший байт, він шукає 64KiB в кінці файлу, читає 64KiB, потім шукає перед останнім байтом і читає 1 байт і робить щось подібне для кожного байта. Зауважте, що те, що ви можете зробити з цими рядками, кодованими base64, обмежене, тому якщо ви читаєте більше одного байту одночасно, витягнути окремі байти цього буде складніше.
Стефан Шазелас

2

З перл:

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може також обернути рядки, тому робити це розщеплення не мало особливого сенсу, і ваша версія набагато краща.
Стефан Шазелас

1

Я спробував таке:

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

Ідея полягає в тому, щоб змусити "tac" використовувати будь-який символ як роздільник. Я спробував це у двійковому файлі, і, здавалося, він працює, але будь-яке підтвердження буде вдячне.

Основна перевага полягає в тому, що він не завантажує файл у пам'ять.


Не працює для мене (тут із 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'Здається, працює.
Стефан Шазелас
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.