Хотя вы можете использовать отформатированную функцию ввода scanf
, чтобы принять ваш ввод в виде строки, scanf
полон ряда ловушек, которые могут оставить случайные символы в вашем потоке ввода (stdin
) в зависимости от того, является ли происходит сбой сопоставления . Он также имеет ограничение на использование "%s"
спецификатора преобразования только для чтения до первого пробела . Если ваш пользователь проскальзывает и вводит "123 45"
, вы читаете "123"
, ваши тесты не пройдены, а "45"
остаются в stdin
непрочитанными, ожидая, пока вы не укусите вас при следующей попытке чтения, если только вы вручную не очистите stdin
.
Кроме того, если вы используете "%s"
без модификатора width-width - вы также можете использовать gets()
, так как scanf
с радостью прочитает неограниченное количество символов в ваш массив из 5 или 6 символов, запись за пределы вашего массива вызывает Undefined Behavior .
Более разумный подход - обеспечить буфер символов, достаточно большой для обработки всего, что пользователь может ввести. ( не экономьте на размере буфера ). Чтение всей строки за раз с помощью fgets()
, что с буфером достаточного размера гарантирует, что вся строка будет занята, исключая возможность для непрочитанных символов в stdin
. Единственное предостережение с fgets
(и с каждой линейно-ориентированной функцией ввода, такой как POSIX getline
) - это '\n'
, также считывается и включается в заполненный буфер. Вы просто обрезаете '\n'
с конца, используя strcspn()
в качестве удобного метода, получая количество символов, введенных одновременно.
( примечание: вы можете до go обрезка '\n'
, если вы отрегулируете свои тесты, чтобы включить '\n'
в длину, с которой вы проверяете, поскольку преобразование в int
будет игнорировать конечный '\n'
)
Ваш лог c отсутствует еще одна необходимая проверка. Что если пользователь введет "123a5"
? Введены все 5 символов, но не все цифры. atoi()
не имеет возможности сообщать об ошибках и с радостью преобразует строку в 123
в автоматическом режиме, без указания каких-либо дополнительных символов. У вас есть два варианта: либо использовать strtol
для преобразования и проверить, что не осталось символов, либо просто l oop над символами в строке, проверяя каждый с помощью isdigit()
, чтобы убедиться, что введены все цифры.
В целом, вы можете сделать что-то вроде следующего:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define NDIGITS 5 /* if you need a constant, #define one (or more) */
#define MAXC 1024
int main (void) {
int mynumber;
size_t digit = NDIGITS;
char buf[MAXC]; /* buffer to hold MAXC chars */
/* infinite loop until valid string entered, or manual EOF generated */
for (;;) {
size_t len;
printf("\nEnter a %zu digit number: ", digit); /* prompt */
if (!fgets (buf, sizeof buf, stdin)) { /* read entire line */
fputs ("(user canceled input)\n", stdout);
break;
}
buf[(len = strcspn(buf, "\n"))] = 0; /* trim \n, get len */
if (len != digit) { /* validate length */
fprintf(stderr, " error: %zu characters.\n", len);
continue;
}
for (size_t i = 0; i < len; i++) { /* validate all digits */
if (!isdigit(buf[i])) {
fprintf (stderr, " error: buf[%zu] is non-digit '%c'.\n",
i, buf[i]);
goto getnext;
}
}
if (sscanf (buf, "%d", &mynumber) == 1) { /* validate converstion */
printf ("you entered %zu digits, mynumber = %d\n", len, mynumber);
break; /* all criteria met, break loop */
}
getnext:;
}
return 0;
}
Пример использования / Вывод
Всякий раз, когда вы пишете подпрограмму ввода, go пробуйте и разбить его. Подтвердите, что он делает то, что вам нужно, и обнаруживает случаи, от которых вы хотите защитить (и еще будет больше проверок, которые вы можете добавить). Здесь он охватывает большинство ожидаемых злоупотреблений:
$ ./bin/only5digits
Enter a 5 digit number: no
error: 2 characters.
Enter a 5 digit number: 123a5
error: buf[3] is non-digit 'a'.
Enter a 5 digit number: 123 45
error: 6 characters.
Enter a 5 digit number: ;alsdhif aij;ioj34 ;alfj a!%#$%$ ("cat steps on keyboard...")
error: 61 characters.
Enter a 5 digit number: 1234
error: 4 characters.
Enter a 5 digit number: 123456
error: 6 characters.
Enter a 5 digit number: 12345
you entered 5 digits, mynumber = 12345
Пользователь отменяет ввод с помощью ctrl + d на Linux (или ctrl + z на windows) создание руководства EOF
:
$ ./bin/only5digits
Enter a 5 digit number: (user canceled input)
( примечание: вы можете добавить дополнительные проверки, чтобы увидеть, были ли введены 1024 или более символов - это вам осталось)
Это немного другой подход к чтению ввода, но с точки зрения общего правила, принимая пользовательский ввод, если вы гарантируете, что используете всю строку ввода, вы избежите многих ловушек, связанных с использованием scanf
для этого цель.
Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.