Каков наилучший способ получить вход от пользователя в C?
Многие говорили, что scanf
не следует использовать в "более серьезной программе", как и в случае с getline
.
Я начал теряться: если каждая входная функция, которую я встречал, сказала, что я не должен использовать их, то что мне использовать? Есть ли более "стандартный" способ получить ввод, о котором я не знаю?
Ответы
Ответ 1
Как правило, fgets()
считается хорошим вариантом. Он считывает целые строки в буфер, а оттуда вы можете делать то, что вам нужно. Если вам нужно поведение типа scanf()
, вы можете передать строки, которые вы читаете, в sscanf()
.
Основным преимуществом этого является то, что если строка не может конвертироваться, ее легко восстановить, тогда как с scanf()
вы останетесь с вводом на stdin
, который вам нужно слить. Кроме того, вы не закроетесь в ловушке смешения линейно-ориентированного ввода с помощью scanf()
, что вызывает головные боли, когда такие вещи, как \n
, остаются на stdin
, обычно ведущие новые кодеры, полагая, что входные вызовы были полностью проигнорированы.
Что-то вроде этого может быть по вашему вкусу:
char line[256];
int i;
if (fgets(line, sizeof(line), stdin)) {
if (1 == sscanf(line, "%d", &i)) {
/* i can be safely used */
}
}
Вы должны заметить, что fgets()
возвращает NULL
в EOF или ошибке, поэтому я завернул его в if
. Вызов sscanf()
возвращает количество успешно преобразованных полей.
Имейте в виду, что fgets()
может не читать целую строку, если строка больше, чем ваш буфер, что в "серьезной" программе, безусловно, вы должны рассмотреть.
Ответ 2
Для простого ввода, где вы можете установить фиксированный предел для входной длины, я бы рекомендовал считывать данные с терминала с помощью fgets()
.
Это связано с тем, что fgets()
позволяет указать размер буфера (в отличие от gets()
, который по этой причине почти никогда не будет использоваться для чтения данных от людей):
char line[256];
if(fgets(line, sizeof line, stdin) != NULL)
{
/* Now inspect and further parse the string in line. */
}
Помните, что он сохранит, например. символ перевода строки, что может быть неожиданным.
ОБНОВЛЕНИЕ: Как указано в комментарии, есть лучшая альтернатива, если вы будете получать ответственность за отслеживание памяти: getline()
. Это, вероятно, лучшее универсальное решение для кода POSIX, поскольку оно не имеет никакого статического предела длины строк, которые нужно читать.
Ответ 3
Существует несколько проблем с использованием scanf
:
-
чтение текста с помощью простого спецификатора преобразования %s
имеет тот же риск, что и при использовании gets()
; если пользователь вводит строку, длина которой превышает размер целевого буфера, вы получите переполнение буфера,
-
если вы используете %d
или %f
для чтения числового ввода, некоторые плохие шаблоны не могут быть пойманы и полностью отвергнуты - если вы читаете целое число с %d
, а пользовательские типы "12r4
", scanf
преобразует и назначает 12
, оставляя r4
во входном потоке, чтобы сгладить следующее чтение;
-
некоторые спецификаторы преобразования пропускают ведущие пробелы, другие - нет, и неспособность принять это во внимание может привести к проблемам, когда какой-либо ввод пропускается полностью;
В основном, при использовании scanf
требуется много дополнительных усилий для чтения с пуленепробиванием.
Хорошей альтернативой является чтение всего ввода в виде текста с помощью fgets()
, а затем токенизация и преобразование ввода с использованием sscanf
или комбинаций strtok
, strtol
, strtod
и т.д.
Ответ 4
Используйте fgets
для получения данных и используйте sscanf
(или другой метод) для их интерпретации.
См. эту страницу, чтобы узнать, почему лучше использовать fgets
+ sscanf
, а не scanf
http://c-faq.com/stdio/scanfprobs.html