素ハフマン圧縮プログラム。
なんか、ハフマン符号化が少しわかった気がするぞ。あと二分木とか。プログラムを書いてようやく。実践を伴わない知識っていうのは宙に浮いてるようで、なんか居心地が悪い。実際に使ってみると、なんだか錨で固定したような気分になれるなあ。二分木に関しては「たぶんこんな感じのデータ構造だったっけ?」と適当に使ったから、スゲー間抜けなことしてるかも。というかそんなこと言い出したら全部そうか。
まず元データを読み込み、バイトごとの出現回数を数える。この出現回数表を書庫の先頭に記述。その回数からハフマン木を作成。ハフマン木により、1バイト->ビット列の変換表を作成。元データを頭から読み込み、先の変換表により書庫に追記。
というプログラムを作成。
以下ソース。
#include <stdio.h>
#include <string.h>
#define HCODEMAX 256
typedef struct{
unsigned char c;
int count;
} item;
struct node{
item data;
struct node *lbranch, *rbranch;
};
struct node** isort_count(struct node *table[], int length){
int i, j;
struct node *tmp;
for(i=1;i<length;++i){
tmp = table[i];
for(j=i;j>0 && table[j-1]->data.count>tmp->data.count;--j)
table[j] = table[j-1];
table[j] = tmp;
}
return table;
}
struct node* mknode(struct node *lbranch, struct node *rbranch){
struct node *n;
n = (struct node *)malloc(sizeof(struct node));
n->data.count = 0;
n->lbranch = lbranch;
n->rbranch = rbranch;
return n;
}
struct node* mktree(struct node *table[], int length){
struct node *root, *n;
if(length == 1) return table[0];
n = mknode(table[0], table[1]);
n->data.count = table[0]->data.count + table[1]->data.count;
table[1] = n;
isort_count(&table[1], length-1);
root = mktree(&table[1], length-1);
return root;
}
int print_table(FILE *fp, struct node *table[]){
int i;
for(i=0;i<256;++i){
if(table[i]->data.count > 0){
fwrite(&(table[i]->data.c), 1, 1, fp);
fwrite(&(table[i]->data.count), 4, 1, fp);
}
}
}
int mkhtable(char *htable[], struct node *root, char *precode){
char lcode[HCODEMAX], rcode[HCODEMAX];
strcpy(lcode, precode);
strcpy(rcode, precode);
if(root->lbranch != NULL)
mkhtable(htable, root->lbranch, strcat(lcode, "0"));
if(root->rbranch != NULL)
mkhtable(htable, root->rbranch, strcat(rcode, "1"));
if(root->lbranch == NULL && root->rbranch == NULL){
htable[root->data.c] = (char *)malloc(sizeof(char)*HCODEMAX);
strcpy(htable[root->data.c], precode);
}
return 1;
}
int putbits(char *bitstream, FILE *fp){
static int length = 0;
static unsigned char bits = 0;
if(bitstream != NULL){
while(*bitstream != '\0'){
bits = (bits << 1) | (*bitstream - '0');
++length;
if(length == 8){
fwrite(&bits, 1, 1, fp);
length = 0;
bits = 0;
}
++bitstream;
}
} else {
if(length != 0){
bits = bits << 8 - length;
fwrite(&bits, 1, 1, fp);
fwrite(&length, 1, 1, fp);
}
}
return 1;
}
int write_archive(FILE *arch, FILE *plain, char *htable[]){
int c;
while((c = fgetc(plain)) != EOF) putbits(htable[c], arch);
putbits(NULL, arch);
return 1;
}
int compress(char *plainname){
char archname[256];
int i;
FILE *plain, *arch;
struct node *table[256], *root;
char *htable[256];
strcpy(archname, plainname);
strcat(archname, ".huf");
if((plain = fopen(plainname, "r")) == NULL){
perror("cannot open plain");
return -1;
}
if((arch = fopen(archname, "w")) == NULL){
perror("cannot open arch");
return -1;
}
for(i=0;i<256;++i){
table[i] = mknode(NULL, NULL);
table[i]->data.count = 0;
table[i]->data.c = i;
}
while((i=fgetc(plain)) != EOF) ++table[i]->data.count;
print_table(arch, table);
isort_count(table, 256);
for(i=0;table[i]->data.count == 0;++i);
root = mktree(&table[i], 256-i);
for(i=0;i<256;++i) htable[i] = NULL;
mkhtable(htable, root, "");
fseek(plain, 0, SEEK_SET);
i = 0;
fwrite(&i, 2, 1, arch);
write_archive(arch, plain, htable);
fclose(plain);
fclose(arch);
return 1;
}
int main(int argc, char *argv[]){
if(argc>1)
if(!compress(argv[argc-1]))
puts("cannot compress");
return 0;
}