diff --git a/sk1/Makefile b/sk1/Makefile new file mode 100644 index 0000000..59cc8cd --- /dev/null +++ b/sk1/Makefile @@ -0,0 +1,17 @@ +CC=gcc +CFLAGS=-Wall -std=c99 +TARGET=compressor + +all: $(TARGET) + +$(TARGET): main.o compressor.o + $(CC) $(CFLAGS) -o $(TARGET) main.o compressor.o + +main.o: main.c compressor.h + $(CC) $(CFLAGS) -c main.c + +compressor.o: compressor.c compressor.h + $(CC) $(CFLAGS) -c compressor.c + +clean: + rm -f *.o $(TARGET) diff --git a/sk1/README.md b/sk1/README.md new file mode 100644 index 0000000..331a608 --- /dev/null +++ b/sk1/README.md @@ -0,0 +1,65 @@ +Zadanie +-Naprogramovať nástroj na kompresiu a dekompresiu binárnych súborov pomocou kompresného algoritmu (nie RLE). Program má podporovať súbory do 10 MB, dosiahnuť minimálne 10% kompresiu a po dekompresii má byť súbor identický s pôvodným. + +Stručný opis funkčnosti +-Program komprimuje a dekomprimuje súbory pomocou Huffmanovho kódovania. Pri +kompresii analyzuje frekvenciu výskytu jednotlivých bajtov a vytvorí pre ne +optimálne binárne kódy - častejšie bajty dostanú kratšie kódy. Pri dekompresii +použije uloženú frekvenčnú tabuľku na rekonštrukciu pôvodných dát. + + + +STRUČNY OPIS RIEŠENIA + + +Program používa Huffmanovo kódovanie: + +Kompresia: +Načíta súbor do pamäte +Spočíta frekvenciu každého bajtu (0-255) +Vytvorí zoznam uzlov pre bajty s nenulovou frekvenciou +Zoradí uzly podľa frekvencie (bubble sort) +Postupne spája dva uzly s najnižšou frekvenciou do stromu +Vygeneruje binárne kódy prechádzaním stromu +Zakóduje dáta pomocou týchto kódov bit po bite +Uloží veľkosť, frekvenčnú tabuľku a komprimované dáta + + +Dekompresia: +Načíta veľkosť a frekvenčnú tabuľku +Zrekonštruuje Huffmanov strom rovnakým spôsobom +Číta komprimované dáta bit po bite +Pre každý bit prechádza stromom (0=vľavo, 1=vpravo) +Keď dosiahne list, zapíše zodpovedajúci bajt +Pokračuje kým nedekóduje celý súbor + + + +PODMIENKY FUNGOVANIA + + +Program funguje správne za týchto podmienok: + +Vstupný súbor existuje a je čitateľný +Veľkosť súboru je maximálne 10 MB +Dostatočná voľná pamäť (aspoň 2× veľkosť súboru) +Súbor nie je prázdny +Pri dekompresii sa používa súbor vytvorený týmto programom + +Kompresia: + +Textové súbory: 40-60% kompresia +Zdrojové kódy: 30-50% kompresia +Binárne súbory: 5-30% kompresia (závisí od obsahu) +Už komprimované súbory (zip, jpg): minimálna alebo žiadna kompresia +Veľmi malé súbory (<1 KB): môžu byť väčšie kvôli hlavičke (1032 B) + + +ZOZNAM POUŽITYCH ZDROJOV + +Youtube: https://www.youtube.com/watch?v=JsTptu56GM8 +Youtube: https://www.youtube.com/watch?v=iiGZ947Tcck +Youtube: https://www.youtube.com/watch?v=goOa3DGezUA +Internet: https://en.wikipedia.org/wiki/LZ77_and_LZ78 +Internet: ChatGBT Prompt: Príklady kompresie dát + diff --git a/sk1/compressor.c b/sk1/compressor.c new file mode 100644 index 0000000..f1c649b --- /dev/null +++ b/sk1/compressor.c @@ -0,0 +1,263 @@ +#include +#include +#include "compressor.h" + +struct node { + unsigned char znak; + int pocet; + struct node *left; + struct node *right; +}; + +struct node* vytvor_node(unsigned char c, int p) { + struct node* n = malloc(sizeof(struct node)); + n->znak = c; + n->pocet = p; + n->left = NULL; + n->right = NULL; + return n; +} + +int compress_file(char *input, char *output) { + FILE *f = fopen(input, "rb"); + if(!f) { + printf("Neviem otvorit subor\n"); + return 1; + } + + fseek(f, 0, SEEK_END); + long velkost = ftell(f); + fseek(f, 0, SEEK_SET); + + unsigned char *data = malloc(velkost); + fread(data, 1, velkost, f); + fclose(f); + + int frekvencia[256]; + int i; + for(i=0;i<256;i++) frekvencia[i]=0; + for(i=0;i0){ + pole[n] = vytvor_node(i, frekvencia[i]); + n++; + } + } + + for(i=0;ipocet > pole[j+1]->pocet){ + struct node* tmp = pole[j]; + pole[j] = pole[j+1]; + pole[j+1] = tmp; + } + } + } + + while(n>1){ + struct node* left = pole[0]; + struct node* right = pole[1]; + + struct node* parent = vytvor_node(0, left->pocet + right->pocet); + parent->left = left; + parent->right = right; + + for(i=0;ipocet < pole[i]->pocet){ + for(int j=n;j>i;j--){ + pole[j] = pole[j-1]; + } + pole[i] = parent; + vlozene=1; + break; + } + } + if(!vlozene) pole[n] = parent; + n++; + } + + struct node* root = pole[0]; + + unsigned int kody[256]; + unsigned char dlzky[256]; + for(i=0;i<256;i++){ + kody[i]=0; + dlzky[i]=0; + } + struct node* zasobnik[512]; + unsigned int kod_zasobnik[512]; + unsigned char dlzka_zasobnik[512]; + int top=0; + + zasobnik[top] = root; + kod_zasobnik[top] = 0; + dlzka_zasobnik[top] = 0; + top++; + + while(top>0){ + top--; + struct node* aktualny = zasobnik[top]; + unsigned int kod = kod_zasobnik[top]; + unsigned char dlzka = dlzka_zasobnik[top]; + + if(!aktualny->left && !aktualny->right){ + kody[aktualny->znak] = kod; + dlzky[aktualny->znak] = dlzka>0 ? dlzka : 1; + }else{ + if(aktualny->left){ + zasobnik[top] = aktualny->left; + kod_zasobnik[top] = kod<<1; + dlzka_zasobnik[top] = dlzka+1; + top++; + } + if(aktualny->right){ + zasobnik[top] = aktualny->right; + kod_zasobnik[top] = (kod<<1)|1; + dlzka_zasobnik[top] = dlzka+1; + top++; + } + } + } + unsigned char* vystup = malloc(velkost*2); + long pos=0; + unsigned char bajt=0; + int bit_pos=0; + + for(i=0;i=0;j--){ + if((kod>>j)&1){ + bajt |= (1<<(7-bit_pos)); + } + bit_pos++; + if(bit_pos==8){ + vystup[pos++] = bajt; + bajt=0; + bit_pos=0; + } + } + } + if(bit_pos>0) vystup[pos++]=bajt; + + FILE *fout = fopen(output, "wb"); + fwrite(&velkost, sizeof(long), 1, fout); + fwrite(frekvencia, sizeof(int), 256, fout); + fwrite(vystup, 1, pos, fout); + fclose(fout); + + printf("Komprimovane: %ld -> %ld\n", velkost, pos+sizeof(long)+256*sizeof(int)); + + free(data); + free(vystup); + return 0; +} + +int decompress_file(char *input, char *output) { + FILE *f = fopen(input, "rb"); + if(!f){ + printf("Neviem otvorit subor\n"); + return 1; + } + long velkost; + int frekvencia[256]; + fread(&velkost, sizeof(long), 1, f); + fread(frekvencia, sizeof(int), 256, f); + + struct node* pole[256]; + int n=0; + int i; + for(i=0;i<256;i++){ + if(frekvencia[i]>0){ + pole[n] = vytvor_node(i, frekvencia[i]); + n++; + } + } + for(i=0;ipocet > pole[j+1]->pocet){ + struct node* tmp = pole[j]; + pole[j] = pole[j+1]; + pole[j+1] = tmp; + } + } + } + + while(n>1){ + struct node* left = pole[0]; + struct node* right = pole[1]; + + struct node* parent = vytvor_node(0, left->pocet + right->pocet); + parent->left = left; + parent->right = right; + + for(i=0;ipocet < pole[i]->pocet){ + for(int j=n;j>i;j--){ + pole[j] = pole[j-1]; + } + pole[i] = parent; + vlozene=1; + break; + } + } + if(!vlozene) pole[n] = parent; + n++; + } + + struct node* root = pole[0]; + + fseek(f, 0, SEEK_END); + long total = ftell(f); + long komp_velkost = total - sizeof(long) - 256*sizeof(int); + fseek(f, sizeof(long) + 256*sizeof(int), SEEK_SET); + + unsigned char *komp_data = malloc(komp_velkost); + fread(komp_data, 1, komp_velkost, f); + fclose(f); + + unsigned char *vystup = malloc(velkost); + long vystup_pos=0; + struct node* aktualny = root; + + for(i=0;i=0 && vystup_pos>bit)&1){ + aktualny = aktualny->right; + }else{ + aktualny = aktualny->left; + } + + if(!aktualny->left && !aktualny->right){ + vystup[vystup_pos++] = aktualny->znak; + aktualny = root; + } + } + } + + FILE *fout = fopen(output, "wb"); + fwrite(vystup, 1, velkost, fout); + fclose(fout); + + printf("Dekomprimovane: %ld\n", velkost); + + free(komp_data); + free(vystup); + return 0; +} diff --git a/sk1/compressor.h b/sk1/compressor.h new file mode 100644 index 0000000..1e79c72 --- /dev/null +++ b/sk1/compressor.h @@ -0,0 +1,7 @@ +#ifndef COMPRESSOR_H +#define COMPRESSOR_H + +int compress_file(char *input, char *output); +int decompress_file(char *input, char *output); + +#endif diff --git a/sk1/main.c b/sk1/main.c new file mode 100644 index 0000000..c6f382f --- /dev/null +++ b/sk1/main.c @@ -0,0 +1,36 @@ +#include +#include +#include "compressor.h" + +void pomoc(){ + printf("Huffman kompresor\n"); + printf("Pouzitie:\n"); + printf(" ./compressor -c \n"); + printf(" ./compressor -d \n"); + printf(" ./compressor -h\n"); +} + +int main(int argc, char *argv[]){ + if(argc==2 && strcmp(argv[1], "-h")==0){ + pomoc(); + return 0; + } + + if(argc!=4) { + printf("Nespravny pocet argumentov!\n"); + pomoc(); + return 1; + } + + if(strcmp(argv[1], "-c")==0){ + return compress_file(argv[2], argv[3]); + }else if(strcmp(argv[1], "-d")==0) { + return decompress_file(argv[2], argv[3]); + }else{ + printf("Neznamy prepinac: %s\n", argv[1]); + pomoc(); + return 1; + } + + return 0; +}