Mam plik zip o rozmiarze 1,5 GB.
Jego zawartość to jeden absurdalnie duży plik tekstowy (60 GB) i obecnie nie mam wystarczająco dużo miejsca na dysku, aby wyodrębnić to wszystko, ani nie chcę wyodrębnić tego wszystkiego, nawet gdybym miał.
Jeśli chodzi o mój przypadek użycia, wystarczyłbym, aby sprawdzić części treści.
Dlatego chcę rozpakować plik jako strumień i uzyskać dostęp do zakresu pliku (jak można to zrobić za pomocą głowy i ogona w normalnym pliku tekstowym).
Albo przez pamięć (np. Wyodrębnij maks. 100 kb, zaczynając od znaku 32 GB) lub przez linie (podaj mi zwykłe linie tekstowe 3700-3900).
Czy istnieje sposób na osiągnięcie tego?
text-processing
zip
k0pernikus
źródło
źródło
Odpowiedzi:
Zauważ, że
gzip
można wyodrębnićzip
pliki (przynajmniej pierwszy wpis wzip
pliku). Jeśli więc w tym archiwum jest tylko jeden ogromny plik, możesz:Aby na przykład wyodrębnić 20 wierszy zaczynających się od 3000.
Lub:
To samo dotyczy bajtów (przy założeniu,
head
że obsługuje implementację-c
).Dla dowolnego dowolnego członka w archiwum, w sposób uniksowy:
Dzięki
head
wbudowanemuksh93
(np. Kiedy/opt/ast/bin
nadchodzi$PATH
) możesz także:Zauważ, że w każdym przypadku
gzip
/bsdtar
/unzip
zawsze będzie musiał rozpakować (i odrzucić tutaj) całą sekcję pliku, która prowadzi do części, którą chcesz wyodrębnić. To zależy od tego, jak działa algorytm kompresji.źródło
gzip
można go obsłużyć, będzie inne „Z aware” media (zcat
,zless
itp) również pracować?gzip
(generalnie prawdziwezless
, niekoniecznie zzcat
których w niektórych systemach jest tylko do odczytu.Z
plików), tak.Jedno rozwiązanie wykorzystujące unzip -p i dd, na przykład, aby wyodrębnić 10kb z przesunięciem 1000 bloków:
Uwaga: nie próbowałem tego z naprawdę dużymi danymi ...
źródło
unzip -l ARCHIVE
do wyświetlenia zawartości archiwum iunzip -p ARCHIVE PATH
wyodrębnienia zawartości pojedynczego obiektuPATH
na standardowe wyjście.dd
na potokach z liczeniem lub pomijaniem jest zawodne, ponieważ spowoduje to tak wieleread()
s do 1024 bajtów. Gwarantuje to, że działa poprawnie tylko wtedy, gdyunzip
zapisuje na rurze w częściach, których rozmiar jest wielokrotnością 1024.Jeśli masz kontrolę nad tworzeniem tego dużego pliku zip, dlaczego nie rozważyć zastosowania kombinacji
gzip
izless
?Umożliwiłoby to korzystanie
zless
z pagera i przeglądanie zawartości pliku bez konieczności zawracania sobie głowy rozpakowaniem.Jeśli nie możesz zmienić formatu kompresji, to oczywiście nie zadziała. Jeśli tak, uważam, że
zless
jest to raczej wygodne.źródło
Aby wyświetlić określone wiersze pliku, potokuj dane wyjściowe do edytora strumieni Unix, sed . Może to przetwarzać dowolnie duże strumienie danych, dzięki czemu można nawet użyć ich do zmiany danych. Aby wyświetlić wiersze 3700-3900 zgodnie z zapytaniem, uruchom następujące polecenie.
źródło
sed -n 3700,3900p
będzie czytać do końca pliku. Lepiej jestsed '3700,$!d;3900q'
tego uniknąć, a nawet ogólnie bardziej wydajnie:tail -n +3700 | head -n 201
Zastanawiałem się, czy można zrobić coś bardziej wydajnego niż dekompresję od początku pliku do samego momentu. Wygląda na to, że odpowiedź brzmi „nie”. Jednak na niektórych procesorach (Skylake)
zcat | tail
nie przyspiesza procesora do pełnej prędkości zegara. Patrz poniżej. Niestandardowy dekoder może uniknąć tego problemu i zapisać wywołania systemowe zapisu potoku, a może być o ~ 10% szybszy. (Lub ~ 60% szybciej w Skylake, jeśli nie zmienisz ustawień zarządzania energią).Najlepsze, co możesz zrobić z dostosowanym zlibem z
skipbytes
funkcją, to parsowanie symboli w bloku kompresji, aby dojść do końca bez konieczności rekonstrukcji zdekompresowanego bloku. Może to być znacznie szybsze (prawdopodobnie co najmniej 2x) niż wywołanie zwykłej funkcji dekodowania zlib w celu zastąpienia tego samego bufora i przejścia do przodu w pliku. Ale nie wiem, czy ktoś napisał taką funkcję. (I myślę, że to tak naprawdę nie działa, chyba że plik został napisany specjalnie, aby umożliwić dekoderowi ponowne uruchomienie w określonym bloku).Miałem nadzieję, że istnieje sposób na przeskakiwanie bloków Deflate bez ich dekodowania, ponieważ byłoby to znacznie szybsze. Drzewo Huffmana jest wysyłane na początku każdego bloku, więc możesz dekodować od początku dowolnego bloku (tak myślę). Och, myślę, że stan dekodera jest czymś więcej niż drzewem Huffmana, jest to również poprzednie 32kB zdekodowanych danych i nie jest to domyślnie resetowane / zapominane ponad granicami bloków. Do tych samych bajtów można się ciągle odwoływać, więc mogą pojawić się dosłownie tylko raz w gigantycznym skompresowanym pliku. (np. w pliku dziennika nazwa hosta prawdopodobnie pozostaje „gorąca” w słowniku kompresji przez cały czas i każde jego wystąpienie odnosi się do poprzedniego, a nie pierwszego).
zlib
Instrukcja mówi, trzeba użyćZ_FULL_FLUSH
podczas rozmowydeflate
, jeśli chcesz, aby strumień sprężonego być możliwy do przeszukania do tego punktu. „Resetuje stan kompresji”, więc myślę, że bez tego referencje wstecz mogą przejść do poprzednich bloków. Tak więc, chyba że plik zip został napisany z okazjonalnymi pełnymi blokami (jak każdy 1G lub coś miałoby nieistotny wpływ na kompresję), myślę, że będziesz musiał wykonać więcej pracy dekodowania do pożądanego poziomu niż ja początkowo myślący. Myślę, że prawdopodobnie nie możesz zacząć od początku żadnego bloku.Resztę tego napisałem, gdy myślałem, że można po prostu znaleźć początek bloku zawierającego pierwszy bajt, który chcesz, i stamtąd zdekodować.
Ale niestety początek bloku Deflate nie wskazuje, jak długo jest on w przypadku bloków skompresowanych. Dane nieskompresowane mogą być kodowane za pomocą nieskompresowanego typu bloku, który ma 16-bitowy rozmiar w bajtach z przodu, ale bloki skompresowane nie: RFC 1951 opisuje format dość czytelnie . Bloki z dynamicznym kodowaniem Huffmana mają drzewo z przodu bloku (więc dekompresor nie musi szukać w strumieniu), więc kompresor musi zachować cały (skompresowany) blok w pamięci przed jego zapisaniem.
Maksymalna odległość odniesienia do tyłu wynosi tylko 32 kB, więc kompresor nie musi przechowywać w pamięci dużej ilości nieskompresowanych danych, ale to nie ogranicza rozmiaru bloku. Bloki mogą mieć wiele megabajtów. (Jest to wystarczająco duży rozmiar, aby poszukiwania dysku były tego warte nawet na napędzie magnetycznym, w przeciwieństwie do sekwencyjnego odczytu do pamięci i po prostu pomijania danych w pamięci RAM, jeśli można było znaleźć koniec bieżącego bloku bez parsowania go).
zlib tworzy bloki tak długo, jak to możliwe: Według Marc Adler , zlib rozpoczyna nowy blok dopiero po zapełnieniu bufora symboli, który przy ustawieniu domyślnym to 16 383 symboli (literałów lub dopasowań)
Skopiowałem dane wyjściowe
seq
(który jest wyjątkowo redundantny i dlatego prawdopodobnie nie jest to świetny test), alepv < /tmp/seq1G.gz | gzip -d | tail -c $((1024*1024*1000)) | wc -c
na tym działa tylko przy ~ 62 MiB / s skompresowanych danych na Skylake i7-6700k przy 3,9 GHz, z DDR4-2666 RAM. To 246 Mb / s zdekompresowanych danych, co jest zmianą w porównaniu domemcpy
prędkości ~ 12 GiB / s dla bloków o zbyt dużych rozmiarach, aby zmieścić się w pamięci podręcznej.(Przy
energy_performance_preference
ustawieniu domyślnymbalance_power
zamiastbalance_performance
, wewnętrzny regulator procesora Skylake decyduje się na działanie tylko przy 2,7 GHz, ~ 43 MiB / s skompresowanych danych. Używam go,sudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'
aby go ulepszyć. Prawdopodobnie tak częste wywołania systemowe nie wyglądają jak prawdziwe związane z procesorem pracuj do jednostki zarządzania energią.)TL: DR:
zcat | tail -c
jest związany z procesorem nawet na szybkim procesorze, chyba że masz bardzo wolne dyski. gzip wykorzystał 100% procesora, na którym działał (i uruchomił instrukcje 1,81 na zegar, zgodnie zperf
) itail
wykorzystał 0,162 procesora, na którym działał (0,58 IPC). System był poza tym w większości bezczynny.Używam Linuksa 4.14.11-1-ARCH, który ma domyślnie włączoną KPTI do pracy w Meltdown, więc wszystkie te
write
wywołania systemowegzip
są droższe niż kiedyś: /Posiadanie wbudowanego wyszukiwania do (
unzip
lubzcat
nadal używanie zwykłejzlib
funkcji dekodowania) uratuje wszystkie zapisy potokowe i sprawi, że procesory Skylake będą działały z pełną prędkością zegara. (Ta redukcja w dół dla niektórych rodzajów obciążenia jest unikalna dla Intel Skylake i późniejszych, które odciążają proces podejmowania decyzji o częstotliwości procesora z systemu operacyjnego, ponieważ mają więcej danych na temat tego, co robi procesor, i mogą szybciej rosnąć / zwalniać. To jest normalnie dobre, ale tutaj prowadzi do tego, że Skylake nie przyspiesza do pełnej prędkości przy bardziej konserwatywnym ustawieniu gubernatora).Żadne wywołania systemowe, po prostu przepisanie bufora pasującego do pamięci podręcznej L2, dopóki nie osiągniesz żądanej początkowej pozycji bajtu, prawdopodobnie spowodowałoby co najmniej kilka% różnic. Może nawet 10%, ale tutaj tylko tworzę liczby. Nie profilowałem
zlib
szczegółowo, aby zobaczyć, jak duży jest rozmiar pamięci podręcznej i jak bardzo opróżnianie TLB (a zatem opróżnianie pamięci podręcznej uop) przy każdym wywołaniu systemowym boli przy włączonym KPTI.Istnieje kilka projektów oprogramowania, które dodają indeks wyszukiwania do formatu pliku gzip . Nie pomaga to, jeśli nie możesz wygenerować widocznych skompresowanych plików, ale inni przyszli czytelnicy mogą skorzystać.
Przypuszczalnie żaden z tych projektów nie ma funkcji dekodowania, która wie, jak przeskakiwać strumień Deflate bez indeksu, ponieważ są one zaprojektowane do działania tylko wtedy, gdy indeks jest dostępny.
źródło
Możesz otworzyć plik zip w sesji Pythona, używając
zf = zipfile.ZipFile(filename, 'r', allowZip64=True)
i po otwarciu możesz otworzyć, do odczytu, dowolny plik w archiwum zip i odczytywać wiersze itp., Tak jakby to był normalny plik.źródło