C编程:如何检查用户是否仅在以下表达式中输入了数值?

C Programming: How to check if user has entered numeric values only in the following expression?

我采用以下格式将特定日期作为用户输入:mm/dd/yyyy。

我如何确定用户只输入了数字值,如果不是,则再次提示他输入有效日期。我已经在根据输入的值检查日期是否有效,但如果用户输入的值不完全是数字,那么程序就会崩溃。

请帮忙...

代码:

int x=0;
printf("\nEnter the date of birth(mm/dd/yyyy):");
do
{
    scanf("%d/%d/%d",&add.dob.month,&add.dob.day,&add.dob.year);
    if((add.dob.month<13 && add.dob.month>0) && (add.dob.day>0 && add.dob.day<32) && (add.dob.year<2016))
    {
        x=1;
    }
    else
    {
        printf("\nThe above date of birth is invalid.\nEnter a valid date of birth(mm/dd/yyyy):");
    }
}while(x!=1);

您想检查 scanf 的 return 值,它将 return 读取的输入数。
就说input1 input2和input3是ints

int isValid = 0;
while (!isValid)
{
    isValid = scanf("%d/%d/%d",&input1, &input2, &input3);
    if(isValid != 3)
    {
        printf("\nThe above date of birth is invalid.\nEnter a valid date of birth(mm/dd/yyyy):");
    }
    else
    {
        // the user entered integers
        if((add.dob.month<13 && add.dob.month>0) && (add.dob.day>0 && add.dob.day<32) && (add.dob.year<2016))
        {
        }
        else
        {
            isValid = 0; // it didn't pass your date validation but it did pass integer validation 
        }
    }
}

如果用户输入字符或字符串或任何非整数的内容,则 scanf 将 return 0 并且您可以使其循环。我在那里使用了一个非常糟糕的 if/else ,将 if 部分留空,否定每一个会更好,但这是另一回事。这个故事的寓意就是检查什么 scanf returns,如果它 returns > 1,用户输入数字,如果它 returns 0,那么他们尝试输入字母并且它失败。

您应该检查 scanf 的 return 值,看看它是否能够完成所有分配。如果没有,您需要在再次尝试之前消耗用户的输入,否则您将陷入无限循环,一遍又一遍地尝试读取相同的输入,但都失败了。例如:

do {
    printf("\nEnter the date of birth(mm/dd/yyyy): ");
    fflush(stdout);
    if (scanf("%d/%d/%d", &month, &day, &year) != 3) {
        scanf("%*[^\n]"); // consume line from input, do not assign
    } else if (month < 13 && month > 0 && day < 32 && day > 0 && year < 2016) {
        break;
    }
    printf("Invalid date!\n");
} while (!feof(stdin));

另外请注意,您缺少对日期有效性的检查,例如,用户仍然可以输入 02/30/-5。

edit:参考下面评论中的讨论,这里对corner cases的处理很差;假设这是一个旨在始终 运行 在本地终端上交互的程序。为了正确处理输入,我强烈建议从 scanf 切换到 fgets(+ 之后可能 sscanf)。然后应该检查 fgets 的 return 值并在那里处理错误,而不是可能有问题的 feof 检查。当然,scanf 解决方案可以通过正确检查不同的 return 值(例如 EOF)来变得更加迂腐,但我认为它不值得。

您可以检查scanf()return值来测试输入的三个值是否属于int类型。

代码:

int returnval = scanf("%d/%d/%d",&input1, &input2, &input3);
if (returnval == 3)
{
    //The input is correct.
}
else
{
   printf("The input is not correct.");
   //The input is not correct.
}

您可以扫描输入,然后通过像 strtol() 这样的函数解析它。 strtol() 会告诉你是全数字还是混合。 这将为您省去很多麻烦,最后您要做的就是检查数字边界。

请查看 atoi() — string to int

的答案

有几个问题。如果使用 scanf 检查其 return。除了数字有效性检查之外,您还需要在用户输入非整数的情况下刷新输入缓冲区的问题。虽然有多种方法可以做到这一点,但与您的方法保持一致的一种方法是简单地执行您正在执行的检查,然后使用第二个 do/while 循环手动刷新输入缓冲区。您还可以扭转您的逻辑并测试任何一个条件是否为真,从而强制另一个提示输入日期。如前所述,有很多方法可以解决这个问题。将此视为您收到的其他答案之一。

int c = 0;          /* value to test for end of input buffer */

printf("\nEnter the date of birth(mm/dd/yyyy):");

while ((scanf("%d/%d/%d",&add.dob.month,&add.dob.day,&add.dob.year) != 3) ||
       (add.dob.month < 1) || 
       (add.dob.month > 12 ) ||
       (add.dob.day < 1 ) ||
       (add.dob.day > 31) ||
       (add.dob.year < 1) || 
       (add.dob.year > 2015))
{
    printf("\nThe above date of birth is invalid.\nEnter a valid date of birth(mm/dd/yyyy):");
    do { c = getchar(); } while (c != '\n' && c != EOF);  /* flush input buffer */
}

如前所述,从 stdin 读取信息的一种更可靠的方法是使用 fgetsgetline 将日期作为字符串读入缓冲区,然后解析缓冲区中的月、日、年信息。这也可以通过多种方式完成。由于您最终将从字符转换为十进制,因此您也可以只使用 strtol 来解析和转换,而不是使用指针逐步降低字符串或使用 strtokstrsep 进行解析(因为无论如何,每个都需要在分离后转换为十进制)。这是一个提供相当可靠解决方案的快速示例。除了 mm/dd/yyyy:

之外,它还允许将日期输入为 mm-dd-yyyymm.dd.yyy
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>

#define SZDATE 12

int main (void) {

    int mm = 0;                     // month/day/year values
    int dd = 0;
    int yyyy = 0;
    int gooddate = 0;               // flag signifying valid date
    char datestr[SZDATE] = {0};     // buffer to hold input string
    char *p = NULL;                 // pointer to use with strtol
    char *endptr = NULL;            // end pointer for strtol
    long val = 0;                   // long value for strtol

    while (gooddate == 0)
    {
        printf("\nEnter the date of birth (mm/dd/yyyy): ");

        /* read string with fgets */
        fgets (datestr, SZDATE-1, stdin);

        /* test sufficient length */
        if (strlen (datestr) < 8) {
            printf ("\n insufficient date length entered, try again.\n");
            continue;
        }

        /* parse month value with strtol (with error checking) */
        errno = 0;
        val = strtol (datestr, &endptr, 10);
        if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
            || (errno != 0 && val == 0)
            || (endptr == datestr)
            || (*endptr != '/' && *endptr != '-' && *endptr != '.')
            || (val < 1)
            || (val > 12)) {
                printf ("\n invalid month entered, try again.\n");
                continue;
        }
        mm = (int) val;     /* set month on successful conversion   */

        p = ++endptr;       /* set p to start of day, reset endptr  */
        endptr = NULL;

        /* parse day value with strtol (with error checking) */
        errno = 0;
        val = strtol (p, &endptr, 10);
        if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
            || (errno != 0 && val == 0)
            || (endptr == p)
            || (*endptr != '/' && *endptr != '-' && *endptr != '.')
            || (val < 1)
            || (val > 31)) {
                printf ("\n invalid day entered, try again.\n");
                continue;
        }
        dd = (int) val;     /* set day on successful conversion     */

        p = ++endptr;       /* set p to start of year, reset endptr */
        endptr = NULL;

        /* parse year value with strtol (with error checking) */
        errno = 0;
        val = strtol (p, &endptr, 10);
        if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN))
            || (errno != 0 && val == 0)
            || (endptr == p)
            || (*endptr != '\n' && *endptr != 0)
            || (val < 1900)
            || (val > 2015)) {
                printf ("\n invalid year entered, try again.\n");
                continue;
        }
        yyyy = (int) val;   /* set year on successful conversion    */

        gooddate = 1;       /* set gooddate flag ending loop        */
    }

    printf ("\n valid date is: %d/%d/%d\n\n", mm, dd, yyyy);

    return 0;
}

仔细考虑与 strtol 转化相关的检查。您可能还有更多想要添加的内容。另外,尝试用 strtok 之类的解析并进行比较。这只是将读取字符串然后进行解析的一种方法。它可以通过多种不同的方式完成。