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.

Prvt_Yadav
źródło

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
Stéphane Chazelas
źródło
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ę
Stéphane Chazelas
źródło
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
fpmurphy
źródło
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.

użytkownik202729
źródło
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.

Bouteille
źródło
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