Почему этот цикл fgets() никогда не заканчивается?

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

#define BUF_SIZE 1024

int main ()
{
    char buffer[BUF_SIZE];
    int contentSize = 1; 

    char* content = (char*)malloc(sizeof(char) * BUF_SIZE);
    content[0] = '\0';

    while(fgets(buffer, BUF_SIZE, stdin))
    {
        contentSize = contentSize + strlen(buffer);
        content = (char*)realloc(content, contentSize);
        strcat(content, buffer);
    }

    return 0;
 }

я пытаюсь прочитать все, что вводит пользователь (даже пробельные символы), и я довольно близок к тому, чтобы заставить его работать, но цикл никогда не заканчивается. fgets() просто просит больше информации, как мне это исправить, пожалуйста.


person user2756746    schedule 08.09.2013    source источник
comment
Как вы сказали в комментарии ниже, вы используете Windows (эту информацию стоит указать в вопросе :-). - Вы пробовали клавишу F6 для EOF? Сравните stackoverflow.com/questions/ 10589637/.   -  person Martin R    schedule 08.09.2013
comment
да это сработало! интересно, спасибо   -  person user2756746    schedule 08.09.2013
comment
Возможный дубликат проблемы с EOF в Windows 7   -  person Martin R    schedule 08.09.2013


Ответы (3)


fgets() возвращает указатель на буфер (который желательно не является указателем NULL), который вы ему передали, до тех пор, пока он не сможет прочитать больше строк из файла.

Конечно, при чтении со стандартного ввода он всегда будет ждать следующей строки, поэтому вам нужно явно отправить EOF на терминал, чтобы он сообщил о конце файла. Вы можете сделать это, например, нажав <ctrl> + <D>.

person Community    schedule 08.09.2013
comment
‹Ctrl›+‹D› в большинстве *nix, которые я видел; ‹Ctrl›+‹Z› в Windows. - person cHao; 08.09.2013
comment
Я только что попробовал это, я поставил [Control] + D в конце некоторых случайных символов, и он все еще запрашивает дополнительный ввод. Я использую Windows 8. Ctrl + Z также не работает. - person user2756746; 08.09.2013
comment
@ user2756746: Возможно, вам придется убедиться, что он находится на отдельной строке. - person cHao; 08.09.2013
comment
это тоже не сработало. Я думаю, что лучший подход — это то, что сказал Манодж Пандей. - person user2756746; 08.09.2013
comment
@ user2756746 Вы наверняка что-то упускаете. В терминале каждой современной операционной системы есть возможность отправить EOF. Даже в той самой винде. Подход Маноя приемлем, если вы можете придумать такое волшебное выходное слово, но если ваши входные данные потенциально будут произвольными, то это неправильно. - person ; 08.09.2013
comment
Если я правильно помню, клавиша F6 имитирует EOF в терминальном приложении Windows, сравните «проблемы с eof в Windows 7»> stackoverflow.com/questions/10589637/. - person Martin R; 08.09.2013
comment
Я использую терминал Microsoft Visual Studio, может быть, поэтому? - person user2756746; 08.09.2013
comment
но заставить пользователя ввести ctrl + z более запутанно, чем заставить его ввести «выход» после того, как он ввел свой произвольный ввод. - person user2756746; 08.09.2013

Один из способов — заставить пользователя ввести определенное ключевое слово (например, quit или quit()), а затем выйти из цикла while.

while(fgets(buffer, BUF_SIZE, stdin)) {
           contentSize = contentSize + strlen(buffer);
           content = (char*)realloc(content, contentSize);
           strcat(content, buffer);
           if (strncmp(buffer, "quit", strlen("quit")) == 0) {
               break;
           }
}
person Manoj Pandey    schedule 08.09.2013
comment
Для программы, которая должна работать в интерактивном режиме (т. е. ожидает, что стандартный ввод будет терминалом), это немного менее уродливо (после того, как проблема с выходом из буфера будет исправлена), чем требование от пользователя ввести символ EOF. Тем не менее, можно легко представить себе ситуацию, когда вы хотели бы, чтобы строка говорила о выходе. Вам нужно будет предоставить какой-то способ сделать это. - person cHao; 08.09.2013
comment
Нахождение выхода в буфере можно исправить, выполнив сравнение перед тем, как вы перераспределите и скопируете. - person cHao; 08.09.2013
comment
это сложнее, чем я себе представлял. что, если я просто использую цикл for? это сработает - person user2756746; 08.09.2013
comment
Это, конечно, сложно... Но некоторые интерактивные программы работают таким образом. Например, в Python вы можете ввести quit() для выхода. Аналогичным образом, приглашение gdb (iirc) также можно закрыть с помощью ключевого слова quit. Теперь вам нужно проверить, учитывая ваш вариант использования, будет ли пользователь вводить ключевое слово, такое как quit(), в качестве обычных входных данных? Если это не очень распространено, вы можете указать, что пользователь может ввести quit() в одну строку, чтобы завершить программу. - person Manoj Pandey; 08.09.2013

Цикл никогда не заканчивается, потому что OP не закрыл stdin. Как только stdin будет закрыт и все данные прочитаны, fgets() вернет NULL.

Решение @ H2CO3 находится в нужном месте, но OP, возможно, упустил некоторые тонкости Windows.

<Ctrl>+<Z> будет сигнализировать EOF, если входной буфер пуст И за ним следует Enter. Пример A, B, C, Enter, теперь пустой буфер, <Ctrl>+<Z>, Enter.

Если во входном буфере уже есть данные, <Ctrl>+<Z> читается как <Ctrl>+<Z>(ASCII 26).

Идея «выйти» имеет свои достоинства, но пользователь все равно может ввести <Ctrl>+<Z>, Enter. Код должен быть готов к этому. (Опубликованный ответ «уйти» делает это.) Я предлагаю избегать волшебных слов - если только это не «relinquo codicem».

person chux - Reinstate Monica    schedule 10.09.2013