Проблема с обработкой файлов (начальный уровень)? - PullRequest
0 голосов
/ 10 февраля 2019

Цель состоит в том, чтобы заполнить файл output.txt символами, которые повторяются один за другим n или более раз (n - целое число, введенное с клавиатуры).

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

int main() {
    int c=0,d=0;
    int n;
    scanf("%d",&n);
    FILE* ulaz=fopen("input.txt","r");
    if(ulaz==NULL) {
        printf("TRY AGAIN");
        return 1;
    }
    FILE* izlaz=fopen("output.txt","w");
    if(izlaz==NULL) {
        fclose(ulaz);
        printf("TRY AGAIN");
        return 2;
    }
    long pos;
    int br=1;
    do {
        c=fgetc(ulaz);
            if(c!=EOF) {
            do {
                long pos=ftell(ulaz);
                d=fgetc(ulaz);
                if(c==d) {
                    br++;
                }
            } while(d==c && d!=EOF);
            int i=0;
            if(br>=n) {
                for(i=0;i<n;i++) {
                    fputc(c,izlaz);
                }
            }
            fseek(ulaz,pos,SEEK_SET);
            //is this line a problem
            br=1; }
    } while(c!=EOF);
    fclose(ulaz);
    fclose(izlaz);
}

Ответы [ 2 ]

0 голосов
/ 10 февраля 2019

В дополнение к проблеме с затенением переменной, отмеченной @ StephanLechner , вы чрезмерно усложняете распознавание и вывод повторяющихся символов во входном файле.Нет необходимости во вложенных циклах в цикле чтения (кроме вывода повторяющихся символов), и нет причин для ftell или fseek.

Использование подхода State-Loop

Каждый раз, когда вы решаете такую ​​проблему, как повторный подсчет символов (или удаление пробелов и т. Д.), Все, что вам нужно, это делать цикл по каждому символу ввода и поддерживать текущее «состояние» того,читают повторяющийся символ и обрабатывают любое изменение состояния o, используя условные выражения в вашем цикле (обычно называемый подходом «цикл состояния»)

В вашем случае вам нужно только сохранить (1) предыдущий символ, прочитанный дляопределить, является ли ток повторным, и (2) счетчик повторений.После этого у вас есть только 2 возможных состояния: (1) текущий символ является повторением последнего или (2) это не так.

После открытия и проверки входные и выходные файлы готовы к чтениюи написание, вы можете обрабатывать обнаружение дубликатов и выходить аналогично следующему:

    int c, last = 0;            /* current and last char read */
    size_t repeated, cnt = 1;   /* min repeated and repeat count */
    FILE *ulaz, *izlaz;         /* file pointers */
    ...
    while ((c = fgetc (ulaz)) != EOF) {     /* read each char */
        if (c == last)                      /* if equals last read */
            cnt++;                          /* increment count */
        else {  /* otherwise */
            if (cnt >= repeated)            /* if count >= min */
                while (cnt--)               /* loop count times */
                    fputc (last, izlaz);    /* writing last to out */
            cnt = 1;                        /* reset count */
        }
        last = c;   /* save c as last */
    }

Ход цикла прост.Вы читаете символы из ввода, проверяете, является ли это повторяющимся символом или нет, и сохраняете текущий символ как last.

Как вы реагируете на каждое состояние?

В случае, если символ является повторяющимся символом, вы просто увеличиваете счетчик повторений (всегда инициализируется 1 - при обнаружении повторения это будет второе вхождение буквы).Если символ не является повторением, то вам нужно проверить, равно или нет накопленное количество повторений, или превышает тот минимум, который вы проверяете, и, если это так, вывести это число last символов в выходной файл - всегда сбрасываясчитать до 1.Вот и все.Подход с циклом состояний может упростить многие проблемы с похожим типом.

Остальное просто считывает ваш минимальный предел [1] как ввод данных пользователем и закрывает ваши файлы, когда высделано.Одно замечание: вы всегда должны проверять закрытие после записи , чтобы защитить от ошибок потока, которые не могут быть перехвачены проверкой самой записи.fclose возврат EOF идентифицирует такие ошибки, и errno устанавливается соответственно.

В целом, вы можете сделать:

#include <stdio.h>

int main (int argc, char **argv) {

    int c, last = 0;            /* current and last char read */
    size_t repeated, cnt = 1;   /* min repeated and repeat count */
    FILE *ulaz, *izlaz;         /* file pointers */

    if (argc < 3 ) {    /* validate at least two arguments given */
        fprintf (stderr, "error: insufficient input,\n"
                    "usage: %s infile outfile\n", argv[0]);
        return 1;
    }

    /* validate input and output files are open */
    if (!(ulaz = fopen (argv[1], "r")) || !(izlaz = fopen (argv[2], "w"))) {
        perror ("fopen-file");
        return 1;
    }

    /* prompt, read, validate minimum repeatee input */
    fputs ("enter the minimum repeat: ", stdout);
    if (scanf ("%zu", &repeated) != 1) {
        fputs ("error: invalid input.\n", stderr);
        return 1;
    }

    while ((c = fgetc (ulaz)) != EOF) {     /* read each char */
        if (c == last)                      /* if equals last read */
            cnt++;                          /* increment count */
        else {  /* otherwise */
            if (cnt >= repeated)            /* if count >= min */
                while (cnt--)               /* loop count times */
                    fputc (last, izlaz);    /* writing last to out */
            cnt = 1;                        /* reset count */
        }
        last = c;   /* save c as last */
    }
    fputc ('\n', izlaz);            /* tidy up with POSIX '\n' */

    fclose (ulaz);                  /* close input */
    if (fclose (izlaz) == EOF)      /* validate close after write */
        perror ("fclose-izlaz");

    return 0;
}

( примечание: вы всегда должны убедиться, что ваш вывод соответствует POSIX, включая '\n' после последней строки в выводе - это применимо, пишете ли вы выходной файл или в терминал - никому не нравится, когда их подсказка портится)

Пример входного файла

$ cat dat/repeat.txt
abbc dddde fgggh ijkllllmmmn oopqrs

Пример использования / выходные файлы

с минимум 2-значными символами:

$ ./bin/repeated_ulaz dat/repeat.txt dat/repeated_out-2.txt
enter the minimum repeat: 2

$ cat dat/repeated_out-2.txt
bbddddgggllllmmmoo

С минимум 3 символа:

$ ./bin/repeated_ulaz dat/repeat.txt dat/repeated_out-3.txt
enter the minimum repeat: 3

$ cat dat/repeated_out-3.txt
ddddgggllllmmm

С минимум 4 символа:

$ ./bin/repeated_ulaz dat/repeat.txt dat/repeated_out-4.txt
enter the minimum repeat: 4

$ cat dat/repeated_out-4.txt
ddddllll

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

Сноски

[1] , если вы используете старую версию компилятора VS, которая не обрабатывает size_t "%zu" спецификатор формата, измените все size_t на unsigned и измените все "%zu" на "%u".

0 голосов
/ 10 февраля 2019

Это потому, что вы пишете long pos=ftell(ulaz); вместо pos=ftell(ulaz);.В блоке do, где вы определяете новую переменную long pos= ..., вы закрываете внешнюю переменную pos, которую вы будете использовать позже для сброса позиции до значения, которое вы сохранили.

Возможно, есть и другие проблемы, но это наиболее очевидная проблема в связи с тем, что "// эта строка - проблема".

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...