1. 程式人生 > >利用哈夫曼編碼壓縮檔案

利用哈夫曼編碼壓縮檔案

                                            利用哈夫曼編碼壓縮解壓檔案

1.     引言

本文為大一下學期C語言課程的期末大作業,經過修改後釋出。文中要用到的測試檔案1.lst見連結:  https://pan.baidu.com/s/1hs7XoIK  密碼: wpru。              

編譯環境:Ubuntu 16.04

本文主要考核如何以C實現積體電路測試向量檔案的無失真壓縮。在通常的檔案儲存中,無論是二進位制格式的檔案還是文字檔案,幾乎都是等寬的編碼。比如ASCII碼格式的文字檔案,每個字元由一個ASCII碼錶示,寬度為8bit。然而,從磁碟空間利用率的角度看,這並不是一種效率最高的儲存方案。為了理解定長編碼與變長編碼的區別,假設某個檔案純粹由abcdef

6個字元組成,作為定長編碼,至少需要3bit才能表示6個不同的字元,假設每個字元出現的頻率以及兩種編碼方案如下表所示


下面我們計算一下上述兩種編碼方案的總編碼長度。由於6個字元共出現了 100K次,對於定長編碼,每個字元佔用3bit,總共將佔用300Kb的空間;對於變長編碼,總計的編碼長度為:45*1+(13+12+16)*3+(9+5)*4=224Kb,節省了76Kb的空間。這也正是本次大作業的基本的實現原理。上表中的變長編碼有個特點,就是出現越頻繁的字元,用越短的編碼去表示;而且,每個字元的編碼都不是另一個字元編碼的字首,從而簡化了解碼時的難度,因此又被稱為字首編碼。哈夫曼編碼就是這樣一種編碼。本作業要求以哈夫曼編碼的原理,編寫一個可以將給定的檔案進行檔案壓縮,同時可以將壓縮的檔案進行解壓的程式。比如假設編寫的程式為huff

huff -c test.dat –o test.hufftest.dat檔案壓縮為test.huff;類似的,huff -d test.huff -o test.dat將壓縮檔案進行解壓為原始的test.dat。現在問題的主要難點在於如何針對給定的檔案由程式自己統計不同字元出現的頻率並據此構造哈夫曼編碼。

哈夫曼編碼原理

此處以一個例項說明構造哈夫曼編碼的過程。構造哈夫曼編碼的過程可以等效為一個構造哈夫曼編碼樹的過程。以上節中的例子為例,首先需要統計各個字元出現的頻率,如
上面表格中所示。 哈夫曼編碼樹的構造過程如下圖所示。



首先,建立 個葉子節點,如上圖(a)所示,冒號後面是該節點字元出現的頻率;接下來每次從當前結點中尋找兩個出現頻度最低的節點,出現頻率最低的放在左邊,如

(b)f,次低的放右邊,如上圖(b)的 e,然後將這兩個出現頻率最低的節點合併為一個節點,以兩個節點出現的總頻率為名字,如上圖(b)的 14 節點, 14 節點和 與 分別用標記為 0/1的邊連線,這時 和 兩個節點已被刪除並被合併為一個 14 節點,因此總的節點數少了一個;接下來繼續尋找兩個頻率最低的節點,如上圖(c)中的 和 b,二者合併為 25 節點,分別與 和 以邊 0/1 連線;接下來 14 和 節點出現頻率最低,分別為 14 和 16,合併為 30節點;繼續此過程, 25 和 30 節點合併為 55 節點, 55 節點和 合併為最終的 100 節點。至此哈夫曼編碼樹構造完成,如上圖(f)。觀察上圖(f),所有表示真實字元的節點都放置於矩形框中,且都是葉子節點。從根節點到葉子節點的邊的標籤相連就是該葉子節點的編碼,即 a: 0; b: 101; c: 100; d: 111; e: 1101;f: 1100,恰好是前面表中的變長編碼方案。 

2.       基本原理/實現方法

以下是幾個主要部分的編碼解釋,大家可以直接跳過,檔案原始碼貼在最後。 

由試驗簡介,根據哈夫曼編碼的原理,可以實現檔案的壓縮與解壓。壓縮時讀取字元轉化而相應的哈夫曼二進位制編碼,解壓即為其逆操作。接下來對完成的程式進行分析,具體的程式碼見huffman.c。

【程式及演算法分析】

a)        哈夫曼樹建立預備步驟

void initialize(struct node *HuffmanTreeNodeTemp,int count[]) 函式

/***********壓縮檔案的準備工作*************/
void initialize(struct node *HuffmanTreeNodeTemp,int count[])
{
	int i,j;
	struct node temp;
	for (i=0;i<256;i++)                      //給每一個臨時的節點賦值
    {
        HuffmanTreeNodeTemp[i].value=count[i];
        HuffmanTreeNodeTemp[i].ch=(char)i;
    }
    for (i=0;i<256;i++)                      //篩選出現過的字元
    {
        for (j=i+1;j<256;j++)
        {
            if (HuffmanTreeNodeTemp[i].value<HuffmanTreeNodeTemp[j].value)
            {
                temp=HuffmanTreeNodeTemp[i];
                HuffmanTreeNodeTemp[i]=HuffmanTreeNodeTemp[j];
                HuffmanTreeNodeTemp[j]=temp;
            }
        }
    }
}


根據main()函式中統計的256個各個不同字元所重複出現的次數,來創建出256個臨時結點結構。又由於實際哈夫曼樹的建立並不需要考慮未出現過的字元,所以將這256個節點根據出現的次數(value值)進行排序,將有用的部分放在前面,為下一步建立正式哈夫曼結點準備。

b)        哈夫曼樹的建立:void CreatHuffmanTree(int nodecount,int max,struct node *HuffmanTreeNodeTemp,structnode *HuffmanTreeNode)函式

/************構造哈夫曼樹************/
void CreatHuffmanTree(int nodecount,int max,struct node *HuffmanTreeNodeTemp,struct node *HuffmanTreeNode)
{
    int i,j,k,m,n,value;
    int min;
    for (i=0;i<nodecount;i++)
    {
        HuffmanTreeNode[i]=HuffmanTreeNodeTemp[i];
    }
    for (i=0;i<2*nodecount-1;i++)
    {
        HuffmanTreeNode[i].tag=0;
        HuffmanTreeNode[i].parent=0;
    }
    for (i=nodecount;i<2*nodecount-1;i++)
    {
        min=INT_MAX;
        for (j=0;j<i;j++)       //查詢最大值m
        {
            if ((HuffmanTreeNode[j].tag==0) && (HuffmanTreeNode[j].value<=min))
            {
                min=HuffmanTreeNode[j].value;
                m=j;              //m,n分別表示其為最大,次大值為第幾個
            }
        }
        HuffmanTreeNode[m].tag=1;
        min=INT_MAX;
        for (j=0;j<i;j++)        //查詢次大值n
        {
            if ((HuffmanTreeNode[j].tag==0)  &&( HuffmanTreeNode[j].value<=min))
            {
                min=HuffmanTreeNode[j].value;
                n=j;
            }
        }
        HuffmanTreeNode[n].tag=1;      			//被找過的值就標記1,下一次就不會再找了
        HuffmanTreeNode[i].lchild=m;
        HuffmanTreeNode[i].rchild=n;
        HuffmanTreeNode[m].parent=i;
        HuffmanTreeNode[n].parent=i;
        HuffmanTreeNode[i].value=HuffmanTreeNode[m].value+HuffmanTreeNode[n].value;
    }

    //生成哈夫曼編碼
    int index,temp;

    for (i=0;i<nodecount;i++)
    {
        index=255;
        for (j=i;HuffmanTreeNode[j].parent!=0;)
        {
            temp=HuffmanTreeNode[j].parent;
            if (HuffmanTreeNode[temp].lchild==j)
            {
                HuffmanTreeNode[i].hufcodetemp[index]=1;
                index--;
            }
            else if (HuffmanTreeNode[temp].rchild==j)
            {
                HuffmanTreeNode[i].hufcodetemp[index]=0;
                index--;
            }
            j=temp;
        }

        int length=255-index;
        HuffmanTreeNode[i].hufcode=malloc(length*sizeof(int));
        HuffmanTreeNode[i].codelen=length;

        for (k=0;k<length;k++)
        {
            index++;
            HuffmanTreeNode[i].hufcode[k]=HuffmanTreeNode[i].hufcodetemp[index];
        }
    }
}


哈夫曼樹的總節點個數為2*葉子個數-1,葉子個數n即為有效的字元個數,因此可以先分配出2n-1個的結點,其中,前n個為葉子結點,後n-1個為非葉子結點。根據哈夫曼樹的建立原理,在建立哈夫曼樹的過程中,先尋找已存在的結點中value值最大和次大的且tag值為0的結點,將這兩個選擇的結點tag值設定為1,這樣下次搜尋就會跳過它。同時,將其父親結點加入下一次的搜尋範圍之內。記錄相應的lchild、rchild、parent等必要資料值。如此迴圈,直到哈夫曼樹建立完成。

c)        壓縮檔案核心部分:

void compressfile(struct node *HuffmanTreeNode,int wordcount,intnodecount,char FILE1[],char FILE2[]) 函式

/**********對檔案進行壓縮************/
void compressfile(struct node *HuffmanTreeNode,int wordcount,int nodecount,char FILE1[],char FILE2[])
{
	FILE *ptr=fopen(FILE1,"rb");
    FILE *ptw=fopen(FILE2,"wb");
    char readtemp;
    unsigned char codetemp;
    int wcount=0,i,j;
    int length,num;
    codetemp='\0';
    						//寫入哈夫曼編碼
    fwrite(&nodecount,sizeof(int),1,ptw);
    fwrite(&wordcount,sizeof(int),1,ptw);
    for (i=0;i<nodecount;i++)
    {
        fwrite(&(HuffmanTreeNode[i].ch),sizeof(char),1,ptw);
    }
    for (i=nodecount;i<nodecount*2-1;i++)
    {
        fwrite(&(HuffmanTreeNode[i].lchild),sizeof(int),1,ptw);
        fwrite(&(HuffmanTreeNode[i].rchild),sizeof(int),1,ptw);
    }

    while(!feof(ptr))
    {
        readtemp=getc(ptr);
        for (j=0;j<nodecount;j++)                    //找對應的字元
        {
            if (HuffmanTreeNode[j].ch==readtemp)
            {
                num=j;
                break;
            }
        }

        for (i=0;i<HuffmanTreeNode[num].codelen;i++)		//位操作來進行
        {
            codetemp<<=1;
            codetemp|=HuffmanTreeNode[num].hufcode[i];

            wcount++;
            if (wcount==8)						//滿八位以後寫入壓縮檔案
            {
                fwrite(&codetemp,sizeof(unsigned char),1,ptw);
                wcount=0;
                codetemp='\0';
            }
        }
       if (feof(ptr))
            break;
    }
    if (wcount>0)							//處理最後的未滿八位的位元組
    {
        for (i=0;i<8-wcount;i++)
            codetemp<<=1;

        fwrite(&codetemp,sizeof(unsigned char),1,ptw);
    }
    fclose(ptr);
    fclose(ptw);
}

根據建立的哈夫曼樹,不妨假設父親節點的左兒子編碼為1,右兒子為0,這樣就能從根結點開始得到每一個字元的哈夫曼編碼。在進行壓縮的時候,依次讀取測試檔案中的字元,根據該字元的哈夫曼編碼通過位操作寫入壓縮檔案中,在寫入的過程中加入一個計數器wcount,當得到一位值,wcount就加一。當其值滿8時及說明可以向壓縮檔案中寫入一個字元,再將計數器歸零。以此迴圈直到最後。最後可能出現為滿8位的情況,這時要將其右端加0填滿8位再寫入檔案。

需要說明的是,為了解壓時候處理最後一個字元方便,需要將解壓時所需要的相關資訊在壓縮之前寫入檔案。需要寫入的重要資訊有:所含有的有效字元種類,為了讀取時分配大小、確定樹葉節點個數;所含有的字元個數,輸出最後一個字元時使用;葉子結點的字元值,非葉子結點的左兒子、右兒子值,為了從根節點一步一步得到葉子結點是使用。

d)        解壓檔案部分:void extract(charFILE2[],char FILE3[])函式

/************解壓檔案正式程式************/
void extract(char FILE2[],char FILE3[])
{
    int i,j;
    FILE *ptr=fopen(FILE2,"rb");
    int countch;
    int curwordcount=0;
    int wordcount;

    fread(&countch,sizeof(int),1,ptr);                          //從檔案中獲取構建哈夫曼樹的相關資訊
    fread(&wordcount,sizeof(int),1,ptr);
	struct node *extractHuffmanTreeNode = malloc((countch*2-1)*sizeof(struct node));
	for (i=0;i<countch;i++)
    {
        extractHuffmanTreeNode[i].rchild=-1;
        extractHuffmanTreeNode[i].lchild=-1;
    }
    for (i=0;i<countch;i++)
    {
        fread(&(extractHuffmanTreeNode[i].ch),sizeof(char),1,ptr);
    }

    for (i=countch;i<2*countch-1;i++)
    {
        fread(&(extractHuffmanTreeNode[i].lchild),sizeof(int),1,ptr);
        fread(&(extractHuffmanTreeNode[i].rchild),sizeof(int),1,ptr);
    }

    int nextnode=2*countch-2;
    int pose;
    unsigned char chtemp;

    FILE *ptw=fopen(FILE3,"wb");
    while (!feof(ptr))							//讀取檔案進行解碼
    {
        fread(&chtemp,sizeof(unsigned char),1,ptr);
        for (i=0;i<8;i++)
        {
            if (curwordcount>=wordcount)	//讓列印的字元數與原檔案相同,這個是由於用huffman編碼壓縮時,最後不滿8位的都被進行移位處理了,當解壓時無法判斷最後的0是不是移位造成的
                break;
            if (((int)chtemp&128)==128)				//採用掩碼來判斷該位上為0還是1
                pose=1;
            else
                pose=0;
            chtemp<<=1;
            if (nextnode>=countch)     //非葉子節點,繼續
            {
                if (pose==1)
                {
                    nextnode=extractHuffmanTreeNode[nextnode].lchild;
                }
                else
                {
                    nextnode=extractHuffmanTreeNode[nextnode].rchild;

                }
                if (nextnode<countch)      //到達葉子節點,將解壓的字元輸出
                {
                    fwrite(&(extractHuffmanTreeNode[nextnode].ch),sizeof(char),1,ptw);
                    curwordcount++;
                    nextnode=2*countch-2;
                }
            }
        }
    }
    free(extractHuffmanTreeNode);
    fclose(ptw);
    fclose(ptr);
}


解壓檔案即為壓縮檔案的逆過程。首先讀取解壓縮所需的相關資訊,根據其建立哈夫曼樹。進行解壓檔案時,依次讀取檔案的一個字元,一位字元佔八位位元組,可以通過掩碼(mask)來確定某一位上的值。確定一位值就在哈夫曼樹上由跟節點移動一步,得到相應的解碼後的字元值。其中和壓縮時設定的一樣,左兒子為1,右兒子為0。每讀到一個字元就向解壓檔案中寫入該字元,再從頭開始重新求下一個字元。

由於壓縮時最後一位的處理比較特殊,通過移動往後增加了幾個0,而解壓時很難判斷這幾個0是哈夫曼編碼還是移動得到的。因此在之前壓縮時已經寫入了該檔案應包含幾個字元,解壓時即可用計數器統計解壓產生的字元個數,當其值到達要求值時就退出迴圈。

3.       實現過程/實驗資料

a)       函式執行結果分析:


時間:

壓縮檔案(包括寫壓縮檔案&構造哈夫曼樹)用時約25.2s,解壓縮(包括重構哈夫曼樹&寫解壓縮檔案)用時約22.6s。

1.lst為原檔案(即老師提供的檔案,改了一下名字,方便輸入),生成的2.lst為壓縮檔案,3.lst為解壓後的檔案。三個檔案見附件。通過ubuntu下的diff命令操作,比較1.lst和3.lst是否相同,當不返回任何內容時,兩個檔案即為相同的,否則則返回兩檔案不同的部分。由截圖可以看出,該程式解壓得到的檔案和原檔案相同。即說明壓縮與解壓程式可以正確執行。

b)        壓縮率分析:


檢視兩個檔案大小:原檔案1.lst大小為849,646,229位元組,壓縮後的檔案2.lst為123,313,632 位元組,壓縮率為

c)   記憶體洩漏分析:

通過valgrind工具可以分析該程式的記憶體洩漏情況:


可以看到,該程式所有需要釋放的記憶體都已經被釋放,記憶體無洩漏。

****************************下面是原始碼***************************

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

struct node{
    int value;                  //權重
    int parent,lchild,rchild;
    char ch;
    int tag;
    int hufcodetemp[256];
    int *hufcode;
    int codelen;
    };

void CreatHuffmanTree(int nodecount,int max,struct node *huffmanTreeNodeTemp,struct node *huffmanTreeNode);
void initialize(struct node *HuffmanTreeNodeTemp,int count[]);
void compressfile(struct node *HuffmanTreeNode,int wordcount,int nodecount,char FILE1[],char FILE2[]);
void compress(int count[],int wordcount,char FILE1[],char FILE2[]);
void extract(char FILE1[],char FILE2[]);

/************解壓檔案正式程式************/
void extract(char FILE2[],char FILE3[])
{
    int i,j;
    FILE *ptr=fopen(FILE2,"rb");
    int countch;
    int curwordcount=0;
    int wordcount;

    fread(&countch,sizeof(int),1,ptr);                          //從檔案中獲取構建哈夫曼樹的相關資訊
    fread(&wordcount,sizeof(int),1,ptr);
	struct node *extractHuffmanTreeNode = malloc((countch*2-1)*sizeof(struct node));
	for (i=0;i<countch;i++)
    {
        extractHuffmanTreeNode[i].rchild=-1;
        extractHuffmanTreeNode[i].lchild=-1;
    }
    for (i=0;i<countch;i++)
    {
        fread(&(extractHuffmanTreeNode[i].ch),sizeof(char),1,ptr);
    }

    for (i=countch;i<2*countch-1;i++)
    {
        fread(&(extractHuffmanTreeNode[i].lchild),sizeof(int),1,ptr);
        fread(&(extractHuffmanTreeNode[i].rchild),sizeof(int),1,ptr);
    }

    int nextnode=2*countch-2;
    int pose;
    unsigned char chtemp;

    FILE *ptw=fopen(FILE3,"wb");
    while (!feof(ptr))							//讀取檔案進行解碼
    {
        fread(&chtemp,sizeof(unsigned char),1,ptr);
        for (i=0;i<8;i++)
        {
            if (curwordcount>=wordcount)	//讓列印的字元數與原檔案相同,這個是由於用huffman編碼壓縮時,最後不滿8位的都被進行移位處理了,當解壓時無法判斷最後的0是不是移位造成的
                break;
            if (((int)chtemp&128)==128)				//採用掩碼來判斷該位上為0還是1
                pose=1;
            else
                pose=0;
            chtemp<<=1;
            if (nextnode>=countch)     //非葉子節點,繼續
            {
                if (pose==1)
                {
                    nextnode=extractHuffmanTreeNode[nextnode].lchild;
                }
                else
                {
                    nextnode=extractHuffmanTreeNode[nextnode].rchild;

                }
                if (nextnode<countch)      //到達葉子節點,將解壓的字元輸出
                {
                    fwrite(&(extractHuffmanTreeNode[nextnode].ch),sizeof(char),1,ptw);
                    curwordcount++;
                    nextnode=2*countch-2;
                }
            }
        }
    }
    free(extractHuffmanTreeNode);
    fclose(ptw);
    fclose(ptr);
}

/************構造哈夫曼樹************/
void CreatHuffmanTree(int nodecount,int max,struct node *HuffmanTreeNodeTemp,struct node *HuffmanTreeNode)
{
    int i,j,k,m,n,value;
    int min;
    for (i=0;i<nodecount;i++)
    {
        HuffmanTreeNode[i]=HuffmanTreeNodeTemp[i];
    }
    for (i=0;i<2*nodecount-1;i++)
    {
        HuffmanTreeNode[i].tag=0;
        HuffmanTreeNode[i].parent=0;
    }
    for (i=nodecount;i<2*nodecount-1;i++)
    {
        min=INT_MAX;
        for (j=0;j<i;j++)       //查詢最大值m
        {
            if ((HuffmanTreeNode[j].tag==0) && (HuffmanTreeNode[j].value<=min))
            {
                min=HuffmanTreeNode[j].value;
                m=j;              //m,n分別表示其為最大,次大值為第幾個
            }
        }
        HuffmanTreeNode[m].tag=1;
        min=INT_MAX;
        for (j=0;j<i;j++)        //查詢次大值n
        {
            if ((HuffmanTreeNode[j].tag==0)  &&( HuffmanTreeNode[j].value<=min))
            {
                min=HuffmanTreeNode[j].value;
                n=j;
            }
        }
        HuffmanTreeNode[n].tag=1;      			//被找過的值就標記1,下一次就不會再找了
        HuffmanTreeNode[i].lchild=m;
        HuffmanTreeNode[i].rchild=n;
        HuffmanTreeNode[m].parent=i;
        HuffmanTreeNode[n].parent=i;
        HuffmanTreeNode[i].value=HuffmanTreeNode[m].value+HuffmanTreeNode[n].value;
    }

    //生成哈夫曼編碼
    int index,temp;

    for (i=0;i<nodecount;i++)
    {
        index=255;
        for (j=i;HuffmanTreeNode[j].parent!=0;)
        {
            temp=HuffmanTreeNode[j].parent;
            if (HuffmanTreeNode[temp].lchild==j)
            {
                HuffmanTreeNode[i].hufcodetemp[index]=1;
                index--;
            }
            else if (HuffmanTreeNode[temp].rchild==j)
            {
                HuffmanTreeNode[i].hufcodetemp[index]=0;
                index--;
            }
            j=temp;
        }

        int length=255-index;
        HuffmanTreeNode[i].hufcode=malloc(length*sizeof(int));
        HuffmanTreeNode[i].codelen=length;

        for (k=0;k<length;k++)
        {
            index++;
            HuffmanTreeNode[i].hufcode[k]=HuffmanTreeNode[i].hufcodetemp[index];
        }
    }
}

/***********壓縮檔案的準備工作*************/
void initialize(struct node *HuffmanTreeNodeTemp,int count[])
{
	int i,j;
	struct node temp;
	for (i=0;i<256;i++)                      //給每一個臨時的節點賦值
    {
        HuffmanTreeNodeTemp[i].value=count[i];
        HuffmanTreeNodeTemp[i].ch=(char)i;
    }
    for (i=0;i<256;i++)                      //篩選出現過的字元
    {
        for (j=i+1;j<256;j++)
        {
            if (HuffmanTreeNodeTemp[i].value<HuffmanTreeNodeTemp[j].value)
            {
                temp=HuffmanTreeNodeTemp[i];
                HuffmanTreeNodeTemp[i]=HuffmanTreeNodeTemp[j];
                HuffmanTreeNodeTemp[j]=temp;
            }
        }
    }
}

/**********對檔案進行壓縮************/
void compressfile(struct node *HuffmanTreeNode,int wordcount,int nodecount,char FILE1[],char FILE2[])
{
	FILE *ptr=fopen(FILE1,"rb");
    FILE *ptw=fopen(FILE2,"wb");
    char readtemp;
    unsigned char codetemp;
    int wcount=0,i,j;
    int length,num;
    codetemp='\0';
    						//寫入哈夫曼編碼
    fwrite(&nodecount,sizeof(int),1,ptw);
    fwrite(&wordcount,sizeof(int),1,ptw);
    for (i=0;i<nodecount;i++)
    {
        fwrite(&(HuffmanTreeNode[i].ch),sizeof(char),1,ptw);
    }
    for (i=nodecount;i<nodecount*2-1;i++)
    {
        fwrite(&(HuffmanTreeNode[i].lchild),sizeof(int),1,ptw);
        fwrite(&(HuffmanTreeNode[i].rchild),sizeof(int),1,ptw);
    }

    while(!feof(ptr))
    {
        readtemp=getc(ptr);
        for (j=0;j<nodecount;j++)                    //找對應的字元
        {
            if (HuffmanTreeNode[j].ch==readtemp)
            {
                num=j;
                break;
            }
        }

        for (i=0;i<HuffmanTreeNode[num].codelen;i++)		//位操作來進行
        {
            codetemp<<=1;
            codetemp|=HuffmanTreeNode[num].hufcode[i];

            wcount++;
            if (wcount==8)						//滿八位以後寫入壓縮檔案
            {
                fwrite(&codetemp,sizeof(unsigned char),1,ptw);
                wcount=0;
                codetemp='\0';
            }
        }
       if (feof(ptr))
            break;
    }
    if (wcount>0)							//處理最後的未滿八位的位元組
    {
        for (i=0;i<8-wcount;i++)
            codetemp<<=1;

        fwrite(&codetemp,sizeof(unsigned char),1,ptw);
    }
    fclose(ptr);
    fclose(ptw);
}

/************壓縮檔案的正式程式***********/
void compress(int count[],int wordcount,char FILE1[],char FILE2[])
{
    int i,j,nodecount=0;
    struct node *HuffmanTreeNodeTemp=malloc(256*sizeof(struct node));      										//先給每一個可能的字元都初始化哈夫曼節點

    initialize(HuffmanTreeNodeTemp, count);		//處理這些節點,排序

    int max=HuffmanTreeNodeTemp[0].value;		//重新建立一棵包含有效的節點的哈夫曼樹

    for (i=0;i<256;i++)
    {
        if (HuffmanTreeNodeTemp[i].value!=0)
                nodecount++;
        if (HuffmanTreeNodeTemp[i].value>max)
                max=HuffmanTreeNodeTemp[i].value;
    }
    struct node *HuffmanTreeNode=malloc((2*nodecount-1)*sizeof(struct node));
    for (i=0;i<nodecount;i++)
    {
        HuffmanTreeNode[i]=HuffmanTreeNodeTemp[i];
    }

    CreatHuffmanTree(nodecount,max,HuffmanTreeNodeTemp,HuffmanTreeNode);

    for (i=0;i<nodecount;i++)				//列印哈夫曼編碼
    {
        printf("[%d]",i);
        printf(" %c ",HuffmanTreeNode[i].ch);
        for (j=0;j<HuffmanTreeNode[i].codelen;j++)
            {
                printf("%d",HuffmanTreeNode[i].hufcode[j]);
            }
        printf("\n");
    }

    compressfile(HuffmanTreeNode,wordcount,nodecount,FILE1,FILE2);   //壓縮檔案
    for (i=0;i<nodecount;i++)
    {
    	free(HuffmanTreeNode[i].hufcode);
    }
        free(HuffmanTreeNode);
        free(HuffmanTreeNodeTemp);
}

/*************主函式**************/
int main()
{
    int i;
    char ch;
    char FILE1[100],FILE2[100],FILE3[100];
    int wordcount=0;      //統計檔案中的字元個數
    int count[256]={0};

    FILE *fp=fopen(FILE1,"rb");
    printf("Please enter the file name.\n");
    scanf("%s",FILE1);
    while ((fp=fopen(FILE1,"r"))==NULL)
    {
        printf("Sorry, can't open it.\nPlease enter the file name again.\n");
        scanf("%s",FILE1);
    }
    printf("Please enter the compressed file name.\n");
    scanf("%s",FILE2);
    printf("Please enter the extracted file name.\n");
    scanf("%s",FILE3);

    while((ch=getc(fp))!=EOF){
                count[(int)ch]+=1;
                wordcount++;
    }
    fclose(fp);
    int a,b;
    printf("compressing now\n");
    a=clock();
    compress(count,wordcount,FILE1,FILE2);
    b=clock();
    printf("\ncompress use time:   %dus\n",b-a);
    printf("extracting now\n");
    a=clock();
    extract(FILE2,FILE3);
    b=clock();
    printf("\nextract use time:   %dus\n",b-a);
    return 0;
}