用scanf()读完整的句子

用scanf()读完整的句子,c,scanf,C,Scanf,我有以下功能: Customer_Account new_acc(){ Customer_Account new_account; new_account.account_id = account_id; account_id++; printf("\nEnter your name: "); scanf("%s", new_account.name); printf("Enter your date of birth (

我有以下功能:

Customer_Account new_acc(){
  Customer_Account new_account;
  new_account.account_id = account_id;
  account_id++;
  printf("\nEnter your name: ");
  scanf("%s", new_account.name);
  printf("Enter your date of birth (DD.MM.YYYY): ");
  scanf("%d.%d.%d", &new_account.birth_date.day, &new_account.birth_date.month, &new_account.birth_date.year);
  printf("\nEnter your ID number: ");
  scanf("%lf", &new_account.tc_ID_number);
  printf("\nEnter your address: ");
  scanf("%s", new_account.adress);
  printf("\nEnter your phone number: ");
  scanf("%lf", &new_account.phone);
  printf("\nEnter the amount you would like to deposit: ");
  scanf("%lf", &new_account.amount);
  printf("\n\033[1;32m Account successfully created\033[0m\n\n");
  sleep(1);
}
但当我运行它并输入名称和姓氏时,它会给出以下输出:

输入您的姓名:姓名
输入您的出生日期(年月日):
输入您的身份证号码:
输入您的地址:
输入您的电话号码:
如果我没有弄错的话,“\n”留在缓冲区中,因此它跳过下一个scanf()。我查找了一些解决方案,但它们也不起作用。当我只输入一个单词时,我不会遇到问题。如何阅读完整的句子而不碰到这个问题?

使用
scanf()
对新的C程序员来说充满了陷阱。特别是因为不同的转换说明符处理前导空格的方式不同。除了
%c
%[…]
(扫描集)和
%n
之外,其他所有内容都将忽略前导空格。此外,输入缓冲区中任何(表示任何)游离的非空白字符都将导致匹配失败,或者如果碰巧与下一个转换说明符匹配,它将被视为您的输入,而不是您想要的

此外,除非检查返回值以验证输入和转换是否成功,否则无法可靠地正确使用任何用户输入功能

为了消除与使用
scanf()
进行用户输入相关的所有缺陷,建议您使用面向行的输入函数,如
fgets()
(或POSIX
getline()
)一次读取整行输入。所有面向行的输入函数读取并在它们填充的缓冲区中包含尾随的
'\n'
。如果需要解析行中的值,只需将缓冲区传递到
sscanf()
,然后像使用
scanf()
一样提取值,除非您是从填充的缓冲区而不是
stdin
中读取。这样,无论转换成功与否,它都不会影响下一次尝试的输入——您使用
fgets()
消耗了整行输入

如果需要将读取的内容存储到缓冲区(字符数组),只需使用strcspn()修剪尾部的
'\n'
,例如

#define MAXC 1024
...
    char buf[MAXC];
    
    fputs ("enter some string: ", stdout);
    if (fgets (buf, MAXC, stdin) == NULL) {    /* validate EVERY input */
        fputs ("(user canceled input with manual EOF)\n", stdout);
        return 1;
    }
    
    buf[strcspn (buf, "\n")] = 0;       /* trim \n by overwriting with \0 */
这就是它的全部内容,用户输入不会有任何问题

在函数中进行输入时,必须提供有意义的返回类型,以指示用户输入是否成功。返回结构不提供输入是否成功的任何指示。相反,将返回类型设置为能够区分成功/失败的任何类型,并将指针作为参数传递给要填充的结构。返回类型为
int
是可以的,返回
0
表示失败,
1
表示成功(或者以您希望的方式执行,只要保持一致)

用户输入可能看起来枯燥和冗余,但您不是在创建艺术,而是在创建一个输入例程,它可以处理用户(或猫踩键盘)提供的任何输入,并且仍然在代码中生成正确(定义)的结果

考虑到这一点,您可以将输入例程更改为使用
fgets()
,以及类似以下内容:

#define MAXC 1024       /* if you need a constant, #define one (or more) */

/* pass pointer to account to fill so return can be used
 * to indicate success or failure of input routine,
 * return 0 on error or manual EOF, 1 on successful input.
 */
int new_acc (Customer_Account *new_account)
{
    char buf[MAXC];                         /* array to hold all user input */
    
    new_account->account_id = account_id;
    account_id++;
    
    /* name */
    fputs ("\nEnter your name: ");          /* no conversion, printf not required */
    if (fgets (buf, MAXC, stdin) == NULL) { /* validate read with fgets */
        fputs ("(user canceled input)\n", stderr);
        return 0;                           /* return 0 on failure */
    }
    buf[strcspn (buf, "\n")] = 0;           /* trim \n */
    strcpy (new_account->name, buf);        /* copy buf to new_account->name */
    
    /* date of birth */
    fputs ("Enter your date of birth (DD.MM.YYYY): ", stdout);
    if (!fgets (buf, MAXC, stdin)) {        /* same validation -- just shorthand */
        fputs ("(user canceled input)\n", stderr);
        return 0;                           /* return 0 on failure */
    }
    if (sscanf (buf, "%d.%d.%d", 
                &new_account->birth_date.day,
                &new_account->birth_date.month,
                &new_account->birth_date.year) != 3) {
        fputs ("error: DOB - input or matching failure.\n", stderr);
        return 0;
    }
    
    /* ID */
    fputs ("\nEnter your ID number: ", stdout);
    if (!fgets (buf, MAXC, stdin)) {        /* ditto */
        fputs ("(user canceled input)\n", stderr);
        return 0;                           /* return 0 on failure */
    }
    if (sscanf (buf, "%lf", &new_account->tc_ID_number) != 1) {
        fputs ("error: ID - input or matching failure.\n", stderr);
        return 0;
    }
    
    /* Address */
    fputs ("\nEnter your adress: ", stdout);
    if (!fgets (buf, MAXC, stdin)) {        /* ditto */
        fputs ("(user canceled input)\n", stderr);
        return 0;                           /* return 0 on failure */
    }
    buf[strcspn (buf, "\n")] = 0;           /* trim \n */
    strcpy (new_account->address, buf);     /* copy buf to new_account->name */
    
    /* Phone Number */
    fputs ("\nEnter your phone number: ");
    if (!fgets (buf, MAXC, stdin)) {        /* ditto */
        fputs ("(user canceled input)\n", stderr);
        return 0;                           /* return 0 on failure */
    }
    if (sscanf (buf, "%lf", &new_account->phone) != 1) {
        fputs ("error: Phone - input or matching failure.\n", stderr);
        return 0;
    }
    
    /* Amount */
    fputs ("\nEnter the amount you would like to deposit: ", stdout);
    if (!fgets (buf, MAXC, stdin)) {        /* ditto */
        fputs ("(user canceled input)\n", stderr);
        return 0;                           /* return 0 on failure */
    }
    if (sscanf (buf, "%lf", &new_account->amount) != 1) {
        fputs ("error: Amount - input or matching failure.\n", stderr);
        return 0;
    }
    
    return 1;        /* return 1 indicating successful input */
}
注意:我将删除出生日期中
%d”
之间的
,因为除非用户输入
否则将发生匹配失败——这可能是问题的根源)

它基本上对每个输入都做相同的事情——这很好。冗余——是的,实心——也是

使用
scanf()
读取字符串的另一个警告。如果不提供字段宽度修饰符以确保不超过数组的限制,
scanf()
并不比
get()
更安全,请参阅

您可以通过声明一个临时结构来填充调用函数并传递一个指向该函数的指针来使用输入例程,例如

    for (;;) {
        Customer_Account tmp;
        
        if (!new_acc (&tmp))
            break;
        
        /* assign tmp to final storage here */
        
        sleep (1);
    }
这大致相当于您的函数的工作方式-除了我们检查
new\u acc(&tmp)
的返回之外-我们知道输入是成功还是失败

仔细检查一下,如果您还有其他问题,请告诉我。

对于新的C程序员来说,使用
scanf()
充满了陷阱。特别是因为不同的转换说明符处理前导空格的方式不同。除了
%c
%[…]
(扫描集)和
%n
之外,其他所有内容都将忽略前导空格。此外,输入缓冲区中任何(表示任何)游离的非空白字符都将导致匹配失败,或者如果碰巧与下一个转换说明符匹配,它将被视为您的输入,而不是您想要的

此外,除非检查返回值以验证输入和转换是否成功,否则无法可靠地正确使用任何用户输入功能

为了消除与使用
scanf()
进行用户输入相关的所有缺陷,建议您使用面向行的输入函数,如
fgets()
(或POSIX
getline()
)一次读取整行输入。所有面向行的输入函数读取并在它们填充的缓冲区中包含尾随的
'\n'
。如果需要解析行中的值,只需将缓冲区传递到
sscanf()
,然后像使用
scanf()
一样提取值,除非您是从填充的缓冲区而不是
stdin
中读取。这样,无论转换成功与否,它都不会影响下一次尝试的输入——您使用
fgets()
消耗了整行输入

如果需要将读取的内容存储到缓冲区(字符数组),只需使用strcspn()修剪尾部的
'\n'
,例如

#define MAXC 1024
...
    char buf[MAXC];
    
    fputs ("enter some string: ", stdout);
    if (fgets (buf, MAXC, stdin) == NULL) {    /* validate EVERY input */
        fputs ("(user canceled input with manual EOF)\n", stdout);
        return 1;
    }
    
    buf[strcspn (buf, "\n")] = 0;       /* trim \n by overwriting with \0 */
这就是它的全部内容,用户输入不会有任何问题

当我