基于Huffman编码的文本文件压缩程序(一)

 

                                                基于Huffman编码的文本文件压缩程序(一)
            从8.5号以来,我一直在写一个基于Huffman编码的文本文件压缩程序,但是我发现困难比想像中的要大得多,Huffman编码是那么简洁明了的一种思想,那么直观的实现算法,对于大多数的学过数据结构的人,你给他一些字符及其对应的频率值,他几下就画出一棵树,然后写出编码来了,但是,真正的要把那个过程用代码表达出来,却不是那么容易,我想应该是因为我的编程经验还不丰富,设计能力还不够,尽管我已经知道并实践了许多树程序生成,遍历的实现,但这次还是没能很快完成程序,我还得到一个教训就是有难题不能拖,一拖下来,就不想去解决了。

            序的功能就是对给定的一个文本文件(暂时只能处理ASCII码,因为我不知道UNICODE怎么个具体对应法,再说C语言本身并不提供对UNICODE的支持,首先没有一个直接的数据存储类型......),做出该文件的压缩副本。
          程序大概的流程为:
(1):读该文件,统计该文件中出现了128个字符中的哪些字符,然后计算出每个字符的出现次数,以此代替Huffman编码时的那个频率值;这一步中用了一个128大小的数组记录对应的字符的出现次数,这种索引方法很不错。
(2):根据该次数值构造Huffman树;这一步中首先要准备遍历上一步生成的那个数组,每读到一个出现次数大于0的字符,就把它的值以及对应的次数做为一组数据存放在一个结构中:
typedef struct htnode    //huffman tree node
{
   int val;
   int times;
   htnode * next;
   htnode * left;
   htnode * right;
}htnode;
为了后面生成树方便,所以在这步中我是把每次读到的字符按从小到大的顺序构成了一个linkedlist,其中next指针就起这个作用。遍历完数组以后,所有需要编码的字符就在这个linkedlist中了,然后我取该linkedlist中的前两个元素,用一个新根结点把它们约束起来,新根结点的val即次数值等于其两个子结点的val值的和,然后我删除处理过的两个字符结点,把新生成的结点按序再次插入原linkedlist中去,这样重复做直到linkedlist中只剩下一个结点为止。此时该结点下面其实就生成了一棵完整的Huffman树。
(3):遍历上一步构成的树,得到每个字符对应的Huffman编码;
这个遍历,用人脑做简直简单的要命,可是实现的时候,我想了很多方法都不太好,因为我要在遍历的时候要标记每找到的一个叶结点(即字符而非内部结点)的路径,这样才能得到编码。递归的非递归的都试了,还是没成功,最后从另一个程序中看到了一种方法,总算实现了。那些弯路就不在这记述了。
(4):根据得到的编码重新生成一个文件,原来的每个字符要用8位存储,而现在按可变长编码存储,应该会减小文件大小,生成这个文件,我们必须把得到的编码按2进制写入文件,可是二进制写文件又不能按bit写,因为数据存取的最小单位是字节,所以我必须用位操作把每8个编码一组的所有编码做成unsigned int 型,然后写入文件。(呵呵,这一步暂未实现)

在写代码的过程中我碰到的问题有:
(1):调试时如果单步执行遇到系统库函数调用,则要用F10跳过该调用,如果此时仍用F11的话,就会进入该函数的源代码处,而该函数是已经编译好了形成的库,没有源文件提供的,当你点击取消就进入了汇编代码库文件,那样就没办法跟踪了,再说那是那个库函数的实现,你看了也多半没用,所以碰到它们时用F10选择不进入该函数就可以了。(因为这个问题,想了半天,又问了人才知道,几乎耽搁了一(shit,就在此时我的第二块键盘打不进字了,真几巴操......又得买键盘))

(2):写二进制文件没有直接按位写的方法。

(3):有点滥用链表的嫌疑!

下面是一份样本编码以及程序的源代码 :
 

呵呵,对齐出了点问题。

 

#include<stdio.h>
#include<stdlib.h>

#define INTERNAL -1                   //flag for intenal node

int  chars[128];                               //only deal with ASCII 128 chars
char code_tmp[100];                    //stores codes for traverse
int  q=0;                                           //trace the different length of codes when traversing 

typedef struct htnode                   //huffman tree node
{
 int val;
 int times;
 htnode * next;
 htnode * parent;
 htnode * left;
 htnode * right;
}htnode;
htnode * htroot;                             //root ,head node of the tree
htnode * currnode;                       //active available
htnode * tmp;

void linkinsert(htnode * newnode);   //insert a node which is new or is combined by two existed nodes to the linkedlist

typedef struct codenode    //to store the code ,one byte store one bit,but i don't know how to change this waste
{
 char code;
 codenode * next;
}codenode;
codenode * codehead;
codenode * codecurrent;
void codeinsert(char code);
void freecodelist(codenode * cnode);

typedef struct charnode                 //all this type nodes made of the char and code list
{
 char val;
 charnode * nextchar;
 codenode * nextnode;
}charnode;
charnode * charhead;
charnode * charcurrent;
void charinsert(char value);

void init();
int  getweight(char * argv1);
void buildhftree();
void huffmancoding(htnode * subroot);
void delehftree();
int  generafile();

int main(int argc,char * argv[])
{
 if(argc!=2)        //checking
 {
  printf("Arguments error!/n");
  return -1;
 }
 
 init();         //init global variables
 if((getweight(argv[1]))==-1)                           //open the file and get weights of each char
 {
  return -1;
 }
 buildhftree();       
 huffmancoding(htroot->next); 
 delehftree();         
 generafile();                                                   //generate compressed file
 return 0;
}


void init()
{
 htroot=(htnode *)malloc(sizeof(htnode));
 htroot->val=INTERNAL;
 htroot->times=0;
 htroot->next=NULL;
 htroot->parent=NULL;
 htroot->left=NULL;
 htroot->right=NULL;

 codehead=(codenode *)malloc(sizeof(codenode));
 codehead->code=0;
 codehead->next=NULL;
 codecurrent=codehead;

 charhead=(charnode *)malloc(sizeof(charnode));
 charhead->nextchar=NULL;
 charhead->nextnode=NULL;
 charhead->val=0;
 charcurrent=charhead;
}

int getweight(char * argv1)
{
 FILE * fp;
 if((fp=fopen(argv1,"r"))==NULL)//open file
 {
  printf("Open file failed!");   
  return -1;
 }
 int current=0;
 while((current=getc(fp))!=EOF)   //read and analyse the file
 {
  if(current>=0 && current <128)
  {
   chars[current]++;
  }
  else
  {
   printf("Read file error!");
   return -1;
  }
 }
 fclose(fp);        //close file
 return 0;
}

void buildhftree()
{
 //build base linked list of all nodes which appears at least one times, from small to lardge
 for(int i=0;i<128;i++)
 {
  if(chars[i]!=0)
  {   
   tmp=(htnode *)malloc(sizeof(htnode));   
   tmp->times=chars[i];
   tmp->val=i;
   tmp->next=NULL;
   tmp->parent=NULL;
   tmp->left=NULL;
   tmp->right=NULL;
   linkinsert(tmp);
   tmp=NULL;
  }
 }
 //combine first two nodes circularly to build Huffman tree
 while(htroot->next->next!=NULL)   //here suppose the file has more than 1 char at least
 {
  htnode * combined=(htnode *)malloc(sizeof(htnode));
  combined->val=INTERNAL;
  combined->times=(htroot->next->times)+(htroot->next->next->times);
  combined->left=htroot->next;
  combined->right=htroot->next->next;
  combined->next=NULL;
  combined->parent=NULL;
  htroot->next->parent=combined;
  htroot->next->next->parent=combined;
  htroot->next=htroot->next->next->next;  
  linkinsert(combined);    //then insert the new node
 }
}

void linkinsert(htnode * newnode)
{
 htnode * curr;
 curr=htroot;
 while(curr->next!=NULL)
 {
  if((newnode->times)<=(curr->next->times))
  {
   newnode->next=curr->next;
   newnode->parent=curr;
   curr->next=newnode;
   return;     //only a flag for after judgement
  }
  else
  {
   curr=curr->next;
  }
 }
 curr->next=newnode;
 newnode->parent=curr;
}

void huffmancoding(htnode * subroot)
{
 if(subroot->left==NULL && subroot->right==NULL)
 {
  charinsert(subroot->val);
  return;
 }
 code_tmp[q++]='0';
 huffmancoding(subroot->left);
 q--; 
 code_tmp[q++]='1';
 huffmancoding(subroot->right);
 q--;
}

void codeinsert(char code)
{
 codenode * codetmp=(codenode *)malloc(sizeof(codenode));
 codetmp->code=code;
 codetmp->next=NULL;
 codecurrent->next=codetmp;
 codecurrent=codecurrent->next;
}

void freecodelist(codenode * cnode)
{
 if(cnode==NULL) return;
 freecodelist(cnode->next);
 free(cnode);
 return;
}


void charinsert(char value)
{
 for(int i=0;i<q;i++)    //generate code list from the chars array
 {
  if(code_tmp[i]=='0')
  {
   codeinsert('0');
  }
  else
  {
   codeinsert('1');
  }
 }
 charnode * chartmp=(charnode *)malloc(sizeof(charnode));
 chartmp->nextnode=codehead->next;
 chartmp->nextchar=NULL;
 chartmp->val=value;
 charcurrent->nextchar=chartmp;
 charcurrent=charcurrent->nextchar;

 codehead->next=NULL;    //clear the codehaed and codecurrent for next time of generate
 codecurrent=codehead;
}

void delehftree()
{

}

int generafile()
{
 int i=0;
 FILE * fp;
 if((fp=fopen("4.ar","w"))==NULL)
 {
  printf("Create compressed file failed!");
  return -1;
 }
 charnode * char_tmp=charhead->nextchar;
 while(char_tmp!=NULL)
 {
  printf("%c/t",char_tmp->val);
  codenode * code_tmp=char_tmp->nextnode;
  while(code_tmp!=NULL)
  {
   printf("%c",code_tmp->code);
   fputc(code_tmp->code,fp);
   code_tmp=code_tmp->next;
  }
  if(i%2==0)
  {
   printf("/n");
  }
  else
  {
   printf("/t/t/t/t");
  }
  i++;
  char_tmp=char_tmp->nextchar;
 }
 fclose(fp);
 return 0;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值