2013-11-24 128 views
0

我必须做RLE算法在C与转义字符(Q)RLE压缩算法的c

例如,如果我有等的输入: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
,我不知道为什么:(

+0

你知道'fread'和其他阅读功能的文件提前在文件中读取位置,不是吗?所以当你只检查0而不存储结果时,A就会被吃掉。另外,请考虑使用'c = getc(f)'而不是'fread',它更适合更长的数据块。 –

+0

是的,我知道这个原因:
fseek(source,-1 * sizeof(char),SEEK_CUR); –

+0

如果我使用getc我怎么能回到文件中的指针? –

回答

1

有没有必要使用Fseek来回滚,因为你已经完成了,这里是一个代码,已经写入,而不使用它通过使用简单的计数器&当前序列字符。

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); 
} 
+0

@MOehm Didnt实现,因为他没有给出规范,但它是一个小的改变,使用整数字符串代码 –

+0

@MOehm检查我修改过的代码count> 9 –

+0

好吧,但没有规范说数量还不到10,或者在那里? Anywy,感谢您的更新。在Q是一个转义字符的情况下,他甚至可能会忽略大于10的计数。 –

1

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

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

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 ... 
} 

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

试着只转发,并使用上面的循环作为编码的基础。

+0

感谢您的建议。我必须做一个像win zip这样的使用rle方法和转义字符的算法。但我认为这是更好的开始只是一个正常的文件,所以我可以看到如何工作的算法。如果它在我必须使用一个文件后运行良好,例如一张png图片。但我认为逻辑完全一样。只改变输入文件。不?我也想问你一些关于EOF的问题。为什么我必须为变量使用整数? EOF是一个数字?所以当我到达文件的末尾,哪个数字会有carSucc?这个数字是EOF的转换吗? thx –

+0

好的,我误解了你的任务。 Q对于转义角色来说是一个奇怪的选择,我认为这是一个“玩具”问题,应该只处理字母。关于'getc'中的'int':它返回一个无符号字符范围内的整数,即。 0到255.特例是'EOF',它是一个负值。它表明你在文件的末尾。 (关键是:用int来存储'getc'的结果,整个故事不适合注释,甚至像'a'这样的char常量也是C中的int。) –

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

0

非常感谢你,我修正了我的算法。 问题是一个变量,在第一个,如果过了一段时间。 之前

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

现在

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

肯定的所有我的算法是野生的。我的意思是它太慢了!
我用我的版本和Vikram Bhat版本做了一个测试,我看到我的算法有多少时间没有了。
肯定与getc()我可以节省更多的时间。

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

例如:
如果我有等的输入:QA7QQBQ33TQQ10QQQ
如何可以识别哪个是转义字符???

感谢