This commit is contained in:
Denis Landa 2026-01-21 00:03:03 +01:00
parent 405a2511af
commit 8c891a179a
5 changed files with 388 additions and 0 deletions

17
sk1/Makefile Normal file
View File

@ -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)

65
sk1/README.md Normal file
View File

@ -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

263
sk1/compressor.c Normal file
View File

@ -0,0 +1,263 @@
#include <stdio.h>
#include <stdlib.h>
#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;i<velkost;i++) frekvencia[data[i]]++;
struct node* pole[256];
int n=0;
for(i=0;i<256;i++){
if(frekvencia[i]>0){
pole[n] = vytvor_node(i, frekvencia[i]);
n++;
}
}
for(i=0;i<n-1;i++){
for(int j=0;j<n-i-1;j++){
if(pole[j]->pocet > 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;i<n-2;i++){
pole[i] = pole[i+2];
}
n = n-2;
int vlozene=0;
for(i=0;i<n;i++){
if(parent->pocet < 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<velkost;i++){
unsigned int kod = kody[data[i]];
unsigned char dlzka = dlzky[data[i]];
for(int j=dlzka-1;j>=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;i<n-1;i++){
for(int j=0;j<n-i-1;j++){
if(pole[j]->pocet > 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;i<n-2;i++){
pole[i] = pole[i+2];
}
n = n-2;
int vlozene=0;
for(i=0;i<n;i++){
if(parent->pocet < 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<komp_velkost && vystup_pos<velkost;i++){
for(int bit=7;bit>=0 && vystup_pos<velkost;bit--){
if((komp_data[i]>>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;
}

7
sk1/compressor.h Normal file
View File

@ -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

36
sk1/main.c Normal file
View File

@ -0,0 +1,36 @@
#include <stdio.h>
#include <string.h>
#include "compressor.h"
void pomoc(){
printf("Huffman kompresor\n");
printf("Pouzitie:\n");
printf(" ./compressor -c <vstupny_subor> <vystupny_subor>\n");
printf(" ./compressor -d <komprimovany_subor> <vystupny_subor>\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;
}