Ваш код вызывает Неопределенное поведение , если пользователь вводит более 18-characters
и нажимает Введите .Зачем?Вы объявляете char name[20];
, что означает, что существует только хранилище для 20-characters
всего (включая нуль-завершающий символ).Тем не менее, вы тогда скажете fgets
прочитать до 200
символов с fgets(name,200,stdin);
.
fgets
будет делать то, что вы сказали.Если ваш пользователь введет 19-characters
, fgets
попытается записать 21-characters
в name
, записав за пределы name
и вызвав Неопределенное поведение .
Почему 21-characters
, если пользователь вводит только 19-characters
?
Вспомните, что происходит, когда пользователь нажимает Ввод , указывая конец ввода.fgets
(фактически все строковые входные функции, включая POSIX getline
) будут включать '\n'
в заполненный буфер (при условии, что достаточно места) - и вы сказали fgets
Есть 200-characters
доступны.Тем не менее, name
имеет всего 20
, например,
20 characters total
first +---+---+---+---+---+
15 ... | . | . | x | \n| \0|
characters +---+---+---+---+---+
. Чтобы решить вашу проблему, вы всегда должны убедиться, что ваше хранилище равно или превышает количество символов, которое выскажи fgets
читать.Как правило, не экономит на размере буфера .Если вы планируете читать 20-characters
как name
, тогда предоставьте буфер, по крайней мере, вдвое длиннее самого длинного имени, которое вы можете найти.Я бы предпочел, чтобы длина была 10 000 байт, а длина - 1 байт ...
Чтобы получить ввод, вы управляете циклом ввода с помощью возврата функции ввода (либо в условном цикле, либо какусловие внутри цикла), которое будет обрабатывать EOF
, пустой ввод, а также слишком длинную длину.
Когда у вас есть определенные условия, которые вы хотите, чтобы пользовательский ввод выполнял, непрерывно зацикливаясь, пока пользователь не введет соответствующийВвод (или отмена с помощью EOF
) обычно является хорошим подходом, например,
#define MAXC 256 /* if you need a constant, #define one (or more) */
...
for (;;) { /* loop continually until good input or user cancels */
fputs ("enter name: ", stdout); /* prompt */
if (!fgets (name, MAXC, stdin)) { /* handle manual EOF */
fputs ("(user canceled)\n", stdout);
return 1;
}
...
}
В цикле вы просто проверяете длину name
с помощью strlen
и обрезаете '\n'
изконец буфера.Если длина превышает 19-characters
, просто обработайте ошибку и continue
запросите другую запись.
В целом, вы можете сделать что-то похожее на:
#include <stdio.h>
#include <string.h>
#define MAXC 256 /* if you need a constant, #define one (or more) */
#define MAXN 20
int main (void) {
char name[MAXC] = {0};
size_t len = 0;
for (;;) { /* loop continually until good input or user cancels */
fputs ("enter name: ", stdout); /* prompt */
if (!fgets (name, MAXC, stdin)) { /* handle manual EOF */
fputs ("(user canceled)\n", stdout);
return 1;
}
len = strlen (name); /* get length of name */
if (len > MAXN || *name == '\n') { /* exceeds length or empty */
fputs (" error: name exceeds 20 char or empty\n", stderr);
continue;
}
else { /* good name, trim \n, break loop */
if (len && name[len - 1] == '\n') /* check last char '\n' */
name[--len] = 0; /* overwrite '\n' with \0 */
break;
}
}
printf ("\nname: %s (%zu chars)\n", name, len);
return 0;
}
( примечание: выше, если пользователь генерирует руководство EOF
с Ctrl + d или Ctrl + z на окнах, чтение останавливается и программа завершается. Если длинапревышает желаемую длину, или если пользователь просто нажимает Enter , что приводит к буферу, содержащему только '\n'
[из пустой строки ниже], генерируется ошибка, и пользователь просит повторно ввести имя.)
Пример использования / Вывод
При выполнении процедуры ввода вводится 20 символов, затем Ввод только и, наконец, действительный 19-строка символов.
$ ./bin/namelen
enter name: 12345678901234567890
error: name exceeds 20 char or empty
enter name:
error: name exceeds 20 char or empty
enter name: 1234567890123456789
name: 1234567890123456789 (19 chars)
Вы можете отрегулировать длину по своему усмотрению.
Если пользователь отменяет действие в любой точке, перед выходом отображается следующее:
$ ./bin/namelen
enter name: (user canceled)
В качестве альтернативы вызову strlen
, а затем вручную проверьте, включен ли '\n'
В буфере вы можете использовать strcspn
, чтобы получить длину и обрезать конечный '\n'
, например:
for (;;) { /* loop continually until good input or user cancels */
fputs ("enter name: ", stdout); /* prompt */
if (!fgets (name, MAXC, stdin)) { /* handle manual EOF */
fputs ("(user canceled)\n", stdout);
return 1;
}
name[(len = strcspn (name, "\n"))] = 0; /* save len, trim '\n' */
if (len > MAXN - 1 || !*name) { /* exceeds length/empty */
fputs (" error: name exceeds 20 char or empty\n", stderr);
continue;
}
else /* good name, break loop */
break;
}
Дайте мне знать, если у вас есть вопросы.Правило большого пальца - Не экономьте на размере буфера!