Проблема с EOF в C
Вопрос
Я пишу программу, которая должна читать две строки, которые могут содержать разрывы строк и другие символы.Поэтому я использую EOF (Ctrl-Z или Ctrl-D) для завершения строки.
Это отлично работает с первой переменной, но со второй переменной это кажется проблематичным, поскольку очевидно, что что-то застряло во входном буфере, и пользователь не может ничего вводить.
Я попытался очистить буфер с помощью while (getchar() != '\n');
и несколько подобных вариантов, но, похоже, ничего не помогает.Все попытки очистки приводили к бесконечному циклу, а без очистки добавление второй переменной невозможно.
Символы обеих переменных считываются в цикле следующим образом: while((c = getchar()) != EOF)
, что предполагает, что это EOF, который я застрял в своем буфере.Или это как-то по-другому влияет на поведение программы?Что-то не так с логикой, которую я использую?
Я начинаю немного отчаиваться после нескольких часов борьбы с этим.
[редактировать:добавлен код ниже]
[править 2:Clearerr(), похоже, все-таки заставляет это решение EOF работать.
Кажется, он работает в своей исходной форме, как я и предполагал под Linux, вчера я пробовал его с Windows.]
код:
#include <stdio.h>
#include <string.h>
int main(void)
{
int x = 0;
int c;
char a[100];
char b[100];
printf("Enter a: ");
while((c = getchar()) != EOF)
{
a[x] = c;
x++;
}
a[x] = '\0';
x = 0;
/*while (getchar() != '\n'); - the non-working loop*/
printf("\nEnter b: ");
while((c = getchar()) != EOF)
{
b[x] = c;
x++;
}
b[x] = '\0';
printf("\n\nResults:\na: %s\n", a);
printf("b: %s\n", b);
return(0);
}
[править 3:]
Проблема с динамической памятью:
Моя программа также должна обрабатывать строки длиной более 100 символов.Первоначально я намеревался решить эту проблему путем динамического выделения памяти, но когда у меня возникли проблемы с описанным выше бесконечным циклом и сбоями, связанными с памятью, я отказался от этого и переключился на char[100].
Я думаю, что то, что я пробовал, было примерно таким:
while((c = getchar()) != EOF)
{
a = malloc(sizeof(char));
a[x] = c;
x++;
}
Это возможный (или разумный) способ сделать это?Я пытаюсь выделить больше памяти для каждого обрабатываемого там персонажа.Индивидуально.С таким кодом (этот пример, вероятно, содержит синтаксические ошибки) у меня возникали сбои, поэтому мне кажется, что malloc может быть здесь не той функцией, или я пытаюсь ее неправильно.Предположим, что это вообще возможно.
Решение
После того, как вы получили EOF от терминала, вы не получите никаких дополнительных данных.Невозможно отменить ввод EOF - конец файла - это, ну, конец.
Поэтому вам следует определить, что каждая переменная вводится в отдельной строке, и чтобы пользователи нажимали Enter вместо EOF.Вам все равно нужно проверить, получили ли вы eof, потому что это означает, что пользователь действительно набрал EOF, и больше вы ничего не увидите — в этом случае вам нужно выйти из цикла и напечатать сообщение об ошибке.
Другие советы
EOF
это не характер - это специальное значение, которое возвращают функции ввода, чтобы указать состояние, что достигнут «конец файла» в этом входном потоке.Как Мартин против.Лёвис говорит, что как только возникает условие «конец файла», это означает, что в этом потоке больше не будет доступных входных данных.
Путаница возникает потому, что:
- Многие типы терминалов распознают специальное нажатие клавиши для обозначения «конца файла», когда «файл» является интерактивным терминалом (например.Ctrl-Z или Ctrl-D);и
- А
EOF
значение — одно из значений, которые может вернуть функцияgetchar()
семейство функций.
Для разделения входных данных вам потребуется использовать фактическое значение символа — нулевой символ ASCII. '\0'
может быть хорошим выбором, если оно не может отображаться как допустимое значение внутри самих входных данных.
Я запускаю код на своем компьютере с Linux, вот результат:
Enter a: qwer
asdf<Ctrl-D><Ctrl-D>
Enter b: 123
456<Ctrl-D><Ctrl-D>
Results:
a: qwer
asdf
b: 123
456
Два сочетания клавиш Ctrl-D потребовались, поскольку входной буфер терминала не был пуст.
Вы можете использовать нулевой символ ('\0'
), чтобы разделить переменные.Различные инструменты UNIX (например. find
) способны разделять элементы вывода таким образом, что позволяет предположить, что это довольно стандартный метод.
Еще одним преимуществом этого является то, что вы можете прочитать поток в один буфер, а затем создать массив char*
s, чтобы указать на отдельные строки, и каждая строка будет правильно '\0'
-завершено без необходимости вручную что-либо менять в буфере.Это означает меньшие затраты на выделение памяти, что может заставить вашу программу работать заметно быстрее в зависимости от того, сколько переменных вы читаете.Конечно, это необходимо только в том случае, если вам нужно хранить в памяти все переменные одновременно — если вы имеете дело с ними по одной, вы не получите этого особого преимущества.
То, что вы пытаетесь, принципиально невозможно с EOF.
Хотя в некотором смысле он ведет себя аналогично, EOF — это не символ в потоке, а определяемый средой макрос, представляющий конец потока.Я не видел вашего кода, но я так понимаю, вы делаете что-то вроде этого:
while ((c=getchar()) != EOF) {
// do something
}
while ((c=getchar()) != EOF) {
// do something else
}
Когда вы вводите символ EOF в первый раз, чтобы завершить первую строку, поток безвозвратно закрывается.То есть статус потока — закрыт.
Таким образом, содержимое второго цикла while никогда не запускается.
Вместо того, чтобы прекращать чтение ввода в EOF - это не персонаж -- остановитесь на ENTER.
while((c = getchar()) != '\n')
{
if (c == EOF) /* oops, something wrong, input terminated too soon! */;
a[x] = c;
x++;
}
EOF — это сигнал о том, что вход завершился.Вам почти гарантировано, что все вводимые пользователем данные заканчиваются на ' ':это последний ключ, который вводит пользователь!!!
Редактировать:вы все еще можете использовать Ctrl-D и clearerr()
для сброса входного потока.
#include <stdio.h>
int main(void) {
char a[100], b[100];
int c, k;
printf("Enter a: "); fflush(stdout);
k = 0;
while ((k < 100) && ((c = getchar()) != EOF)) {
a[k++] = c;
}
a[k] = 0;
clearerr(stdin);
printf("Enter b: "); fflush(stdout);
k = 0;
while ((k < 100) && ((c = getchar()) != EOF)) {
b[k++] = c;
}
b[k] = 0;
printf("a is [%s]; b is [%s]\n", a, b);
return 0;
}
$ ./a.out Enter a: two lines (Ctrl+D right after the next ENTER) Enter b: three lines now (ENTER + Ctrl+D) a is [two lines (Ctrl+D right after the next ENTER) ]; b is [three lines now (ENTER + Ctrl+D) ] $
Как ввести null в программу?
Вы можете реализовать функцию -print0, используя:
putchar(0);
Это приведет к печати нулевого символа ASCII '\0' в sdtout.