使用scanf接受用户输入

6

gcc 4.4.2

我正在阅读一篇关于scanf的文章。个人来说,我从未检查过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读取输入。但是,如果我不知道用户将输入什么,我怎么知道该解析什么。例如,如果用户输入包含数字和字符并且顺序任意的地址,我该如何处理?


3
从任何输入例程中永远不检查返回代码总是非常可疑的 - 你必须很幸运。简短的答案是“我不使用 scanf()fscanf();偶尔使用 sscanf(),但要小心行事”。 - Jonathan Leffler
更新了我的答案后,如果您不确定用户将输入什么数据,通常如何使用sscanf来解析数据?谢谢。 - ant2009
5个回答

6
不要直接使用scanf,因为它很难使用。最好先读取整行输入,然后解析它(可能使用sscanf)。
阅读来自comp.lang.c FAQ的这篇文章(以及其引用的文章): http://c-faq.com/stdio/scanfprobs.html 编辑: 好的,为了回答你自己编辑中的额外问题:如果你允许非结构化输入,则需要尝试以多种方式解析字符串,直到找到有效匹配为止。如果无法找到有效匹配,则应拒绝该输入并再次提示用户,可能会说明所需输入的格式。
对于任何更复杂的内容,您可能最好使用正则表达式库,甚至使用专用词法分析器/解析器工具包(例如flex和bison)。

你好,我已经更新了我的答案。但是我不确定使用sscanf进行解析是否正确?谢谢。 - ant2009

5
我在进行用户交互时不使用scanf(),而是使用fgets()将所有内容都读取为文本,然后根据需要解析输入内容,使用strtol()strtod()将文本转换为数字值。
一个scanf()无法处理的例子是当用户输入错误的数字值,但是其前面部分是有效的,类似于以下情况:
if (scanf("%d", &num) == 1)
{
  // process num
}
else
{
  // handle error
}

如果用户输入"12e4",scanf()将成功地将"12"转换并赋值给num,留下"e4"在输入流中以后读取时会出现问题。整个输入应该被视为虚假的,但scanf()无法捕获这种错误。然而,如果我做如下操作:
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() 是一个很好的工具,如果你可以保证你的输入总是格式良好的。


你好。假设你想让用户输入他们的地址,这里面会有数字和字母。你可以使用fgets来读取输入。但是你该如何解析这个输入呢?我认为你可以使用fgets将其读入缓冲区中,然后就完成了。我不知道如何使用sscanf来处理它。谢谢。 - ant2009
我会将邮寄地址视为所有文本,可能分成几个字段(地址、街道名称、城市、州、邮政编码等)。什么时候以及如何对其进行标记化取决于应用程序(例如,如果我需要根据邮政编码或街道名称搜索记录)。如果我只需要将其存储以便稍后显示,我可能甚至不会费心对其进行标记化。 - John Bode

2

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
我使用循环调用fgets直到读取完整行,然后调用sscanf解析数据。最好检查sscanf是否到达输入行的末尾。

1

我很少使用scanf。大多数情况下,我使用fgets()将数据读取为字符串。然后,根据需要,我可能会使用sscanf()或其他函数,例如strto*函数族、str*chr()等,从字符串中获取数据。

如果我使用scanf()fgets()+sscanf(),我总是检查函数的返回值,以确保它们执行了我想要的操作。我也不使用strtok()来分解字符串,因为我认为strtok()的接口有问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接