İkili dosyanın içeriği nasıl tersine çevrilir?


11

Dosya uzantısı olmayan bir veri dosyası bulduğum bir sorunu çözüyordum . fileBir komut gösterir data file (application/octet-stream). hdKomut gösterileri GSMH. son satırda. Bu dosyayı tersine çevirirsem .PNG biçiminde dosyayı alırım, her yerde aradım ama ikili dosyanın içeriğini nasıl tersine çevireceğini açıklayan bir çözüm bulamadım.

Yanıtlar:



4

İçinde zsh(ikili verilerle dahili olarak ilgilenebilen tek kabuk ( ksh93'ün base64 kodlama yaklaşımını dikkate almak istemiyorsanız )):

zmodload zsh/mapfile
(LC_ALL=C; printf %s ${(s::Oa)mapfile[file.gnp]} > file.png)
  • LC_ALL=C: karakterler bayttır
  • $mapfile[file.gnp]: file.gnpdosyanın içeriği
  • s::: dizeyi bayt bileşenlerine böl
  • Oa: Ters Oüzerinde Sarf array indis bu dizi

1
zshikili verileri işleyebilen tek kabuk değildir.
fpmurphy

2

İşte kullanarak bir ikili dosyayı tersine çevirmenin bir yolu ksh93. Anlamak daha kolay hale getirmek için kodu "gevşek" bıraktı.

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

Güzel. Şimdiye kadar tüm dosyanın hafızada saklanmasını içermeyen tek cevap bu. Ancak, dosyanın her bir baytı için birkaç sistem çağrısı (ve base64'e / base64'ten / dönüşümleri) için çok verimsiz olduğundan, belleğe sığmayan dosyalar için de uygun olmaz.
Makinemde

readYukarıdaki ilk dosyanın sonunda olduğu gibi hiçbir şey okumamalıdır.
Stéphane Chazelas

Neden bu kadar yavaş olduğunu anlamaya çalışarak, altında çalıştırmayı denedim straceve ksh93çok garip davranıyor gibi görünüyor, burada dosyanın içindeki her yeri arıyor ve o zaman büyük miktarlar okuyor. Belki bir çeşit github.com/att/ast/issues/15
Stéphane Chazelas

@ StéphaneChazelas. Neden nispeten yavaş olduğu konusunda bir gizem yok. Döngü içinde her bayt okuduğunda geriye doğru arama yapmak zorundadır. Bu, bir kerede birden fazla bayt okuyarak ve yazarak 20 veya daha fazla faktörle kolayca azaltılabilir. Şeylerin yazma tarafı da benzer şekilde optimize edilebilir. İşleri daha da hızlandırmak için birçok başka teknik mevcuttur. Bu egzersizi size bırakacağım.
fpmurphy

straceNe demek istediğimi görmek için senaryoyu deneyin . ksh93dosyaları binlerce kez okur. Örneğin, ilk baytı okumadan önce dosyanın sonundan 64KiB arar, 64KiB okur, sonra son bayttan önce arar ve 1 bayt okur ve her bayt için benzer bir şey yapar. Bu base64 kodlu dizelerle yapabileceklerinizin sınırlı olduğunu unutmayın, bu nedenle bir seferde birden fazla bayt okursanız, bunun ayrı ayrı baytlarını ayıklamak daha zor olacaktır.
Stéphane Chazelas

2

Perl ile:

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

Performans testi:

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

Sonuç:

  • Yerel olarak test edildi: benim çözümüm en hızlı, perl -0777 -Fen yavaş olanı.
  • Test edildi Çevrimiçi deneyin! : benim çözümüm en hızlı, xxden yavaş.

Not: diffÇıktı aynı olması gerektiğinden , çalışma süresi tüm çözümler için aynı olmalıdır.


1
Bir perltanesini sildim . O zamanlar reversedizeleri tersine çevirebileceğini fark etmemiştim, bu yüzden bölmeyi yapmak çok mantıklı değildi ve versiyonunuz çok daha iyi.
Stéphane Chazelas

1

Aşağıdakileri denedim:

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

Fikir ayırıcı olarak herhangi bir karakteri kullanarak 'tac' zorlamaktır. Ben bir ikili dosyada denedim ve işe yaramış gibi görünüyordu ama herhangi bir onay mutluluk duyacağız.

Ana avantajı dosyayı belleğe yüklememesidir.


tacGiriş yeni satır karakterleri içerdiğinde benim için çalışmıyor (burada GNU 8.28 ile). yerine printf '1\n2' | tac -rs . | od -vAn -tcçıktılar . Ayrıca çok baytlık karakterlere ihtiyacınız olabilir veya eşleşebilir. \n 2 12 \n 1LC_ALL=C.
Stéphane Chazelas

4
LC_ALL=C tac -rs $'.\\|\n'olsa işe yarıyor gibi görünüyor.
Stéphane Chazelas
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.