2013-11-24 5 views
0

Я должен сделать алгоритм RLE в C с экранирующий символ (Q)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
и я не знаю почему :(

+0

Вы знаете, что 'fread' и другие функции чтения файлов заранее позицию чтения в файле, не так ли? Поэтому, когда вы просто проверяете 0, не сохраняя результат, A получает едят. Кроме того, рассмотрите возможность использования 'c = getc (f)' вместо 'fread', что лучше подходит для более длинных блоков данных. –

+0

да, я знаю по этой причине:
fseek (источник, -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 Проверьте мой модифицированный код для подсчета> 9 –

+0

Хорошо, но не было спецификации, которая говорила счет меньше 10, или был там? В любом случае, спасибо за обновление. В сценарии, где Q является escape-символом, он может даже уйти с игнорированием отсчетов более 10. –

1

Ваш код имеет различные проблемы. Во-первых, я не уверен, следует ли читать прямо из файла. В вашем случае может быть лучше прочитать исходную строку в текстовом буфере сначала с fgets, а затем сделать кодировку. (Я думаю, что в вашем задании вам нужно только кодировать буквы. Если source является обычным текстовым файлом, он будет иметь как минимум одну новую строку.)

Но давайте предположим, что вам нужно читать прямо с диска: t должны идти назад. Вы уже используете две переменные для текущего и следующего символов. Прочитайте следующий символ с диска один раз. Перед тем, как читать далее «Следующие символы», назначьте:

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-метод с escape-символом. Но мне сложно начать с обычного файла, поэтому я вижу, как работает алгоритм. если он работает хорошо после того, как я должен работать с файлом, например, png-изображение. но я думаю, что логика точно такая же. изменяет только входной файл. нет ??? Я также хотел бы спросить вас об EOF. почему я должен использовать целое число для переменной? EOF - это число? поэтому, когда я дойду до конца файла, у которого будет номер carSucc? и это число является преобразованием EOF? thx –

+0

Хорошо, я неправильно понял вашу задачу. Поскольку Q является странным выбором для escape-символа, я думал, что это «игрушечная» проблема, которая должна обрабатывать только буквы. О 'int' в' getc': он возвращает целое число в диапазоне unsigned char, т. Е. 0 до 255. Частным случаем является «EOF», который является отрицательным значением. Это означает, что вы находитесь в конце файла. (Дело в том, что используйте int для хранения результата 'getc'. Вся история не вписывается в комментарий. Также даже константы char, такие как' 'a'', являются' int 'in C.) –

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.Кроме того, возможно, вам захочется подумать, что произойдет, если во входной строке появится escape-символ 'Q' или будет выполняться 10 или более повторяющихся символов. Рассматривайте эти случаи в outputRLE.


неродственная проблема в вашем коде является то, что тип возвращаемого main должен быть int, не void.

0

Большое вам спасибо, я исправил свой алгоритм. Проблема была переменной, в первой, если после этого. Перед

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

Теперь

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

наверняка весь мой алгоритм дик. Я имею в виду, что это слишком медленно!
Я сделал тест с моей версией и с версией Викрама Бхата, и я увидел, насколько мой алгоритм потерял время.
Конечно с помощью getc() я могу сэкономить больше времени.

Теперь я думаю о кодировании (декомпрессии), и я вижу небольшую проблему.

пример:
, если я иметь вход как: QA7QQBQ33TQQ10QQQ
как я могу признать что экранирующий символ ???

благодарит

Смежные вопросы