Очистить поток ввода после fgets установить нулевой символ перед новой строкой
У меня есть программа, где пользователь вводит два ввода. Поскольку я не могу контролировать то, что вводит пользователь, он может пройти фиксированный размер массива. поскольку fgets()
appends сохраняет символ новой строки до конца перед нулевым символом; в случае, если символ новой строки не помещается, когда пользователь выходит за пределы предполагаемого размера, нулевой символ усекает ввод. Символ новой строки, когда пользователь нажимает ввод, все еще существует во входном потоке? Если так, то почему fgets()
пропускает второй раз из-за перевода строки с первого ввода?
#include <stdio.h>
int main(){
char str[5];
fgets(str,5,stdin);
printf("Output:%s",str);
fgets(str,5,stdin);
printf("Output:%s",str);
return 0;
}
Пример ввода ABCDE\n
Выход
Output:ABCDOutput:E
После прочтения этого SO ответа fgets() не запрашивает пользователя второй раз, похоже, проблема не в том, что поток ввода не очищается через fflush(stdin)
, но я слышал противоречивую информацию, говорящую, поскольку это приводит к неопределенному поведению. Мой последний вопрос: какой будет правильный способ очистить поток ввода, если проблема вызвана сохранением новой строки?
4 ответа
пользователь может пройти мимо фиксированного размера массива. поскольку
fgets()
добавляет символ новой строки в конец перед нулевым символом
Нет. Он записывает символы, считанные из входных данных, в предоставленный буфер, вплоть до первой новой строки или включительно, или до тех пор, пока не будет исчерпан указанный размер буфера (менее одного байта для ограничителя строки), или до тех пор, пока не произойдет ошибка или не закончится конец потока. достигнуто, что наступит раньше. Новая строка не изобретена fgets()
; это приходит от входа.
, в случае если новая строка не может уместиться, когда пользователь выходит за рамки предполагаемого размера, нулевой символ усекает ввод. Символ новой строки, когда пользователь нажимает ввод, все еще существует во входном потоке?
Все символы, введенные пользователем и не скопированные в буфер, остаются в ожидании чтения в потоке. Это будет включать новую строку, если пользователь ввел один.
Если да, то является ли это причиной того, что fgets() пропускает второй раз из-за новой строки из первого ввода?
fgets()
не пропускает, но выбирает, где предыдущий вызов прекратил передачу символов из ввода в буфер. Ни один персонаж не потерян. Это означает, что второй вызов возвращает часть первой входной строки, если первый вызов не вернул всю вещь. Вам так или иначе нужно учесть возможность того, что входные данные не соответствуют вашим ожиданиям по длине линии.
проблема, кажется, не очищает входной поток через
fflush(stdin)
,
Нет, это не так. Сброс предназначен для отправки буферизованного вывода на основное устройство вывода. Очистка входного потока приводит к неопределенному поведению. В принципе, это может проявляться как дамп буфера, и данная реализация может даже определять такое поведение, но вы не хотите этого, потому что может быть больше данных в буфере, чем вы хотите избавиться.
но я слышал противоречивую информацию, сказавшую, что это ведет к неопределенному поведению. Мой последний вопрос: какой будет правильный способ очистить поток ввода, если проблема вызвана сохранением новой строки?
Вы читаете из ввода, пока не прочитаете новую строку. Для этого есть множество функций ввода / вывода. fgets()
само по себе может оказаться удобным, так как вы уже используете его:
char str[5];
if (fgets(str, 5, stdin)) {
printf("Output:%s", str);
// read and consume the tail of the line, if any (overwrites str)
while (!strchr(str, '\n') && fgets(str, 5, stdin)) { /* empty */ }
}
fgets()
читает до
1) Новая строка
2) буфер заполнен
3) Конец файла
4) Ошибка ввода (редко)
Этот код читает и заботится о #3 и #4
#define N 5
char buf[N];
if (fgets(buf, sizeof buf, stdin) == NULL) {
// Handle EOF or Error
return EOF;
}
Различать, если '\n'
присутствует... (# 2 из # 1)
// look for a lack of \n
if (strchr(buf, '\n') == NULL) {
И если так, читайте, пока он не найден или EOF
,
int ch;
while ((ch = fgetc(stdin)) != '\n' && ch != EOF);
}
-
Не используйте следующий код. Его можно использовать, читая нулевой символ в качестве первого символа.
size_t len = strlen(buf);
if (buf[len - 1] != '\n') { // bad way to detect \n
Мог бы использовать
if (len > 0 && buf[len - 1] != '\n') { // Good
Я не могу контролировать то, что вводит пользователь...
Нет, ты не можешь.
пользователь может пройти мимо фиксированного размера массива.
Правильно. Это всегда проблема. Тем не менее, в общем, вы хотите организовать вещи так, чтобы это случалось редко.
Например, если вы действительно хотите ограничить (скажем) вводную строку длиной 4 символа, пусть он наберет все, что захочет, а затем посмотрит, сколько он набрал, и если оно превышает ваш лимит, выведите красивое сообщение об ошибке или что-то. Но я не рекомендую звонить fgets(str, 5, stdin)
если вы ожидаете 4 символа ввода плюс символ новой строки, потому что это слишком сложно восстановить, когда (не если) пользователь вводит слишком много.
в случае, если новая строка не может уместиться, когда пользователь выходит за рамки предполагаемого размера, нулевой символ усекает ввод. Символ новой строки, когда пользователь нажимает ввод, все еще существует во входном потоке?
Абсолютно да.
Если да, то является ли это причиной того, что fgets() пропускает второй раз из-за новой строки из первого ввода?
В значительной степени да.
Я рекомендую выделить гораздо больший буфер, а затем выполнить что-то вроде этого:
char inpbuf[512);
if(fgets(inpbuf, sizeof(inpbuf), stdin) == NULL) {
fprintf(stderr, "end of file\n");
return;
}
char *p = strrchr(inpbuf, '\n');
if(p == NULL) {
fprintf(stderr, "looks like you typed *way* too much\n");
return;
}
*p = '\0'; /* erase the \n */
if(strlen(inpbuf) > 4) {
fprintf(stderr, "you typed too much (max 4)\n");
return;
}
strcpy(str, inpbuf);
printf("Output:%s", str);
Один глюк с этим кодом, как написано, однако: если пользователь нажимает клавишу конца файла (control-D в Unix/Linux) перед нажатием Return, вы получите ложное сообщение "похоже, вы набрали слишком много",
Если строка прочитана fgets
не заканчивается новой строкой, вы знаете, что она все еще в буфере. В этом случае позвоните getchar
в цикле, пока вы не получите новую строку.
fgets(str,5,stdin);
printf("Output:%s",str);
if (strchr(str, '\n') == NULL) {
int c;
while ((c = getchar()) != EOF && c != '\n');
}
fgets(str,5,stdin);
printf("Output:%s",str);