Dlaczego Zip jest w stanie skompresować pojedynczy plik mniejszy niż wiele plików o tej samej zawartości?

126

Załóżmy, że mam 10 000 plików XML. Załóżmy teraz, że chcę wysłać je do przyjaciela. Przed wysłaniem chciałbym je skompresować.

Metoda 1: Nie kompresuj ich

Wyniki:

Resulting Size: 62 MB
Percent of initial size: 100%

Metoda 2: Spakuj każdy plik i wyślij mu 10 000 plików XML

Komenda:

for x in $(ls -1) ;  do   echo $x ; zip "$x.zip" $x ; done

Wyniki:

Resulting Size: 13 MB
Percent of initial size: 20%

Metoda 3: Utwórz pojedynczy plik zip zawierający 10 000 plików xml

Komenda:

zip all.zip $(ls -1)

Wyniki:

Resulting Size: 12 MB
Percent of initial size: 19%

Metoda 4: Połącz pliki w jeden plik i skompresuj go

Komenda:

cat *.xml > oneFile.txt ; zip oneFile.zip oneFile.txt

Wyniki:

Resulting Size: 2 MB
Percent of initial size: 3%

Pytania:

  • Dlaczego otrzymuję tak radykalnie lepsze wyniki, gdy tylko kompresuję pojedynczy plik?
  • Spodziewałem się radykalnie lepszych wyników przy użyciu metody 3 niż metody 2, ale nie. Dlaczego?
  • Czy to zachowanie jest specyficzne zip? Jeśli spróbuję użyć, gzipczy uzyskam inne wyniki?

Dodatkowe informacje:

$ zip --version
Copyright (c) 1990-2008 Info-ZIP - Type 'zip "-L"' for software license.
This is Zip 3.0 (July 5th 2008), by Info-ZIP.
Currently maintained by E. Gordon.  Please send bug reports to
the authors using the web page at www.info-zip.org; see README for details.

Latest sources and executables are at ftp://ftp.info-zip.org/pub/infozip,
as of above date; see http://www.info-zip.org/ for other sites.

Compiled with gcc 4.4.4 20100525 (Red Hat 4.4.4-5) for Unix (Linux ELF) on Nov 11 2010.

Zip special compilation options:
    USE_EF_UT_TIME       (store Universal Time)
    SYMLINK_SUPPORT      (symbolic links supported)
    LARGE_FILE_SUPPORT   (can read and write large files on file system)
    ZIP64_SUPPORT        (use Zip64 to store large files in archives)
    UNICODE_SUPPORT      (store and read UTF-8 Unicode paths)
    STORE_UNIX_UIDs_GIDs (store UID/GID sizes/values using new extra field)
    UIDGID_NOT_16BIT     (old Unix 16-bit UID/GID extra field not used)
    [encryption, version 2.91 of 05 Jan 2007] (modified for Zip 3)

Edycja: metadane

Jedna odpowiedź sugeruje, że różnicą są metadane systemowe przechowywane w pliku zip. Nie sądzę, że może tak być. Aby przetestować, wykonałem następujące czynności:

for x in $(seq 10000) ; do touch $x ; done
zip allZip $(ls -1)

Otrzymany zip ma 1,4 MB. Oznacza to, że nadal jest ~ 10 MB niewyjaśnionego miejsca.

sześćdziesiąt stóp
źródło
34
Jeśli się nie mylę, to zjawisko powoduje, że ludzie robią, .tar.gza nie tylko spakowanie całego katalogu.
corsiKa
18
Podobne pytanie było już zadawane, tl; dr używać stałych archiwa 7zip.
Dmitrij Grigoriew
3
@sixtyfootersdude Jako test sprawdzający poprawność niektórych odpowiedzi, możesz spróbować skompresować zip wytworzony metodą 3? Podejrzewam, że zmniejszy to rozmiar pliku do czegoś porównywalnego z metodą 4.
Travis
7
Zamiast $(ls -1), wystarczy użyć *: for x in *; zip all.zip *
muru
4
Jeśli chcesz wykonać solidną kompresję za pomocą ZIP, oto obejście: najpierw utwórz nieskompresowany ZIP zawierający wszystkie twoje pliki. Następnie umieść ten ZIP w innym skompresowanym pliku ZIP.
user20574,

Odpowiedzi:

129

Podczas kompresji Zip traktuje zawartość każdego pliku osobno. Każdy plik będzie miał swój własny skompresowany strumień. Algorytm kompresji obsługuje (zwykle DEFLATE ) w celu identyfikacji powtarzających się odcinków. Jednak nie ma wsparcia w Zip, aby znaleźć redundancję między plikami.

Dlatego jest tak dużo dodatkowego miejsca, gdy zawartość jest w wielu plikach: umieszcza ten sam skompresowany strumień w pliku wiele razy.

Alan Shutko
źródło
9
Z tego też powodu niektóre narzędzia do kompresji umożliwiają kompresowanie plików osobno lub pojedynczo. (Chociaż ogólnie oznacza to również, że musisz zdekompresować więcej archiwum niż w innym przypadku, jeśli chcesz wyświetlić tylko jeden plik.)
JAB
28
@JAB: Narzędzia do kompresji, takie jak 7z i rar, używają terminu „solidne” archiwum do pakowania wielu plików od stóp do głów w większe strumienie kompresji. Przy umiarkowanym rozmiarze porcji, takim jak 64 MB, losowy dostęp do pojedynczego pliku może wymagać dekompresji do 64 MB danych od początku bloku kompresji, w którym się znajduje. Możesz uzyskać przyzwoity kompromis między dostępem swobodnym a znalezieniem redundancji między plikami. 7z może korzystać z bardziej skutecznego (ale wolniejszego do kompresji) schematu kompresji LZMA, co jest kolejną zaletą w stosunku do zip.
Peter Cordes,
Mówisz, że there is no support in Zip to find redundancy between filesjest to specyfikacja pliku zip?
sixtyfootersdude
6
@sixtyfootersdude Wiele algorytmów kompresji, takich jak DEFLATE, działa jako strumień. Aby odzyskać wystarczającą ilość informacji do zdekompresowania części strumienia, musisz przetworzyć cały strumień do tego momentu. Gdyby próbowali znaleźć redundancję między plikami, musiałbyś zdekompresować wszystkie 1000 plików, aby przejść do ostatniego. W rzeczywistości tak zwykle działa tgz. Jednak zip został zaprojektowany, aby umożliwić wyodrębnianie pojedynczych plików. tgz został zaprojektowany tak, aby był bardziej „wszystko albo nic”
Cort Ammon
1
@sixtyfootersdude - to prawda. Parafrazując Cort: Specyfikacje pkzip nie obsługują działającego pliku krzyżowego. Jeśli tak, wyodrębnienie jednego pliku może wymagać wyodrębnienia całego archiwum (i każdego pliku).
James Snell,
48

Kompresja ZIP opiera się na powtarzalnych wzorcach w danych, które mają być kompresowane, a kompresja staje się lepsza, im dłuższy jest plik, ponieważ coraz więcej wzorów można znaleźć i używać.

Uproszczone, jeśli skompresujesz jeden plik, słownik odwzorowujący (krótkie) kody na (dłuższe) wzory jest koniecznie zawarty w każdym wynikowym pliku zip; jeśli skompresujesz jeden długi plik, słownik zostanie „ponownie użyty” i stanie się jeszcze bardziej skuteczny we wszystkich treściach.

Jeśli twoje pliki są nawet trochę podobne (jak zawsze tekst), ponowne użycie „słownika” staje się bardzo wydajne, a wynikiem jest znacznie mniejszy całkowity zip.

Aganju
źródło
3
ZIP wykonuje zarówno archiwizację, jak i kompresję. Czy to oznacza, że ​​ZIP kompresuje każdy plik osobno, nawet jeśli wszystkie kończą się w tym samym pliku ZIP?
gerrit
2
to w pewnym sensie - wyobraź sobie, że usuwasz pojedynczy plik, nie chcesz, aby spędził kolejne pół godziny na kompresji reszty za pomocą nowego „słownika”. - ponadto prawdopodobnie zakłada, że ​​różne pliki potrzebują bardzo różnych „słowników”.
Aganju
2
Nie rozumiem, dlaczego to musi. Za pomocą narzędzi uniksowych najpierw archiwizuję plik za pomocą tar, a następnie kompresuję go za pomocą gzip / bz2 / lzma. Algorytm kompresji nie ma znaczenia, ile plików jest zakodowanych w archiwum. Jak często zdarza się usuwanie pojedynczego pliku ze skompresowanego archiwum? Nie sądzę, żebym kiedykolwiek to zrobił.
gerrit
4
Nie zgadzam się, a to prawdopodobnie dobry sposób. Nie zaprojektowałem ani nie napisałem ZIP. Właśnie powiedziałem, co robi ...
Aganju,
16
@gerrit Ma swoje problemy. Zip został zaprojektowany, aby umożliwić Ci szybki dostęp do dowolnego pliku w archiwum - spróbuj rozpakować pojedynczy plik z archiwum 100 GiB UHA, a zobaczysz, dlaczego wybrali ten sposób. Jest również przeznaczony do dołączania - możesz mieć zapasowy plik zip i dodawać (lub zastępować) pliki w razie potrzeby. Wszystko to stanowi ogromną pomoc przy korzystaniu z archiwów. Kompromis polega na tym, że jeśli kompresujesz pliki, które są bardzo podobne (co nie jest tak powszechne), nie może wykorzystać podobieństw do zmniejszenia rozmiaru archiwum.
Luaan,
43

W Zip każdy plik jest kompresowany osobno. Przeciwieństwem jest „solidna kompresja”, tzn. Pliki są kompresowane razem. 7-zip i Rar domyślnie używają solidnej kompresji. Gzip i Bzip2 nie mogą kompresować wielu plików, więc najpierw używany jest Tar, co daje taki sam efekt jak solidna kompresja.

Ponieważ plik xml ma podobną strukturę i prawdopodobnie podobną zawartość, jeśli pliki zostaną skompresowane razem, kompresja będzie wyższa.

Na przykład, jeśli plik zawiera ciąg, "<content><element name="a kompresor już znalazł ten ciąg w innym pliku, zastąpi go małym wskaźnikiem do poprzedniego dopasowania, jeśli kompresor nie używa „stałej kompresji”, pierwsza częstotliwość łańcucha w plik zostanie nagrany jako literał, który jest większy.

ggf31416
źródło
9

Zip nie tylko przechowuje zawartość pliku, ale także przechowuje metadane pliku, takie jak identyfikator użytkownika będącego właścicielem, uprawnienia, czasy tworzenia i modyfikacji itd. Jeśli masz jeden plik, masz jeden zestaw metadanych; jeśli masz 10 000 plików, masz 10 000 zestawów metadanych.

Mike Scott
źródło
3
Dobra uwaga, ale metadane systemowe zajmują tylko 1,4 MB miejsca. Zobacz moją edycję.
sixtyfootersdude
1
Nie znam algorytmu zip, ale metadane to nie tylko informacje o pliku, ale także rzeczy takie jak rozmiar i słownik, być może niektóre informacje o rozmieszczeniu znaków. Słownik niepustego pliku tekstowego będzie niezerowy. Prawdopodobnie dlatego widzisz większe metadane w plikach xml niż puste pliki.
Ben Richards,
To była moja pierwsza myśl. Informacje nagłówka pliku
zip
To tylko wyjaśnia różnicę między 2 a 3 - nie 4.
Luaan
@Luaan Nie, zarówno w 2, jak i 3 metadane dla wszystkich 10 000 plików są zawarte w pliku zip lub plikach zip, więc całkowity rozmiar pliku jest prawie taki sam. W wersji 4 są tylko metadane dla jednego pliku, a plik zip jest znacznie mniejszy.
Mike Scott,
7

Opcją pominiętą przez OP jest skompresowanie wszystkich plików razem z wyłączoną kompresją, a następnie skompresowanie wynikowego zipu z kompresją ustawioną na maksimum. To z grubsza emuluje zachowanie skompresowanych archiwów * nix .tar.Z, .tar.gz, .tar.bz itp., Umożliwiając kompresji wykorzystanie nadmiarowości między granicami plików (czego algorytm ZIP nie może wykonać, gdy jest uruchomiony w jednym przechodzić). Pozwala to później wyodrębnić poszczególne pliki XML, ale maksymalizuje kompresję. Minusem jest to, że proces wyodrębniania wymaga dodatkowego kroku, tymczasowo zajmując znacznie więcej miejsca na dysku niż byłoby to potrzebne w przypadku zwykłego pliku .zip.

Dzięki wszechobecności bezpłatnych narzędzi, takich jak 7-Zip do rozszerzenia rodziny tar na Windows, naprawdę nie ma powodu, aby nie używać .tar.gz lub .tar.bz itp., Ponieważ Linux, OS X i BSD mają natywne narzędzia do manipulowania nimi.

Monty Harder
źródło
gzip i bzip2 mogą skończyć się jeszcze gorzej, ponieważ zostały zaprojektowane z myślą o kompresowaniu strumieni, więc będą musiały rozpocząć wysyłanie skompresowanych danych, zanim wszystkie dane do kompresji będą znane.
rackandboneman
@rackandboneman: Jest to kompromis, który musisz zrobić, kompresując pliki większe niż ilość pamięci, którą chcesz użyć w czasie kompresji. (A także ilość czasu procesora wymagana do znalezienia wszystkiego, co byłoby optymalne globalnie, byłaby ogromna.) Ogromny słownik kompresji może również zwiększyć pamięć wymaganą do dekompresji . Jest to opcja dla LZMA ( xz/ 7-zip). W każdym razie adaptacyjne słowniki mogą wykrywać wzory, gdy są widoczne. To nie tak, że po prostu buduje system kodowania statycznego oparty na pierwszych 32k. Właśnie dlatego gzip nie jest do kitu.
Peter Cordes,
Naprawdę podoba mi się ta „sztuczka”, jeśli chcesz pozostać przy formacie zip. Nie zgadzam się z twoim „nie ma powodu, aby nie używać 7-zip” - jeśli wysyłam plik do nietechnicznego przyjaciela, chcę mieć pewność, że będzie mógł go łatwo otworzyć. Jeśli wysyłam do klienta biznesowego, tym bardziej.
Wowfunhappy
5

Format kompresji zip przechowuje i kompresuje każdy plik osobno. Nie korzysta z powtarzania między plikami, tylko w obrębie pliku.

Łączenie pliku pozwala zipowi na skorzystanie z powtórzeń we wszystkich plikach, co powoduje znacznie większą kompresję.

Powiedzmy na przykład, że każdy plik XML ma określony nagłówek. Nagłówek występuje tylko raz w każdym pliku, ale jest powtarzany prawie identycznie w wielu innych plikach. W metodach 2 i 3 zip nie mógł tego skompresować, ale w metodzie 4 mógł.

Dąb Bonsai
źródło
3
Czym różni się to od jednej z 3 najlepszych odpowiedzi opublikowanych już 5 godzin wcześniej?
Xen2050,
1
@ Xen2050 Niewielka różnica, po prostu pomyślałem, że mogę to wyjaśnić jaśniej.
BonsaiOak,
1
@BonsaiOak - następnie dodaj komentarz do poprawnej odpowiedzi lub edytuj, jeśli masz wystarczającą liczbę przedstawicieli. Jeśli nie, ale Twój komentarz dodaje jasności, ktoś inny może to odebrać i edytować post.
AdamV
@AdamV Rozumiem twój punkt widzenia. Moja odpowiedź nie dodaje obecnie żadnych użytecznych informacji, chociaż prawdopodobnie zrobiła to, kiedy to napisałem. Pod pierwszą odpowiedzią są już odpowiednie komentarze, więc nie widzę też sensu ich dodawania. Mówisz, że powinienem po prostu zamknąć odpowiedź? Jaką szkodę ma pozostawienie go otwartego?
BonsaiOak,
4

Obok metadanych wspomnianych przez Mike'a Scotta w algorytmie kompresji występuje również narzut.

Podczas kompresji kilku pojedynczych małych plików musisz mieć dużo szczęścia, aby móc je skompresować, tak aby wypełnić jeden blok kompresji. Podczas kompresji pojedynczego bloku monolitycznego system może po prostu kontynuować przesyłanie strumieniowe danych do algorytmu, ignorując „granice” (z powodu braku lepszego słowa) poszczególnych plików.

Wiadomo również, że ASCII ma wysoki współczynnik kompresji. Ponadto XML jest często bardzo powtarzalny, co sprawia, że ​​metadane stanowią dużą część danych, których nie można tak łatwo skompresować jak zawartość XML.

Wreszcie, jeśli pamięć działa poprawnie, zip używa kodowania słownikowego, co jest szczególnie skuteczne w plikach ascii, a tym bardziej w XML ze względu na ich powtarzalność

Wyjaśnienie kompresji danych: http://mattmahoney.net/dc/dce.html

GapWim
źródło
3

Rozważ to XML:

<root>
  <element id="1" />
  <element id="2" /> 
  <other id="3" />
  ...
</root>

XML ma bardzo powtarzalną strukturę, Zip korzysta z tych powtórzeń, aby zbudować słownik, w którym wzorzec ma więcej wystąpień, a następnie, podczas kompresji, używa mniej bitów do przechowywania większej liczby powtarzających się wzorów, a więcej bitów do przechowywania mniej powtarzanych wzorów .

Gdy połączysz te pliki, plik źródłowy (źródło zip) jest duży, ale zawiera znacznie więcej powtarzających się wzorców, ponieważ dystrybucja nudnych struktur XML jest amortyzowana w dużym całym pliku, dając szansę ZIPowi na przechowanie tych wzorów używając mniej bitów.

Teraz, jeśli połączysz różne XML w jeden plik, nawet jeśli pliki te mają zupełnie inne nazwy znaczników, algorytm kompresji znajdzie najlepszą dystrybucję wzorców we wszystkich plikach, a nie plik po pliku.

Ostatecznie algorytm kompresji znalazł najlepszy powtarzalny rozkład wzorca.

rnrneverdies
źródło
-1

Oprócz odpowiedzi na 7-Zip istnieje jeszcze jedno podejście, które nie jest tak dobre, ale byłoby warte przetestowania, jeśli z jakiegoś powodu nie chcesz używać 7-Zip:

Skompresuj plik zip. Teraz zwykle plik zip jest nieściśliwy, ale gdy zawiera wiele identycznych plików, kompresor może znaleźć tę nadmiarowość i skompresować ją. Zauważ, że zauważyłem również niewielki zysk w przypadku dużej liczby plików bez redundancji. Jeśli naprawdę zależy Ci na rozmiarze, warto wypróbować, jeśli masz w archiwum bardzo dużo plików.

Loren Pechtel
źródło
Działa to tylko wtedy, gdy zrobisz pierwszy zip z wyłączoną kompresją, jak wspomniałem powyżej.
Monty Harder
@MontyHarder Widziałem, jak działa przy włączonej kompresji.
Loren Pechtel,