This commit is contained in:
Matej Hajduk 2026-02-07 01:44:33 +01:00
parent f396159878
commit a0c4f91d0e
5 changed files with 243 additions and 0 deletions

21
sk1/Makefile Normal file
View File

@ -0,0 +1,21 @@
CC=gcc
CFLAGS=-Wall -Wextra -std=c11
all: compressor
compressor: main.o compressor.o
$(CC) $(CFLAGS) -o compressor 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 compressor

35
sk1/README.md Normal file
View File

@ -0,0 +1,35 @@
Zadanie
Cieľom domácej úlohy je navrhnúť a implementovať program v jazyku C, ktorý umožňuje kompresiu a dekompresiu ľubovoľných binárnych súborov s maximálnou veľkosťou 10 MB. Na kompresiu je potrebné použiť niektorý z bežných kompresných algoritmov, napríklad Huffmanovo kódovanie, LZ77 alebo LZ78, prípadne vlastný algoritmus. Použitie algoritmu RLE nie je povolené. Po dekompresii musí byť výsledný súbor bitovo zhodný s pôvodným vstupným súborom. Program musí používať výhradne štandardnú knižnicu jazyka C a musí byť spustiteľný z príkazového riadka. Rozhranie programu má podporovať prepínače na kompresiu, dekompresiu a zobrazenie nápovedy.
Stručný opis funkčnosti
Program umožňuje skomprimovať zadaný vstupný súbor do výstupného súboru pomocou prepínača -c a dekomprimovať už skomprimovaný súbor späť do pôvodnej podoby pomocou prepínača -d. Vstupný a výstupný súbor sa zadávajú ako argumenty príkazového riadka. Program pracuje priamo s binárnymi dátami a nepredpokladá žiadny konkrétny formát vstupného súboru. Súčasťou programu je aj nápoveda, ktorá sa zobrazí pri nesprávnom použití alebo pri spustení s neplatnými argumentmi.
Stručný opis riešenia
Riešenie je založené na Huffmanovom kódovaní, ktoré patrí medzi bezstratové kompresné algoritmy. Pri kompresii program najskôr prečíta celý vstupný súbor a spočíta frekvenciu výskytu každého z 256 možných bajtových symbolov. Tieto frekvencie sa uložia do hlavičky výstupného súboru, aby bolo možné pri dekompresii znovu zostaviť identický Huffmanov strom.
Na základe vypočítaných frekvencií sa zostaví Huffmanov strom, v ktorom majú menej časté symboly dlhšie binárne kódy a častejšie symboly kratšie kódy. Z tohto stromu sa následne rekurzívne vygenerujú binárne kódy pre jednotlivé symboly. Po vytvorení kódov sa vstupný súbor znovu prečíta a jeho obsah sa zapíše do výstupného súboru v zakódovanej bitovej podobe. Bity sú postupne zoskupované do bajtov a zapisované do súboru.
Pri dekompresii program najskôr načíta hlavičku súboru obsahujúcu frekvencie symbolov. Z týchto frekvencií sa opätovne zostaví Huffmanov strom rovnakým spôsobom ako pri kompresii. Program následne číta zakódované bajty zo súboru, postupne ich rozkladá na jednotlivé bity a podľa nich prechádza Huffmanov strom. Po dosiahnutí listového uzla sa zapíše príslušný symbol do výstupného súboru. Tento proces pokračuje až do spracovania celého vstupného súboru.
Podmienky správnej funkčnosti
Program je určený na spúšťanie v prostredí s podporou štandardu jazyka C11. Používa výhradne štandardnú knižnicu jazyka C a nevyužíva žiadne externé knižnice. Program predpokladá korektný vstup vo forme existujúceho súboru a dostatočné oprávnenia na čítanie a zápis súborov. Maximálna veľkosť vstupného súboru je 10 MB. Správna funkčnosť programu bola overená testovaním na binárnych súboroch z Canterbury corpus. Po dekompresii musí byť výsledný súbor bitovo zhodný s pôvodným súborom.
Zoznam použitých zdrojov
Prednášky a cvičenia k predmetu zameranému na dátové štruktúry a algoritmy. Online vzdelávacie materiály GeeksForGeeks k téme Huffmanovo kódovanie. Generatívny jazykový model ChatGPT bol použitý ako pomocný nástroj na konzultáciu návrhu riešenia a štruktúry programu. Použitý prompt bol: Implement Huffman compressor in C.

140
sk1/compressor.c Normal file
View File

@ -0,0 +1,140 @@
#include "compressor.h"
#include <string.h>
static HuffmanNode *create_node(unsigned char s, uint32_t f) {
HuffmanNode *n = malloc(sizeof(HuffmanNode));
n->symbol = s;
n->freq = f;
n->left = n->right = NULL;
return n;
}
static void free_tree(HuffmanNode *root) {
if (!root) return;
free_tree(root->left);
free_tree(root->right);
free(root);
}
static HuffmanNode *build_tree(uint32_t freq[]) {
HuffmanNode *nodes[SYMBOLS * 2];
int n = 0;
for (int i = 0; i < SYMBOLS; i++)
if (freq[i] > 0)
nodes[n++] = create_node((unsigned char)i, freq[i]);while (n > 1) {
int a = 0, b = 1;
if (nodes[b]->freq < nodes[a]->freq) { int t=a;a=b;b=t; }
for (int i = 2; i < n; i++) {
if (nodes[i]->freq < nodes[a]->freq) {
b = a; a = i;
} else if (nodes[i]->freq < nodes[b]->freq) {
b = i;
}
}
HuffmanNode *p = create_node(0, nodes[a]->freq + nodes[b]->freq);
p->left = nodes[a];
p->right = nodes[b];
nodes[a] = p;
nodes[b] = nodes[n-1];
n--;
}
return nodes[0];
}
static void build_codes(HuffmanNode *r, char *code, int d, char codes[SYMBOLS][256]) {
if (!r->left && !r->right) {
code[d] = '\0';
strcpy(codes[r->symbol], code);
return;
if (r->left) {
code[d] = '0';
build_codes(r->left, code, d+1, codes);
}
if (r->right) {
code[d] = '1';
build_codes(r->right, code, d+1, codes);
}
}
int compress_file(const char *infile, const char *outfile) {
FILE *in = fopen(infile, "rb");
FILE *out = fopen(outfile, "wb");
if (!in || !out) return 1;
uint32_t freq[SYMBOLS] = {0};
int c;
while ((c = fgetc(in)) != EOF) freq[c]++;}fwrite(freq, sizeof(uint32_t), SYMBOLS, out);
HuffmanNode *root = build_tree(freq);
char codes[SYMBOLS][256] = {{0}};
char tmp[256];
build_codes(root, tmp, 0, codes);
rewind(in);
unsigned char buf = 0;
int bits = 0;
while ((c = fgetc(in)) != EOF) {
char *p = codes[c];
while (*p) {
buf <<= 1;
if (*p == '1') buf |= 1;
bits++;
if (bits == 8) {
fputc(buf, out);
buf = 0;
bits = 0;
}
p++;
}
}
if (bits) {
buf <<= (8 - bits);
fputc(buf, out);
free_tree(root);
fclose(in);
fclose(out);
return 0;
}
int decompress_file(const char *infile, const char *outfile) {
FILE *in = fopen(infile, "rb");
FILE *out = fopen(outfile, "wb");
if (!in || !out) return 1;
uint32_t freq[SYMBOLS];
fread(freq, sizeof(uint32_t), SYMBOLS, in);
HuffmanNode *root = build_tree(freq);
HuffmanNode *cur = root;
int byte;
while ((byte = fgetc(in)) != EOF) {
for (int i = 7; i >= 0; i--) {
int bit = (byte >> i) & 1;
cur = bit ? cur->right : cur->left;
if (!cur->left && !cur->right) {
fputc(cur->symbol, out);
cur = root;
}
}
}}free_tree(root);
fclose(in);
fclose(out);
return 0;
}

24
sk1/compressor.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef COMPRESSOR_H
#define COMPRESSOR_H
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define SYMBOLS 256
typedef struct HuffmanNode {
unsigned char symbol;
uint32_t freq;
struct HuffmanNode *left, *right;
} HuffmanNode;
int compress_file(const char *infile, const char *outfile);
int decompress_file(const char *infile, const char *outfile);
#endif

23
sk1/main.c Normal file
View File

@ -0,0 +1,23 @@
#include "compressor.h"
#include <string.h>
static void help(void) {
printf("Použitie:\n");
printf(" ./compressor -c infile outfile\n");
printf(" ./compressor -d infile outfile\n");
}
int main(int argc, char *argv[]) {
if (argc < 2) {
help();
return 1;
}
if (!strcmp(argv[1], "-c") && argc == 4)
return compress_file(argv[2], argv[3]);
if (!strcmp(argv[1], "-d") && argc == 4)
return decompress_file(argv[2], argv[3]);
help();
return 1;
}