Использование scanf для подтверждения ввода пользователя - PullRequest
5 голосов
/ 27 января 2010

gcc 4.4.2

Я читал статью о scanf. Лично я никогда не проверял код возврата сканфа.

#include <stdio.h>

int main(void)
{
  char buf[64];
  if(1 == scanf("%63s", buf))
  {
    printf("Hello %s\n", buf);
  }
  else
  {
    fprintf(stderr, "Input error.\n");
  }
  return 0;
}

Мне просто интересно, какие еще методы используют опытные программисты, когда они используют scanf, когда хотят получить пользовательский ввод? Или они используют другую функцию или пишут свою собственную?

Спасибо за любые предложения,

РЕДАКТИРОВАТЬ =========

#include <stdio.h>

int main(void)
{
    char input_buf[64] = {0};
    char data[64] = {0};

    printf("Enter something: ");
    while( fgets(input_buf, sizeof(input_buf), stdin) == NULL )
    {
    /* parse the input entered */
    sscanf(input_buf, "%s", data);
    }

    printf("Input [ %s ]\n", data);

    return 0;
}

Я думаю, что большинство программистов согласны с тем, что scanf плох, и большинство соглашается использовать fgets и sscanf. Тем не менее, я могу использовать fgets для чтения ввода. Однако, если я не знаю, что пользователь введет, как я знаю, что анализировать. Например, как если бы пользователь должен был ввести свой адрес, который будет содержать цифры и символы, и в любом порядке?

Ответы [ 5 ]

6 голосов
/ 27 января 2010

Не используйте scanf напрямую. Это удивительно сложно в использовании. Лучше прочитать всю строку ввода и затем проанализировать ее (возможно, с помощью sscanf).

Прочтите эту запись (и записи, на которые она ссылается) из часто задаваемых вопросов по comp.lang.c: http://c -faq.com / STDIO / scanfprobs.html

Edit: Хорошо, чтобы ответить на ваш дополнительный вопрос из вашего собственного редактирования: если вы разрешите неструктурированный ввод, вам придется пытаться анализировать строку несколькими способами, пока не найдете подходящий. Если вы не можете найти правильное соответствие, то вам следует отклонить ввод и снова запросить пользователя, возможно, объяснив, в каком формате вы хотите, чтобы ввод был.

Для чего-то более сложного, вам, вероятно, будет лучше использовать библиотеку регулярных выражений или даже использовать специальные наборы инструментов лексера / анализатора (например, flex и bison).

5 голосов
/ 27 января 2010

Я не использую scanf() для интерактивного ввода данных пользователем; Я читаю все как текст, используя fgets(), а затем анализирую ввод по мере необходимости, используя strtol() и strtod() для преобразования текста в числовые значения.

Один из примеров, где scanf() падает, - это когда пользователь вводит неверное числовое значение, но его начальная часть действительна, что-то вроде следующего:

if (scanf("%d", &num) == 1)
{
  // process num
}
else
{
  // handle error
}

Если пользователь вводит «12e4», scanf() успешно преобразует и присваивает «12» num, оставляя «e4» во входном потоке, чтобы запутать будущее чтение. Вход весь должен рассматриваться как фальшивый, но scanf() не может уловить такую ​​ошибку. OTOH, если я сделаю что-то вроде:

if (fgets(buffer, sizeof buffer, stdin))
{
  int val;
  char *chk;
  val = (int) strtol(buffer, &chk, 10);
  if (!isspace(*chk) && *chk != 0)
  {
    // non-numeric character in input; reject it completely
  }
  else
  {
    // process val
  }
}

Я могу отловить ошибку на входе и отклонить ее перед использованием любой ее части. Это также лучше не оставляет мусор во входном потоке.

scanf() - отличный инструмент , если , вы можете гарантировать, что ваш вклад всегда будет корректным.

2 голосов
/ 27 января 2010

scanf () имеет проблемы, в том случае, если от пользователя ожидается, что он введет целое число, а вместо этого наберет строку, часто программа взрывается. Этого можно избежать, прочитав весь ввод как строку (используйте getchar ()), а затем преобразовав строку в правильный тип данных.

/* example one, to read a word at a time */
#include <stdio.h>
#include <ctype.h>
#define MAXBUFFERSIZE   80

void cleartoendofline( void );  /* ANSI function prototype */

void cleartoendofline( void )
{
    char ch;
    ch = getchar();
    while( ch != '\n' )
        ch = getchar();
}

main()
{
    char    ch;                     /* handles user input */
    char    buffer[MAXBUFFERSIZE];  /* sufficient to handle one line */
    int     char_count;             /* number of characters read for this line */
    int     exit_flag = 0;
    int     valid_choice;

    while( exit_flag  == 0 ) {
        printf("Enter a line of text (<80 chars)\n");
        ch = getchar();
        char_count = 0;
        while( (ch != '\n')  &&  (char_count < MAXBUFFERSIZE)) {
            buffer[char_count++] = ch;
            ch = getchar();
        }
        buffer[char_count] = 0x00;      /* null terminate buffer */
        printf("\nThe line you entered was:\n");
        printf("%s\n", buffer);

        valid_choice = 0;
        while( valid_choice == 0 ) {
            printf("Continue (Y/N)?\n");
            scanf(" %c", &ch );
            ch = toupper( ch );
            if((ch == 'Y') || (ch == 'N') )
                valid_choice = 1;
            else
                printf("\007Error: Invalid choice\n");
            cleartoendofline();
        }
        if( ch == 'N' ) exit_flag = 1;
    }
}
1 голос
/ 27 января 2010

Я редко использую scanf. В большинстве случаев я использую fgets() для чтения данных в виде строки. Затем, в зависимости от необходимости, я могу использовать sscanf() или другие функции, такие как strto* семейство функций, str*chr() и т. Д., Для получения данных из строки.

Если я использую scanf() или fgets() + sscanf(), я всегда проверяю возвращаемые значения функций, чтобы убедиться, что они сделали то, что я от них хотел. Я также не использую strtok() для токенизации строк, потому что я думаю, что интерфейс strtok() не работает.

1 голос
/ 27 января 2010

Я выполняю вызов цикла fgets, пока не будет прочитан конец строки, а затем вызываю sscanf для анализа данных Рекомендуется проверить, достигает ли sscanf конец строки ввода.

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