Какой лучший способ получить вход от пользователя в C? - PullRequest
38 голосов
/ 14 февраля 2012

Многие люди говорили, что scanf не следует использовать в «более серьезных программах», как и в getline.

Я начал теряться: если каждая функция ввода, которую я встречал, говорила, что я не должен использовать ни одну из них, то что я должен использовать? Есть ли более «стандартный» способ получения информации, о которой я не знаю?

Ответы [ 4 ]

35 голосов
/ 14 февраля 2012

Как правило, 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() может не прочитать всю строку, если строка больше вашего буфера, что в «серьезной» программеэто то, что вы должны учитывать.

11 голосов
/ 14 февраля 2012

Для простого ввода, где вы можете установить фиксированный предел длины ввода, я бы рекомендовал читать данные с терминала с помощью <a href="http://linux.die.net/man/3/fgets" rel="noreferrer">fgets()</a>.

Это потому, что fgets() позволяет вам указать размер буфера (в отличие от gets(), который по этой самой причине должен в значительной степени никогда использоваться для чтения ввода от людей):

char line[256];

if(fgets(line, sizeof line, stdin) != NULL)
{
  /* Now inspect and further parse the string in line. */
}

Помните, что он сохранит, например, символы перевода строки, что может быть удивительным.

ОБНОВЛЕНИЕ : Как указано в комментарии, есть лучшая альтернатива, если вы согласны взять на себя ответственность за отслеживание памяти: <a href="http://linux.die.net/man/3/getline" rel="noreferrer">getline()</a>. Это, вероятно, лучшее решение общего назначения для кода POSIX, поскольку оно не имеет статического ограничения на длину строк, которые нужно прочитать.

5 голосов
/ 14 февраля 2012

Существует несколько проблем с использованием scanf:

  • чтение текста с простым %s спецификатором преобразования имеет тот же риск, что и использование gets();если пользователь вводит строку, которая длиннее, чем размер целевого буфера для хранения, вы получите переполнение буфера;

  • при использовании %d или %f для чтениячисловой ввод, некоторые неправильные шаблоны не могут быть полностью перехвачены и отклонены - если вы читаете целое число с %d и пользователь вводит "12r4", scanf преобразует и присваивает 12, оставляя r4 во входном потоке, чтобы запутать следующее чтение;

  • некоторые спецификаторы преобразования пропускают начальные пробелы, другие нет, и если не принять это во внимание, это может привести к проблемам, когда некоторые входные данныеполностью пропущен;

В основном, требуется лот дополнительных усилий для пуленепробиваемого чтения с использованием scanf.

Хорошей альтернативой является чтениевсе вводятся как текст с использованием fgets(), а затем токенизируются и преобразуются с использованием sscanf или комбинаций strtok, strtol, strtod и т. д.

2 голосов
/ 14 февраля 2012

Используйте fgets для получения данных и sscanf (или другой метод) для их интерпретации.

Смотрите эту страницу, чтобы узнать, почему лучше использовать fgets + sscanf, а не scanf

http://c -faq.com / STDIO / scanfprobs.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...