Необходимо взять входную строку размером 5. Также необходимо проверить достоверность в каком-то несвязанном смысле. Если ввод недействителен, пользователю будет предложено ввести его еще раз.
Используя scanf("%4s", input);
Но если пользовательский ввод предполагает строку из 8 символов, scanf просто сканирует последние 4 символа, все еще находящиеся в буфере, прежде чем запрашивать фактический пользовательский ввод.
Я попытался создать буферную переменную, в которой будут храниться входные данные перед передачей их целевой переменной.
Среди других соответствующих вопросов см. scanf() оставляет символ новой строки в буфере.
Просто: всегда получайте от пользователя полную строку текста (используя fgets()
).
Если пользователь не предоставляет вам полную строку текста, то введенные данные недействительны; либо пожаловаться и прервать выполнение, либо отбросить ввод, пока не найдете эту новую строку и не спросите еще раз.
Это связано с простым принципом:
⟶ Пользователь всегда нажимает Enter в конце каждого запрошенного ввода ⟵
Как только будет получена полная строка текста, попытайтесь преобразовать ее в int
или что-то еще.
«Пользователь всегда нажимает Enter в конце каждого запрошенного ввода» -> это, безусловно, распространенное явление. Другие возможности: 1) Вход закрыт - финала нет '\n'
. например ввод был "abc"
, а затем был сигнализирован конец файла без '\n'
. 2) Редко: при вводе имеется ошибка ввода.
И то, и другое можно считать ненормальной реакцией (и, следовательно, ошибочной) на такие вещи, как «What is your age?
». Если TTY задает вам вопрос, он всегда должен ввести ответ, а затем нажать Enter, чтобы подтвердить его. Также следует ожидать, что конвейерные процессы или файлы, притворяющиеся людьми в этом контексте, будут отвечать как человек; файл, заканчивающийся без новой строки, здесь считается ошибкой.
Я мог видеть, как идти этим путем. Тем не менее, по моему опыту, я обнаружил, что терпимость к вводу, заканчивающемуся концом файла (и отсутствие обязательного '\n'
), делает программное обеспечение более полезным и требует меньше обслуживания. Ваш звонок.
Как предотвратить переполнение буфера, когда мы рекурсивно запрашиваем ввод, если ввод недействителен?
n
символов (что составляет n + 1
символов, если считать Enter или '\n'
), прочитайте хотя бы строку, содержащую не менее n + 1
символов. При использовании fgets()
это должно поместиться в буфер размером n + 2
. Дополнительная единица предназначена для хранения добавленного нулевого символа.#define STRING_LENGTH_MAX 5
char buf[STRING_LENGTH_MAX + 2];
for (;;) {
if (fgets(buf, sizeof buf, stdin) == NULL) {
// There is no more input from stdin.
// TBD code - perhaps quit?
}
...
'\n'
. Если нашли, отрежьте его и продолжайте. Если он не найден и строка длинная, прочитайте оставшуюся часть строки. Отбросить все и вернуться (не рекурсивно) к шагу 1. buf[strcspn(buf, "\n")] == '\0'; // Lop off a potential '\n'.
if (strlen(buf) <= STRING_LENGTH_MAX) {
// Perform other validation tests on `buf`.
// Otherwise ...
break;
}
// Read and toss rest of line.
int ch;
while ((ch = getchar()) != '\n' && ch != EOF) {
;
}
}
Определите буфер гораздо большей длины, например #define STRING_MAX_LENGTH 2048
. Затем обработайте введенные строки слишком большой (или недостаточной) длины. Высоки шансы, что таким образом вам не удастся получить частичную линию.
@JonathanLeffler Да, больший буфер снижает вероятность необходимости очистки while ((ch = getchar()) != '\n' && ch != EOF)
, но не устраняет ее. Как правило, когда буфер n
является ожидаемым максимальным, я кодирую 2*n
.
Не используйте рекурсию, используйте цикл.