使用c语言编程实现以下要求要求: 1)从终端读入字符集大小为n,及n个字符和n个权值,建立哈夫曼树,进行编码并且输出,并将它存于文件hfmtree中。 2)利用已建好的哈夫曼编码文件hfmtree,对存储于文件中的某正文进行编码。输出字符正文,再输出该文的二进制码。 3)对2)的编码进行译码,译码结果存储到文件中,并与编码前的文件对比。
时间: 2024-03-24 15:38:35 浏览: 89
好的,这是一个较为复杂的问题,需要通过多个步骤来实现。我会尽量详细地解释每个步骤的实现方法。
首先,我们需要了解哈夫曼编码的原理。哈夫曼编码是一种用于无损数据压缩的算法,它将出现频率高的字符用较短的编码表示,出现频率低的字符用较长的编码表示,从而实现对数据的压缩。
以下是实现该问题的步骤:
1. 从终端读入字符集大小为n,及n个字符和n个权值,建立哈夫曼树,进行编码并且输出,并将它存于文件hfmtree中。
这个步骤可以分为两个小步骤:
1.1 建立哈夫曼树
下面是建立哈夫曼树的方法:
首先,我们需要定义一个哈夫曼树节点的结构体:
```c
struct HuffmanNode {
char ch;
int weight;
int parent;
int left;
int right;
};
```
其中,ch表示字符,weight表示权值,parent表示父节点的下标,left和right表示左右子节点的下标。
接下来,我们需要定义一个比较函数,用于排序:
```c
int cmp(const void *a, const void *b) {
return ((struct HuffmanNode *)a)->weight - ((struct HuffmanNode *)b)->weight;
}
```
然后,我们需要按照权值从小到大排序,建立哈夫曼树:
```c
void buildHuffmanTree(struct HuffmanNode *huffmanTree, int n) {
int i, j, k;
for (i = 0; i < n; i++) {
huffmanTree[i].parent = huffmanTree[i].left = huffmanTree[i].right = -1;
}
for (i = n; i < 2 * n - 1; i++) {
huffmanTree[i].parent = huffmanTree[i].left = huffmanTree[i].right = -1;
int min1 = -1, min2 = -1;
for (j = 0; j < i; j++) {
if (huffmanTree[j].parent == -1) {
if (min1 == -1 || huffmanTree[j].weight < huffmanTree[min1].weight) {
min2 = min1;
min1 = j;
} else if (min2 == -1 || huffmanTree[j].weight < huffmanTree[min2].weight) {
min2 = j;
}
}
}
huffmanTree[i].weight = huffmanTree[min1].weight + huffmanTree[min2].weight;
huffmanTree[i].left = min1;
huffmanTree[i].right = min2;
huffmanTree[min1].parent = i;
huffmanTree[min2].parent = i;
}
}
```
其中,n是字符集大小,huffmanTree是哈夫曼树的数组。
1.2 进行编码并且输出,并将它存于文件hfmtree中
下面是进行编码的方法:
首先,我们需要定义一个编码表的结构体:
```c
struct CodeNode {
char ch;
char code[100];
};
```
其中,ch表示字符,code表示编码字符串。
接下来,我们需要遍历哈夫曼树,生成编码表:
```c
void generateCode(struct HuffmanNode *huffmanTree, int n, struct CodeNode *codeTable) {
int i, j, k;
for (i = 0; i < n; i++) {
int node = i;
k = 0;
while (huffmanTree[node].parent != -1) {
int parent = huffmanTree[node].parent;
if (node == huffmanTree[parent].left) {
codeTable[i].code[k++] = '0';
} else {
codeTable[i].code[k++] = '1';
}
node = parent;
}
codeTable[i].code[k] = '\0';
strrev(codeTable[i].code);
codeTable[i].ch = huffmanTree[i].ch;
}
}
```
其中,n是字符集大小,huffmanTree是哈夫曼树的数组,codeTable是编码表的数组。
最后,我们需要将哈夫曼树和编码表存入文件hfmtree中:
```c
void saveHuffmanTree(struct HuffmanNode *huffmanTree, int n, struct CodeNode *codeTable) {
FILE *fp = fopen("hfmtree", "wb");
fwrite(&n, sizeof(int), 1, fp);
fwrite(huffmanTree, sizeof(struct HuffmanNode), 2 * n - 1, fp);
fwrite(codeTable, sizeof(struct CodeNode), n, fp);
fclose(fp);
}
```
2. 利用已建好的哈夫曼编码文件hfmtree,对存储于文件中的某正文进行编码。输出字符正文,再输出该文的二进制码。
这个步骤可以分为两个小步骤:
2.1 对存储于文件中的某正文进行编码
下面是对正文进行编码的方法:
首先,我们需要读取文件hfmtree中的哈夫曼树和编码表:
```c
void readHuffmanTree(struct HuffmanNode *huffmanTree, int *n, struct CodeNode *codeTable) {
FILE *fp = fopen("hfmtree", "rb");
fread(n, sizeof(int), 1, fp);
fread(huffmanTree, sizeof(struct HuffmanNode), 2 * (*n) - 1, fp);
fread(codeTable, sizeof(struct CodeNode), *n, fp);
fclose(fp);
}
```
然后,我们需要读取正文文件,对其中的每个字符进行编码:
```c
void encode(char *infile, char *outfile, struct CodeNode *codeTable, int n) {
FILE *fin = fopen(infile, "rb");
FILE *fout = fopen(outfile, "wb");
int i, j, k;
char ch;
while (fread(&ch, sizeof(char), 1, fin) == 1) {
for (i = 0; i < n; i++) {
if (ch == codeTable[i].ch) {
fwrite(codeTable[i].code, sizeof(char), strlen(codeTable[i].code), fout);
break;
}
}
}
fclose(fin);
fclose(fout);
}
```
其中,infile是正文文件名,outfile是编码文件名,codeTable是编码表,n是字符集大小。
2.2 输出字符正文,再输出该文的二进制码
下面是输出字符正文和二进制码的方法:
```c
void printCode(char *filename) {
FILE *fp = fopen(filename, "rb");
char ch;
printf("Character text: ");
while (fread(&ch, sizeof(char), 1, fp) == 1) {
printf("%c", ch);
}
printf("\n");
fseek(fp, 0, SEEK_SET);
printf("Binary code: ");
while (fread(&ch, sizeof(char), 1, fp) == 1) {
for (int i = 7; i >= 0; i--) {
if (ch & (1 << i))
printf("1");
else
printf("0");
}
}
printf("\n");
fclose(fp);
}
```
其中,filename是编码文件名。
3. 对2)的编码进行译码,译码结果存储到文件中,并与编码前的文件对比。
这个步骤也可以分为两个小步骤:
3.1 对编码进行译码
下面是对编码进行译码的方法:
首先,我们需要读取文件hfmtree中的哈夫曼树和编码表:
```c
void readHuffmanTree(struct HuffmanNode *huffmanTree, int *n, struct CodeNode *codeTable) {
FILE *fp = fopen("hfmtree", "rb");
fread(n, sizeof(int), 1, fp);
fread(huffmanTree, sizeof(struct HuffmanNode), 2 * (*n) - 1, fp);
fread(codeTable, sizeof(struct CodeNode), *n, fp);
fclose(fp);
}
```
接下来,我们需要读取编码文件,将二进制码转化为字符:
```c
void decode(char *infile, char *outfile, struct HuffmanNode *huffmanTree, int n) {
FILE *fin = fopen(infile, "rb");
FILE *fout = fopen(outfile, "wb");
int i, j, k, node = 2 * n - 2;
char ch;
while (fread(&ch, sizeof(char), 1, fin) == 1) {
for (i = 7; i >= 0; i--) {
if (ch & (1 << i)) {
node = huffmanTree[node].right;
} else {
node = huffmanTree[node].left;
}
if (huffmanTree[node].left == -1 && huffmanTree[node].right == -1) {
fwrite(&huffmanTree[node].ch, sizeof(char), 1, fout);
node = 2 * n - 2;
}
}
}
fclose(fin);
fclose(fout);
}
```
其中,infile是编码文件名,outfile是译码文件名,huffmanTree是哈夫曼树,n是字符集大小。
3.2 译码结果存储到文件中,并与编码前的文件对比
下面是将译码结果存储到文件中,并与编码前的文件对比的方法:
```c
void compareFiles(char *file1, char *file2) {
FILE *fp1 = fopen(file1, "rb");
FILE *fp2 = fopen(file2, "rb");
char ch1, ch2;
while (fread(&ch1, sizeof(char), 1, fp1) == 1 && fread(&ch2, sizeof(char), 1, fp2) == 1) {
if (ch1 != ch2) {
printf("Files are not the same.\n");
fclose(fp1);
fclose(fp2);
return;
}
}
if (fread(&ch1, sizeof(char), 1, fp1) != fread(&ch2, sizeof(char), 1, fp2)) {
printf("Files are not the same.\n");
fclose(fp1);
fclose(fp2);
return;
}
printf("Files are the same.\n");
fclose(fp1);
fclose(fp2);
}
```
其中,file1是原始文件名,file2是译码文件名。
最后,我们可以将上述方法组合起来,实现对正文进行哈夫曼编码、译码,并对译码结果与原始文件进行对比的功能。以下是完整代码:
阅读全文