C - Ввести мертвый цикл после попытки прочитать строку за строкой файла и строки могут содержать пробел? - PullRequest
0 голосов
/ 18 сентября 2018

Я пытаюсь прочитать файл построчно и посчитать символы каждой строки.Эти строки могут содержать пробелы, и мне нужно их посчитать.Мне разрешено использовать только функции feof и scanf.

Пример кода

...
while(!feof(stdin)){
    char inputLineArray[1000];
    scanf("%[^\n]s", inputLineArray);
    printf(inputLineArray);
}
...

Мой пример файла представляет собой текстовый файл, который содержит следующее содержимое:

hello world
abcdsdsdsdsd

Но после печати:

hello world

Моя программа застряла в бесконечном цикле, который ничего не делает.

Ответы [ 2 ]

0 голосов
/ 18 сентября 2018

scanf("%[^\n]s", inputLineArray); неверно и неуместно:

  • у спецификатора конверсии нет завершающего s, просто %[^\n];
  • scanf читаетпоток и сохраняет любые символы перед новой строкой в ​​inputLineArray и оставляет новую строку ожидающей в потоке;
  • scanf должно быть задано максимальное количество символов для хранения, чтобы избежать неопределенного поведения на длинных строках: scanf("%999[^\n]", inputLineArray);
  • Вы должны проверить возвращаемое значение scanf(), чтобы определить, было ли преобразование успешным.Тест while (!feof(stdin)) патологически неуместен: Почему «while (! Feof (file))» всегда неверно? ;
  • , тогда вы увидите другую проблему: это преобразование завершается неудачно в пустых строкахпоскольку в массиве назначения нет символов, которые нужно сохранить, и поскольку scanf() оставляет перевод новой строки в ожидании, второй вызов завершается неудачно и все последующие тоже.

Обратите также внимание, что вызов с высокой степенью рискаprintf с предоставленными пользователем данными в виде строки формата.Поведение не определено, если строка содержит нетривиальные спецификации формата.

Вот лучший способ прочитать файл построчно:

#include <stdio.h>
#include <string.h>

...

    char inputLineArray[1001];
    while (fgets(inputLineArray, sizeof inputLineArray, stdin)) {
        buf[strcspn(buf, "\n")] = '\0';  // strip the trailing newline if present
        printf("%s\n", inputLineArray);
    }

...

Обратите внимание, что строки ввода с 1000 байтов илибольше будет разбито на несколько выходных строк.

scanf() - не тот инструмент, который вам подходит, действительно, он полон причуд и недостатков, но если вам необходимо использовать scanf(), вотисправленная версия:

    char inputLineArray[1000];
    while (scanf("%c", &inputLineArray[0]) == 1) {
        /* one byte was read, check if it is a newline */
        if (inputLineArray[0] == '\n') {
            /* empty line must be special cased */
            inputLineArray[0] = '\0';
        } else {
            /* set the null terminator in case the next `scanf` fails */
            inputLineArray[1] = '\0';
            /* attempt to read the rest of the line */
            scanf("%998[^\n]", inputLineArray + 1);
            /* consume the pending newline, if any */
            scanf("%*1[\n]");
        }
        printf("%s\n", inputLineArray);
    }
    if (feof(stdin)) {
        /* scanf() failed at end of file, OK */
    } else {
        printf("read error\n");
    }

Обратите внимание, что feof() не используется, так как scanf("%c", ...) вернет EOF в конце файла, поэтому цикл while() с остановкой, как и ожидалось.

feof() используется только для того, чтобы отличить конец файла от состояния ошибки чтения в потоковом вводе / выводе.Большинству программ на С не нужно различать их, поскольку ошибки чтения могут обрабатываться так же, как и усеченные входные файлы.Эта функция почти всегда используется неправильно.Короче говоря, вы никогда не должны использовать feof() или другие подверженные ошибкам или устаревшие функции, такие как gets() и strncpy().Будьте также очень осторожны с sprintf(), strcpy(), strcat() ...

0 голосов
/ 18 сентября 2018

С man 3 scanf :

Семейство функций scanf () сканирует ввод в соответствии с форматом, описанным ниже.

Это означает, что предоставленный вами шаблон %[^\n]s (не соответствует символу новой строки) прекратит сопоставление после world, поскольку есть символ новой строки. Вам нужно перейти к следующему символу в потоке.

В Stackoverflow есть много вопросов, подобных вашему, поиск по scanf infinite loop.

...