压缩算法c

时间:2013-11-24 15:34:50

标签: c algorithm

我必须使用转义字符(Q)

在c中执行rle算法

示例如果我输入如下: AAAAAAABBBCCCDDDDDDEFG
输出必须是: QA7BBBCCCQD6FFG

这是我做的代码:

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

void main()
{ 
    FILE *source = fopen("Test.txt", "r");
    FILE *destination = fopen("Dest.txt", "w");
    char carCorrente; //in english: currentChar
    char carSucc;     // in english: nextChar
    int count = 1;

    while(fread(&carCorrente, sizeof(char),1, source) != 0) {
        if (fread(&carCorrente, sizeof(char),1, source) == 0){
            if(count<=3){
                for(int i=0;i<count;i++){
                    fprintf(destination,"%c",carCorrente);
                }
            }
            else {
                    fwrite("Q",sizeof(char),1,destination);
                    fprintf(destination,"%c",carCorrente);
                    fprintf(destination,"%d",count);
                }
            break;
        }
        else fseek(source,-1*sizeof(char), SEEK_CUR);

        while (fread(&carSucc, sizeof(char), 1, source) != 0) {
            if (carCorrente ==  carSucc) {
                count++;
            } 
            else {
                if(count<=3){
                    for(int i=0;i<count;i++){
                        fprintf(destination,"%c",carCorrente);
                    }
                }
                else {
                    fwrite("Q",sizeof(char),1,destination);
                    fprintf(destination,"%c",carCorrente);
                    fprintf(destination,"%d",count);
                }

                count = 1;
                goto OUT;
            }
        }

OUT:fseek(source,-1*sizeof(char), SEEK_CUR); //exit 2° while
    }
}

问题是当我有这样的输入时: ABBBCCCDDDDDEFGD
在这种情况下,输出为: QB4CCCQD5FFDD
我不知道为什么:(

4 个答案:

答案 0 :(得分:1)

您的代码存在各种问题。首先,我不确定你是否应该直接从文件中读取。在您的情况下,最好先使用fgets将源字符串读取到文本缓冲区,然后再进行编码。 (我认为在你的作业中,你应该只编码字母。如果source是一个普通的文本文件,它至少会有一个换行符。)

但是我们假设您需要直接从磁盘读取:您不必倒退。你已经有两个变量用于当前和下一个char。从磁盘读取下一个char一次。在进一步阅读“下一个字符”之前,请指定:

int carSucc, carCorr;             // should be ints for getc

carSucc = getc(source);           // read next character once before loop 
while (carSucc != EOF) {          // test for end of input stream
    int carCorr = next;           // this turn's char is last turn's "next"

    carSucc = getc(source);
    // ... encode ...
}

前进和后退使循环变得复杂。此外,如果第二次读取读取零字符,即已到达文件末尾会发生什么?然后你回溯一次然后进入第二个循环。这看起来并不像是有意的。

尝试仅前进,并使用上面的循环作为编码的基础。

答案 1 :(得分:1)

没有必要像你所做的那样使用Fseek来回放,这是一个使用简单的计数器&amp;当前序列字符。

C实施:

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

void main()
{ 
    FILE *source = fopen("Test.txt", "r");
    FILE *destination = fopen("Dest.txt", "w");
    char currentChar;
    char seqChar;
    int count = 0;

    while(1) {
      int flag = (fread(&currentChar, sizeof(char),1, source) == 0); 

      if(flag||seqChar!=currentChar) {

         if(count>3) {
           char ch = 'Q';
           int k = count;
           char str[100];
           int digits = sprintf(str,"%d",count);
           fwrite(&ch,sizeof(ch),1,destination);
           fwrite(&seqChar,sizeof(ch),1,destination);
           fwrite(&str,sizeof(char)*digits,1,destination);
         }
         else {
           for(int i=0;i<count;i++) 
              fwrite(&seqChar,sizeof(char),1,destination);
         }
         seqChar = currentChar;
         count =1;
      }

     else count++;

     if(flag)
       break;
    }

   fclose(source);
   fclose(destination);
}

答案 2 :(得分:1)

我认为你的方法中的主要问题是它太复杂了,有多个不同的地方,你在输入中读取输入和寻找。 RLE可以一次完成,不需要寻找前面的字符。解决这个问题的一种方法是将逻辑更改为查看前面的字符以及重复它们的次数,而不是尝试展望未来的字符。例如:

int repeatCount = 0;
int previousChar = EOF;
int currentChar; // type changed to 'int' for fgetc input

while ((currentChar = fgetc(source)) != EOF) {
    if (currentChar != previousChar) {
        // print out the previous run of repeated characters
        outputRLE(previousChar, repeatCount, destination);
        // start a new run with the current character
        previousChar = currentChar;
        repeatCount = 1;
    } else {
        // same character repeated
        ++repeatCount;
    }
}
// output the final run of characters at end of input
outputRLE(previousChar, repeatCount, destination);

然后,您可以实施outputRLE来执行输出,以打印出c次重复count次的符号(注意count可以为0);这是函数声明:

void outputRLE(const int c, const int count, FILE * const destination)

您可以使用与当前代码相同的方式执行此操作,但可以通过将fwrite和两个fprintf组合到单个fprintf来大大简化。此外,您可能想要考虑如果输入中出现转义字符'Q',或者如果有10个或更多重复字符的运行会发生什么。在outputRLE处理这些案例。


代码中一个不相关的问题是main的返回类型应该是int,而不是void

答案 3 :(得分:0)

非常感谢,我修复了算法。 问题是一个变量,在第一个如果之后。 前

if (fread(&carCorrente, sizeof(char),1, source) == 0)

现在

if (fread(&carSucc, sizeof(char),1, source) == 0){

肯定我的所有算法都很疯狂。我的意思是它太慢了! 我用我的版本和Vikram Bhat的版本进行了测试,我看到了我的算法耗费了多少时间。
肯定有getc()我可以节省更多时间。

现在我正在考虑编码(解压缩),我可以看到一个小问题。

例如:
如果我有如下输入: QA7QQBQ33TQQ10QQQ
我怎么能识别哪个是逃脱字符???

感谢