Jak odwrócić zawartość pliku binarnego?


11

Rozwiązałem wyzwanie, w którym znalazłem plik danych bez rozszerzenia. Do filepodaje komunikat, że jest data file (application/octet-stream). hdPodaje komunikat PNB. w ostatniej linii. Więc jeśli odwrócę ten plik, to otrzymam plik w formacie .PNG , szukałem wszędzie, ale nie znalazłem rozwiązania wyjaśniającego, jak odwrócić zawartość pliku binarnego.

Odpowiedzi:


11

Z xxd(z vim) i tac(z GNU coreutils, także tail -rw niektórych systemach):

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

Czy jest jakiś sposób na połączenie tego z vi.stackexchange.com/a/2237/10649 ? Próbowałem wszelkiego rodzaju kombinacji bez powodzenia :(
— Iulian Onofrei

To nie jest rozwiązanie, ponieważ spowoduje odbicie lustrzane całego pliku.
— Philippe Delteil

@PililpeDelteil, dublowanie całego pliku było tym, o co prosi OP tutaj? Co jeszcze byś chciał?
— Stéphane Chazelas

4

W zsh(jedyna powłoka, która może wewnętrznie radzić sobie z danymi binarnymi (chyba że chcesz rozważyć podejście do kodowania base64 w ksh93 )):

zmodload zsh/mapfile
(LC_ALL=C; printf %s ${(s::Oa)mapfile[file.gnp]} > file.png)
  • LC_ALL=C: znaki są bajtami
  • $mapfile[file.gnp]: treść file.gnppliku
  • s::: podziel ciąg na składniki bajtowe
  • Oa: odwróć Order na arrayu indeksuj tę tablicę

1
zshnie jest jedyną powłoką, która może obsługiwać dane binarne.
— fpmurphy

2

Oto jeden ze sposobów odwrócenia pliku binarnego za pomocą ksh93. Zostawiłem „luźny” kod, aby ułatwić zrozumienie.

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

miły. To jedyna jak dotąd odpowiedź, która nie wymaga przechowywania całego pliku w pamięci. Jest jednak okropnie nieefektywny, ponieważ wykonuje kilka wywołań systemowych dla każdego bajtu pliku (i konwersji do / z base64), więc nie byłby odpowiedni dla plików, które nie mieszczą się w pamięci. Na moim komputerze przetwarza pliki z prędkością około 10 KB / s
— Stéphane Chazelas,

Zauważ, że pierwsze readpowyżej nie powinno czytać nic, ponieważ odbywa się to na końcu pliku.
— Stéphane Chazelas,

Próbując zrozumieć, dlaczego jest tak wolny, próbowałem go uruchomić stracei ksh93wydaje się, że zachowuje się bardzo dziwnie, gdy szuka w dowolnym miejscu pliku i odczytuje duże ilości w tym czasie. Może wariant github.com/att/ast/issues/15
— Stéphane Chazelas

@ StéphaneChazelas. Nie ma tajemnicy, dlaczego jest stosunkowo wolny. W pętli musi szukać wstecz za każdym razem, gdy czyta bajt. Można to łatwo znacznie zmniejszyć 20-krotnie lub nawet więcej, czytając i pisząc więcej niż jeden bajt na raz. Stronę zapisu rzeczy można podobnie zoptymalizować. Dostępnych jest wiele innych technik umożliwiających dalsze przyspieszenie. To ćwiczenie pozostawiam tobie.
— fpmurphy

Wypróbuj straceskrypt, aby zobaczyć, co mam na myśli. ksh93czyta pliki tysiące razy. Na przykład przed odczytaniem pierwszego bajtu szuka 64 KB na końcu pliku, odczytuje 64 KB, a następnie szuka ostatniego bajtu i odczytuje 1 bajt i robi coś podobnego dla każdego bajtu. Zauważ, że to, co możesz zrobić z tymi ciągami kodowanymi base64, jest ograniczone, więc jeśli czytasz więcej niż jeden bajt na raz, trudniej będzie wyodrębnić poszczególne bajty tego.
— Stéphane Chazelas

2

Z perlem:

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

Test wydajności:

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

Wynik:

  • Testowane lokalnie: moje rozwiązanie jest najszybsze, perl -0777 -Fjest najwolniejsze.
  • Testowane na Wypróbuj online! : moje rozwiązanie jest najszybsze, xxdjest najwolniejsze.

Uwaga: diffbieg czasu powinien być taki sam dla wszystkich rozwiązań, ponieważ dane wyjściowe powinny być takie same.


1
Usunąłem mój perl. Nie zdawałem sobie wtedy sprawy z tego, że reversemoże również odwracać łańcuchy, więc dzielenie nie miało większego sensu, a twoja wersja jest znacznie lepsza.
— Stéphane Chazelas

1

Próbowałem następujące:

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

Chodzi o to, aby wymusić „tac” przy użyciu dowolnego znaku jako separatora. Próbowałem tego na pliku binarnym i wydawało się, że działa, ale każde potwierdzenie będzie mile widziane.

Główną zaletą jest to, że nie ładuje pliku do pamięci.


Nie działa dla mnie (tutaj z GNU tac8.28), gdy dane wejściowe zawierają znaki nowego wiersza. printf '1\n2' | tac -rs . | od -vAn -tcwyjścia \n 2 1zamiast 2 \n 1. Będziesz także potrzebował LC_ALL=Club .mógł dopasować znaki wielobajtowe.
— Stéphane Chazelas

4
LC_ALL=C tac -rs $'.\\|\n'wydaje się jednak działać.
— Stéphane Chazelas
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.