1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 //////////////////////////////////////////////////////////////////////////////
5 /*定义赫夫曼树结点的结构体变量,存放结点的权值、字符、双亲、坐孩子和右孩子*/
6 typedef struct{
7 int weight;//权值
8 char ch; //增加一个域用于存放该节点的字符
9 int parent, lchild, rchild;
10 }HTNode, *HuffmanTree;
11 typedef char **HuffmanCode; //指向赫夫曼编码的指针
12 //////////////////////////////////////////////////////////////////////////////
13 /*本程序用到的函数原型*/
14 void welcome(); //打印操作选择界面
15 void HuffmanCoding(HuffmanTree &, char *, int *, int);//建立赫夫曼树的算法
16 void select(HuffmanTree HT, int j, int *s1, int *s2); //从目前已建好的赫夫曼树中选择parent为0且weight最小的两个结点
17 void Init(); //输入n个字符及其对应的权值,根据权值建立哈夫曼树
18 void Coding(); //编码
19 void Decoding(); //译码
20 void Print_code(); //打印译码好的代码文件
21 void Print_tree(); //以凹凸表形式打印哈夫曼树
22 int Read_tree(HuffmanTree &HT); //从文件中读入赫夫曼树
23 void find(HuffmanTree &HT, char *code, char *text, int i, int m);//译码时根据01字符串寻找相应叶子节点的递归算法
24 void Convert_tree(unsigned char T[100][100], int s, int *i, int j);//将内存中的赫夫曼树转换成凹凸表形式的赫夫曼树
25 HuffmanTree HT; //全局变量,指向存放赫夫曼树的存储空间
26 int n = 0; //全局变量,存放赫夫曼树叶子结点的数目
27 int main()
28 {
29 char select;
30 while (1)
31 {
32 welcome();
33 scanf("%c", &select);
34 switch (select)
35 {
36 case 'i':
37 case 'I':Init(); break;
38 case 'c':
39 case 'C':Coding(); break;
40 case 'd':
41 case 'D':Decoding(); break;
42 case 'p':
43 case 'P':Print_code(); break;
44 case 't':
45 case 'T':Print_tree(); break;
46 case 'e':
47 case 'E':exit(1);
48 default:printf("Input error!\n");
49 }
50 getchar();
51 }
52 return 0;
53 }
54 void welcome() //打印操作选择界面
55 {
56 printf("*-----------------------------------------------------*\n");
57 printf("| What do you want to do? |\n");
58 printf("|-----------------------------------------------------|\n");
59 printf("| |\n");
60 printf("| I--------------------------Init the Huffman tree. |\n");
61 printf("| C--------------------------Code your file. |\n");
62 printf("| D--------------------------Decode the code. |\n");
63 printf("| P--------------------------Print the codefile. |\n");
64 printf("| T--------------------------Print the Huffman tree. |\n");
65 printf("| |\n");
66 printf("*-----------------------------------------------------*\n");
67 }
68 //////////////////////////////////////////////////////////////////////////////////////
69 /*初始化函数,输入n个字符及其对应的权值,根据权值建立哈夫曼树,并将其存于文件hfmtree中*/
70 void Init()
71 {
72 FILE *fp;
73 int i, n, w[52]; //w数组存放n个字符的权值
74 char character[52]; //存放n个字符
75 printf("\n输入字符个数 n:");
76 scanf("%d", &n); //输入字符集大小
77 printf("输入%d个字符及其对应的权值:\n", n);
78 for (i = 0; i < n; i++)
79 {
80 char b = getchar();//?
81 scanf("%c", &character[i]);
82 scanf("%d", &w[i]); //输入n个字符和对应的权值
83 }
84 HuffmanCoding(HT, character, w, n); //建立赫夫曼树
85 if ((fp = fopen("hfmtree.txt", "w")) == NULL)
86 printf("Open file hfmtree.txt error!\n");
87 for (i = 1; i <= 2 * n - 1; i++)
88 {
89 if (fwrite(&HT[i], sizeof(HTNode), 1, fp) != 1) //将建立的赫夫曼树存入文件hfmtree.txt中
90 printf("File write error!\n");
91 }
92 printf("\n建立赫夫曼树成功,已将其存于文件hfmtree.txt中\n");
93 fclose(fp);
94 }
95 ///////////////////////////////////////////////////////////////////////////////////
96 //////建立赫夫曼树的算法///////////////////////////////////////////////////////////
97 void HuffmanCoding(HuffmanTree &HT, char *character, int *w, int n)
98 {
99 int m, i, s1, s2;
100 HuffmanTree p;
101 if (n <= 1)
102 return;
103 m = 2 * n - 1;
104 HT = (HuffmanTree)malloc((m + 1)*sizeof(HTNode));
105 for (p = HT + 1, i = 1; i <= n; ++i, ++p, ++character, ++w)
106 {
107 p->ch = *character;
108 p->weight = *w;
109 p->parent = 0;
110 p->lchild = 0;
111 p->rchild = 0;
112 }
113 for (; i <= m; ++i, ++p)
114 {
115 p->ch = 0;
116 p->weight = 0;
117 p->parent = 0;
118 p->lchild = 0;
119 p->rchild = 0;
120 }
121 for (i = n + 1; i <= m; ++i)
122 {
123 select(HT, i - 1, &s1, &s2);
124 HT[s1].parent = i;
125 HT[s2].parent = i;
126 HT[i].lchild = s1;
127 HT[i].rchild = s2;
128 HT[i].weight = HT[s1].weight + HT[s2].weight;
129 }
130 }
131 ///////////////////////////////////////////////////////////////////////////////
132 /*从HT[1]到HT[j]中选择parent为0且weight最小的两个结点,用s1和s2返回其序号*/
133 void select(HuffmanTree HT, int j, int *s1, int *s2)
134 {
135 int i;
136 //找weight最小的结点
137 for (i = 1; i <= j; i++)
138 if (HT[i].parent == 0)
139 {
140 *s1 = i;
141 break;
142 }
143 for (; i <= j; i++)
144 if ((HT[i].parent == 0) && (HT[i].weight < HT[*s1].weight))
145 *s1 = i;
146 HT[*s1].parent = 1;
147 //找weight次小的结点
148 for (i = 1; i <= j; i++)
149 if (HT[i].parent == 0)
150 {
151 *s2 = i; break;
152 }
153 for (; i <= j; i++)
154 if ((HT[i].parent == 0) && (i != *s1) && (HT[i].weight < HT[*s2].weight))
155 *s2 = i;
156 }
157 ///////////////////////////////////////////////////////////////////////////////
158 /*对文件tobetrans中的正文进行编码,然后将结果存入文件codefile中*/
159 void Coding()
160 {
161 FILE *fp, *fw;
162 int i, f, c, start;
163 char *cd;
164 HuffmanCode HC;
165 if (n == 0)
166 n = Read_tree(HT);//从文件hfmtree.txt中读入赫夫曼树,返回叶子结点数
167 /////以下程序段求赫夫曼树中各叶子节点的字符对应的的编码,并存于HC指向的空间中
168 {
169 HC = (HuffmanCode)malloc((n + 1)*sizeof(char*));
170 cd = (char *)malloc(n*sizeof(char));
171 cd[n - 1] = '\0';
172 for (i = 1; i <= n; ++i)
173 {
174 start = n - 1;
175 for (c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent)
176 if (HT[f].lchild == c)
177 cd[--start] = '0';
178 else cd[--start] = '1';
179 HC[i] = (char *)malloc((n - start)*sizeof(char));
180 strcpy(HC[i], &cd[start]);
181 }
182 free(cd);
183 }
184 /////////////////////////////////////////////////////////////////////////////////////
185 if ((fp = fopen("tobetrans.txt", "rb")) == NULL)
186 printf("Open file tobetrans.txt error!\n");
187 if ((fw = fopen("codefile.txt", "wb+")) == NULL)
188 printf("Open file codefile.txt error!\n");
189 char temp;
190 fscanf(fp, "%c", &temp); //从文件读入第一个字符
191 while (!feof(fp))
192 {
193 for (i = 1; i <= n; i++)
194 if (HT[i].ch == temp) break; //在赫夫曼树中查找字符所在的位置
195 for (int r = 0; HC[i][r] != '\0'; r++) //将字符对应的编码存入文件
196 fputc(HC[i][r], fw);
197 fscanf(fp, "%c", &temp); //从文件读入下一个字符
198 }
199 fclose(fw);
200 fclose(fp);
201 printf("\n对文件hfmtree.txt编码成功,结果已存入codefile.txt中。\n\n");
202 }
203
204 /////////////////////////////////////////////////////////////////////////
205 /*将文件codefile中的代码进行译码,结果存入文件textfile中*/
206 void Decoding()
207 {
208 FILE *fp, *fw;
209 int m, i;
210 char *code, *text, *p;
211 if (n == 0)
212 n = Read_tree(HT);//从文件hfmtree.txt中读入赫夫曼树,返回叶子结点数
213 if ((fp = fopen("codefile.txt", "rb")) == NULL)
214 printf("Open file codefile.txt error!\n");
215 if ((fw = fopen("textfile.txt", "wb+")) == NULL)
216 printf("Open file textfile.txt error!\n");
217 code = (char *)malloc(sizeof(char));
218 fscanf(fp, "%c", code); //从文件读入一个字符
219 for (i = 1; !feof(fp); i++)
220 {
221 code = (char *)realloc(code, (i + 1)*sizeof(char)); //增加空间
222 fscanf(fp, "%c", &code[i]); //从文件读入下一个字符
223 }
224 code[i - 1] = '\0';
225 /////////到此codefile.txt文件中的字符已全部读入,存放在code数组中
226 text = (char *)malloc(100 * sizeof(char));
227 p = text;
228 m = 2 * n - 1;
229 if (*code == '0')
230 find(HT, code, text, HT[m].lchild, m); //从根节点的左子树去找
231 else
232 find(HT, code, text, HT[m].rchild, m); //从根节点的右子树去找
233 for (i = 0; p[i] != '\0'; i++) //把译码好的字符存入文件textfile.txt中
234 fputc(p[i], fw);
235 fclose(fp);
236 fclose(fw);
237 printf("\n对codefile.txt文件译码成功,结果已存入textfile.txt文件。\n\n");
238 }
239 //////////////////////////////////////////////////////////////////////////////////////////////////////
240 /*将文件codefi1e以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件codeprint中。*/
241 void Print_code()
242 {
243 FILE *fp, *fw;
244 char temp;
245 int i;
246 if ((fp = fopen("codefile.txt", "rb")) == NULL)
247 printf("Open file codefile.txt error!\n");
248 if ((fw = fopen("codeprint.txt", "wb+")) == NULL)
249 printf("Open file codeprint.txt error!\n");
250 printf("\n文件codefi1e以紧凑格式显示如下:\n");
251 fscanf(fp, "%c", &temp); //从文件读入一个字符
252 for (i = 1; !feof(fp); i++)
253 {
254 printf("%c", temp);
255 if (i % 50 == 0) printf("\n");
256 fputc(temp, fw); //将该字符存入文件codeprint.txt中
257 fscanf(fp, "%c", &temp); //从文件读入一个字符
258 }
259 printf("\n\n此字符形式的编码已写入文件codeprint.txt中.\n\n");
260 fclose(fp);
261 fclose(fw);
262 }
263 //////////////////////////////////////////////////////////////////////////////////////////////////
264 /*将已在内存中的哈夫曼树以凹凸表形式显示在屏幕上,同时将此字符形式的哈夫曼树写入文件treeprint中。*/
265 void Print_tree()
266 {
267 unsigned char T[100][100];
268 int i, j, m = 0;
269 FILE *fp;
270 if (n == 0)
271 n = Read_tree(HT);//从文件hfmtree.txt中读入赫夫曼树,返回叶子结点数
272
273 Convert_tree(T, 0, &m, 2 * n - 1); //将内存中的赫夫曼树转换成凹凸表形式的树,存于数组T中
274 if ((fp = fopen("treeprint.txt", "wb+")) == NULL)
275 printf("Open file treeprint.txt error!\n");
276 printf("\n以凹凸表形式打印已建好的赫夫曼树:\n");
277 for (i = 1; i <= 2 * n - 1; i++)
278 {
279 for (j = 0; T[i][j] != 0; j++)
280 {
281 if (T[i][j] == ' ') { printf(" "); fputc(T[i][j], fp); }
282 else
283 {
284 printf("%d", T[i][j]); fprintf(fp, "%d\n", T[i][j]);
285 }
286 }
287 printf("\n");
288 }
289 fclose(fp);
290 printf("\n此字符形式的哈夫曼树已写入文件treeprint.txt中.\n\n");
291
292 }
293 //////////////////////////////////////////////////////////////////////////////////
294 /*从文件hfmtree.txt中读入赫夫曼树,返回叶子节点数*/
295 int Read_tree(HuffmanTree &HT)
296 {
297 FILE *fp;
298 int i, n;
299 HT = (HuffmanTree)malloc(sizeof(HTNode));
300 if ((fp = fopen("hfmtree.txt", "r")) == NULL)
301 printf("Open file hfmtree.txt error!\n");
302 for (i = 1; !feof(fp); i++)
303 {
304 HT = (HuffmanTree)realloc(HT, (i + 1)*sizeof(HTNode)); //增加空间
305 fread(&HT[i], sizeof(HTNode), 1, fp); //读入一个节点信息
306 }
307 fclose(fp);
308 n = (i - 1) / 2;
309 return n;
310 }
311 ////////////////////////////////////////////////////////////////
312 /*译码时根据01字符串寻找相应叶子节点的递归算法*/
313 void find(HuffmanTree &HT, char *code, char *text, int i, int m)
314 {
315
316 if (*code != '\0') //若译码未结束
317 {
318 code++;
319 if (HT[i].lchild == 0 && HT[i].rchild == 0) //若找到叶子节点
320 {
321 *text = HT[i].ch; //将叶子节点的字符存入text中
322 text++;
323 if ((*code == '0'))
324 find(HT, code, text, HT[m].lchild, m); //继续从根节点的左子树找
325 else
326 find(HT, code, text, HT[m].rchild, m); //继续从根节点的右子树找
327 }
328 else //如果不是叶子节点
329 if (*code == '0')
330 find(HT, code, text, HT[i].lchild, m); //从该节点的左子树去找
331 else
332 find(HT, code, text, HT[i].rchild, m); //从该节点的右子树去找
333
334 }
335 else
336 *text = '\0'; //译码结束
337 }
338 ////////////////////////////////////////////////////////////////////////
339 /*将内存中的赫夫曼树转换成凹凸表形式的赫夫曼树*/
340 void Convert_tree(unsigned char T[100][100], int s, int *i, int j)
341 {
342 int k, l;
343 l = ++(*i);
344 for (k = 0; k < s; k++)
345 T[l][k] = ' ';
346 T[l][k] = HT[j].weight;
347 if (HT[j].lchild)
348 Convert_tree(T, s + 1, i, HT[j].lchild);
349 if (HT[j].rchild)
350 Convert_tree(T, s + 1, i, HT[j].rchild);
351 T[l][++k] = '\0';
352 }