huff
This commit is contained in:
parent
f396159878
commit
a0c4f91d0e
21
sk1/Makefile
Normal file
21
sk1/Makefile
Normal 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
35
sk1/README.md
Normal 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
140
sk1/compressor.c
Normal 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
24
sk1/compressor.h
Normal 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
23
sk1/main.c
Normal 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;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user