2013-11-24 155 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
如何可以識別哪個是轉義字符???

感謝