Witam. W tym artykule przedstawiam korzystanie z unitu Zlib.

Unit Zlib służy do pakowania danych algorytmem Deflate, co pozwala zaoszczędzić miejsce na dysku zajmowane przez dane używane przez nasz program. Pakowanie przebiega dość szybko, szczególnie w nowszych wersjach unitu, a rozpakowywanie-bardzo szybko. Algorytm nie należy do wydajnych, nie można porównywać rozmiaru spakowanych danych do rozmiaru spakowanych tych samych danych np. WinRarem, jednakże obsługa Zliba jest bardzo prosta, nie musimy szukać dodatkowych bibliotek, instalować komponentów, wszystko mamy na miejscu. Jeżeli chcemy korzystać z unitu Zlib, musi dodać go do sekcji uses. Jest on dołączony standardowo do Delphi, jednakże zalecam pobrać najnowszą wersję ze strony:
http://www.base2ti.com/zlib.htm
gdyż unit został nieco rozbudowany a algorytm kompresji przyspieszony. W tym artykule używam wersji 1.2.3, a w razie czego podam co należy zmienić aby przykłady działały także w starszej wersji, to znaczy tej, która jest standardowo dołączona do Delphi (dodam tylko, że pracuję w Delphi 7). Do ściągnięcia są przykłady dla obu wersji, starszej i nowszej, w jednym archiwum.

Obsługa nowszej wersji.

Jeżeli nie masz zamiaru korzystać z nowszej wersji Zlib’a, opuść ten paragraf.

Po ściągnięciu nowej wersji Zlib’a, wypakuj go i nazwij folder ‘Zlib’, następnie przenieś go do folderu z Delphi. Uruchom Delphi’aka, wybierz Menu->Tools->Environment Options, następnie kliknij na zakładkę Library i w miejscu LibraryPath na samym końcu dopisz:

;$(DELPHI)\Zlib\

Możesz od teraz korzystać z nowego unitu, który w wersji 1.2.3 nazywa się ZlibEx, a w wersji dołączonej standardowo do Delphi-Zlib.

Opisy klasy TZCompressionStream

Do pakowania danych używamy strumienia klasy TZCompressionStream (w starszej wersji: TCompressionStream), która jest pochodną od TCustomZStream (poprzednio TCustomZlibStream). TZCompressionStream jest strumieniem tylko i wyłącznie do zapisu. Kompresuje on dane, wpisując je do strumienia, który podamy podczas wywoływania konstruktora strumienia pakującego. Musimy jednak sami zatroszczyć się o wcześniejsze utworzenie strumienia docelowego oraz zwolnienie go, gdy skończymy na nim pracę.

Próba czytania z obiektu TZCompressionStream wywoła wyjątek typu ECompressionError z wiadomością „Invalid Stream Operation”. To samo dotyczy próby użycia funkcji Seek (służy ona do przesuwania się po pliku). Jednakże, użycie funkcji Seek z parametrami Offset = 0 oraz Origin = soFromCurrent zwróci liczbę bajtów już wpisanych do strumienia.

Do dyspozycji mamy jeszcze właściwość CompressionRate (typu Single), która oznacza, jakim procentem oryginalnej danej jest jej spakowana wersja, np. jeżeli plik zajmował przed kompresją 100KB, a po kompresji zajmuje 10KB, to współczynnik kompresji wyniesie 10(%).

Do utworzenia nowego strumienia mamy do dyspozycji dwa konstruktory (w starszej wersji-tylko jeden). W artykule przedstawię tylko ten wspólny dla starszej i nowszej wersji. Jako pierwszy parametr podajemy strumień, do którego wpisywane będą skompresowane dane. Natomiast drugi parametr oznacza rodzaj kompresji (w nowej bibliotece jest to domyślnie zcDefault). W starszej wersji biblioteki, parametry zamienione są ze sobą miejscami:

Do wyboru mamy 4 rodzaje kompresji (w nawiasie podałem nazwę ze starej biblioteki):

Wywołując konstruktor, musimy podać strumień, do którego wpisywane będą kompresowane dane. Posłużymy się strumieniem klasy TFileStream. Aby skompresować dane, używamy funkcji Write, podając w parametrach źródło danych oraz ich rozmiar. Funkcja ta w rezultacie zwraca ilość bajtów wypisanych danych.
Podczas wpisywania, gdy zapełni się bufor wyjścia strumienia, zawartość bufora zostanie wpisana do zewnętrznego strumienia (podanego podczas tworzenia strumienie kompresującego). Po wpisaniu, uruchomiane jest zdarzenie OnProgress. Możemy w nim np. poinformować użytkownika postęp kompresji danych.

Uff…mnóstwo teorii 😉 To co wyżej napisałem może się wydawać trudne/niezrozumiałe, ale zapewniam, że korzystanie ze Zlib’a jest banalnie proste. Czas na praktykę! Czas na

Kompresowanie danych.

Zacznijmy od zdefiniowania strumienia kompresującego oraz strumienia, do którego wpisywane będą skompresowane dane. Niech będą one obiektami globalnymi:

Mamy już nasze strumienie, teraz dodaj dwa przyciski na formę. Zdarzenie OnClick pierwszego z nich powinno wyglądać następująco:

Skompresujmy coś wreszcie!

Uruchom program, kliknij najpierw na pierwszy, a potem na drugi przycisk. Po chwili kompresja dobiegnie końca.
W tym przypadku, nieskompresowane dane zajmują 3,81MB – dużo…po kompresji z parametrem zcDefault – 2,54MB. Nadmienię tylko, że ten sam plik spakowany WinRar’em zajmuje 105KB.
Spróbuj poeksperymentować, inaczej inicjalizując tablicę Mapa oraz zmieniając sposoby kompresji na zcMax lub zcFastest, a aby się przekonać, ile dana zajmuje w nieskompresowanej postaci, użyj zcNone, lub po prostu – SizeOf. W przypadku zcMax, róznica nie będzie wielka, jeśli w ogóle widoczna, a spowolni widocznie proces kompresji, natomiast użycie parametru zcFastest zwiększy się zauważalnie rozmiar pliku, ale także da się odczuć szybsze przyspieszenie kompresji.

Jak widać, kompresowanie jest łatwe. Dodamy teraz pasek postępu, informujący nas ile % już skompresowano. Poza tym, wypiszemy, o ile % skompresowane dane są mniejsze od danych nieskompresowanych.

Dodaj na formę dwie etykiety (label) oraz pasek postępu (progress bar). W sekcji public formy wpisz deklarację metody, która zostanie przypisana do zdarzenia OnProgress strumienia kompresującego:

Dodaj definicję:

Najpierw obliczamy, jakim % rozmiaru mapy jest pozycja (w bajtach) strumienia kompresującego w Mapie. Zaokrąglamy wynik. W drugiej linijce wpisujemy do etykiety procentowe wykonanie zadania. Następnie wywołujemy funkcję ProcessMessages, przez którą aplikacja nie sprawia wrażenia zawieszonej. Dzięki niej napis na etykiecie uaktualniany jest ‘na bieżąco’.

Pozostało już tylko zmodyfikować procedurę OnClick drugiego przycisku:

Kompresowanie mamy opanowane, poznajmy zatem

Opis klasy TZDecompressionStream.

Jak nietrudno się domyślić, strumienia tej klasy (w starszej wersji klasa ta nosi nazwę: TDecompressionStream) używać będziemy do rozpakowywania danych. Strumień ten służy jedynie do odczytu, a próba wpisu danych spowoduje wywołanie wyjątku. Możliwe jest przesuwanie się w strumieniu do przodu, ale nie do tyłu. W klasie TZDecompressionStream (co logiczne) nie ma właściwości CompressionRate. Poza tym, możesz wywołać funkcję Seek z parametrem Offset = 0 i Origin = soFromBeginning. W efekcie, strumień wyjściowy zostanie zresetowany, przesuwając go na początek. W przeciwnym razie, możesz jako Offset podać dodatni argument typu Longint i Origin = soFromCurrent lub soFromBeginning, co spowoduje przesunięcie się zewnętrznego strumienia do przodu, dekompresując dane, po których się przesunąłeś.

Dalej nie będą opisywał, ponieważ TZDecompressionStream nie różnie się znacznie od TZCompressionStream. Po więcej inforamcji zajrzyj do fragmentu o opisie klasy kompresującej.

Zajmiemy się teraz używaniem nowo poznanej klasy.

Dekompresowanie danych.

Dodajmy nową definicję globalnego obiektu TDecompressionStream:

Dodaj na formę nowy przycisk. Jego procedura OnClick powinna wyglądać następująco:

Dodaj także metodę ZlibDecompressionProgress. Deklaracja:

oraz definicja:

Rozpakowywanie trwa dużo szybciej od pakowania. Załączam przykładowe kody źródłowe, jeden, pracujący na starszej wersji oraz drugi, wykorzystujący nowszą wersję Zlib’a.

Oficjalna strona Zlib’a: http://www.zlib.net/

Na tym zakończę artykuł. Dzięki świeżo nabytej wiedzy możesz zacząć wykorzystywać Zlib’a w swoich aplikacjach, co pozwoli na zmniejszenie rozmiaru danych przez nią wykorzystywanych. W razie uwag/spostrzeżeń/problemów/sugestii itd. – napisz co i jak na forum, w dziale poświęconym serwisowi lub wyślij mi PW.

Autor: Iskar

Załączniki