2017-01-30 43 views
10

Mam spakowany plik binarny w systemie operacyjnym Windows, który próbuję odczytać za pomocą R. Do tej pory działa przy użyciu unz() funkcja w połączeniu z funkcją readBin().Odczytuj pliki binarne w R z pliku zip i znaną pozycję początkową (przesunięcie bajtowe)

> bin.con <- unz(zip_path, file_in_zip, open = 'rb') 
> readBin(bin.con, 
      "double", 
      n = byte_chunk, 
      size = 8L, 
      endian = "little") 
> close(bin.con) 

Gdzie zip_path jest ścieżką do pliku zip, file_in_zip jest nazwa pliku w pliku zip, który ma być odczytany i byte_chunk liczbę bajtów, które chcę przeczytać.

W moim przypadku użycia, operacja readBin jest częścią pętli i stopniowo odczytuje cały plik binarny. Jednak rzadko chcę czytać wszystko i często dokładnie wiem, które części chcę przeczytać. Niestety, readBin nie ma argumentu start/skip, aby pominąć pierwsze n bajtów. Dlatego próbowałem warunkowo zastąpić readBin() przez seek(), aby pominąć faktyczny odczyt niechcianych części.

Kiedy próbuję to pojawia się błąd:

> bin.con <- unz(zip_path, file_in_zip, open = 'rb') 
> seek(bin.con, where = bytes_to_skip, origin = 'current') 
Error in seek.connection(bin.con, where = bytes_to_skip, origin = "current") : 
    seek not enabled for this connection 
> close(bin.con) 

Do tej pory nie mogę znaleźć sposobu, aby rozwiązać ten problem. Podobne pytania można znaleźć tutaj (niestety bez satysfakcjonującej odpowiedzi):

Porady całym internecie sugerują dodanie Argument open = 'r' do unz() lub całkowite odrzucenie otwartego argumentu, ale działa tylko w przypadku plików innych niż binarne (ponieważ domyślnie jest to "r"). Ludzie sugerują najpierw rozpakowanie plików, ale ponieważ pliki są dość duże, jest to praktycznie niemożliwe.

Czy jest jakieś obejście w poszukiwaniu binarnego spakowanego pliku lub odczytu z offsetem bajtowym (potencjalnie przy użyciu C++ za pośrednictwem pakietu Rcpp)?

Aktualizacja:

Dalsze badania zdają się wskazywać, że seek() w plikach zip nie jest łatwym problemem. sugeruje bibliotekę C++, która w najlepszym przypadku może posłużyć się szukaniem zgrubnym. This Python question oznacza, że ​​dokładne wyszukiwanie jest całkowicie niemożliwe ze względu na sposób implementacji zip (chociaż nie jest to sprzeczne z metodą poszukiwania zgrubnego).

+0

w dokumentacji dla "seek", mówi, że korzystanie z funkcji wyszukiwania w systemie Windows jest odradzane, więc należy ostrzec. tylko ciekawe pytanie: w jaki sposób tworzony jest ten plik? masz kontrolę nad tym, jak jest tworzony? – chinsoon12

+0

Czy chcesz wziąć pod uwagę inne języki? To wydaje się problemem dla języków takich jak C/C++/Java. zobacz ten http://www.phillipciske.com/blog/index.cfm/2008/10/2/Reading-Binary-Files-in-a-Zip-File- Before-CF8 – chinsoon12

+0

@ chinsoon12, źródło tego błędu jest wątpliwy, jak wspomniano tutaj: http://stackoverflow.com/questions/32736845/is-seek-reliable-on-modern-windows/32737017 Odpowiedź na twoje drugie pytanie jest negatywna. Nie tworzę pliku, ponieważ jest on tworzony przez narzędzie innej firmy. – takje

Odpowiedz

7

Oto trochę hack, który może ci pomóc. Oto fałszywy plik binarny:

writeBin(as.raw(1:255), "file.bin") 
readBin("file.bin", raw(1), n = 16) 
# [1] 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 

A oto produkowane plik zip:

zip("file.zip", "file.bin") 
# adding: file.bin (stored 0%) 
readBin("file.zip", raw(1), n = 16) 
# [1] 50 4b 03 04 0a 00 02 00 00 00 7b ab 45 4a 87 1f 

ta wykorzystuje tymczasowy pośredni plik binarny.

system('sh -c "unzip -p file.zip file.bin | dd of=tempfile.bin bs=1c skip=5c count=4c"') 
# 4+0 records in 
# 4+0 records out 
# 4 bytes copied, 0.00044964 s, 8.9 kB/s 
file.info("tempfile.bin")$size 
# [1] 4 
readBin("tempfile.bin", raw(1), n = 16) 
# [1] 06 07 08 09 

Metoda ta kompensuje "wydatek" związany z wielkością przechowywanych danych binarnych do powłoki/rury, z R.

To działało na win10, R-3.3.2. Używam dd z Git for Windows (wersja 2.11.0.3, choć 2.11.1 jest dostępna) i unzip i sh z RTools.

+1

Bardzo eleganckie rozwiązanie. Zrobiłem kilka testów i wygląda na to, że to rozwiązanie nie zachowuje całego rozpakowanego pliku w pamięci. Rozpakowanie zajmuje trochę czasu procesora, ale domyślam się, że tak naprawdę nie da się tego obejść. Jeszcze jedną poprawą byłoby zatrzymanie rozpakowywania, gdy tylko zostanie osiągnięty koniec licznika przesunięcia +. Czy masz pojęcie, jak to zrobić? – takje

+0

Nie, to część problemu: myślę, że najlepsza rozdzielczość jaką masz z 'unzip' jest" per-file ". – r2evans

+0

Czy jesteś zmuszony do skompresowania drzwi za pomocą 'zip', czy możesz ponownie skompresować za pomocą innego protokołu/narzędzia? – r2evans