检查输入是否为数字,如果不是则返回输入

时间:2012-03-24 21:09:52

标签: c input

所以,我是这个网站的新手,我是编程新手。我在我的第一本书,我的父亲做了一个“功课”,他不知道做这个的代码,但我想做。所以这就是我想做的事情: 我想创建一个像计算器的程序,你输入一个值A和一个值B,然后程序显示A + B的结果。我已经这样做了,问题是如果我输入一个字符,程序会继续读取其他代码。这是我写的,也许你会更好地理解。

#include <stdio.h>
#include <conio.h>
#include <ctype.h>
void main(void)
  {
   int val_a;
   int val_b;
   int result;

   printf("Write a number A=");
   scanf("%d", &val_a);
   if(isdigit(val_a))
   {
    printf("\nWrite a number B=");
    scanf("%d", &val_b);
   }
else
  printf("\nI said number.\n");
result = val_a + val_b;
printf("%d + %d = %d", val_a, val_b, result);
getch();
printf("\033[2J");   \\ to clear the screen
}

所以我想要的是程序将回到我必须输入数字A的地方。 我在google上找到了关于标签的内容,但我什么都不懂:D。也许有人可以发布我想要的例子,以便我可以研究它。

我应该写我的名字吗?也许下次:P

PS:我希望有人会在我写的内容中找到NOOB错误并告诉我。 :祈祷:

4 个答案:

答案 0 :(得分:2)

批判

#include <stdio.h>
#include <conio.h>

所以你正在使用Windows。

#include <ctype.h>
void main(void)

main()的返回类型应为int,即使您发现了许多相反的例子。

{
    int val_a;
    int val_b;
    int result;

    printf("Write a number A=");
    scanf("%d", &val_a);

您应该测试scanf()的返回值,看它是否能够读取数字。

    if (isdigit(val_a))

此测试&#39;工作&#39;但它会检查输入的数字是否为48..57范围内的值(ASCII,CP1252,Unicode,ISO 8859-1等代码数字'0''9')。这可能不是你的想法。

    {
        printf("\nWrite a number B=");
        scanf("%d", &val_b);

用户输入了换行符,因此不需要printf()格式字符串前面的换行符,尽管它也没有害处。关于检查scanf()的相同评论也适用于此。

    }
    else
        printf("\nI said number.\n");

做错误检查很好。但是,即使您检测到错误,也会继续向前。你真的需要停下来。

    result = val_a + val_b;
    printf("%d + %d = %d", val_a, val_b, result);

总的来说,最好用换行符结束行。它将数据刷新到文件或屏幕。

    getch();
    printf("\033[2J");   \\ to clear the screen

printf()格式字符串不可移植。鉴于您包含<conio.h>,您应该使用clrscr()(可能会将该转义序列或等效项发送到终端)。我不相信有必要在程序结束时清除屏幕,但我主要在Unix系统上工作,而不是在Windows上工作。

}

鉴于MSVC仍然是C89编译器,您应该在程序结束时包含return 0;以返回成功状态。

重写

添加所有更改后,您最终会:

#include <stdio.h>
#include <conio.h>

int main(void)
{
    int val_a;
    int val_b;
    int result;

    printf("Write a number A=");
    if (scanf("%d", &val_a) != 1)
    {
        printf("I said number.\n");
        getch();
        clrscr();
        return(1);
    }
    printf("\nWrite a number B=");
    if (scanf("%d", &val_b) != 1)
    {
        printf("I said number.\n");
        getch();
        clrscr();
        return(1);
    }

    result = val_a + val_b;
    printf("%d + %d = %d\n", val_a, val_b, result);
    getch();
    clrscr();
    return(0);
}

当您了解编写函数时,可以将其压缩为:

#include <stdio.h>
#include <conio.h>

static int get_a_number(const char *prompt)
{
    int value;
    printf(prompt);
    if (scanf("%d", &value) != 1)
    {
        printf("I said number.\n");
        getch();
        clrscr();
        exit(1);
    }
    return value;
}

int main(void)
{
    int val_a  = get_a_number("Write a number A=");
    int val_b  = get_a_number("Write a number B=");
    int result = val_a + val_b;
    printf("%d + %d = %d\n", val_a, val_b, result);
    getch();
    clrscr();
    return(0);
}

get_a_number()函数不是通用函数,但它在此上下文中很有用,因为它封装了一段共同的代码。

  
      
  • 子程序调用允许我们总结参数列表中的违规行为
  •   
  • 子程序本身总结了代码的规则性
  •   

这是Kernighan&amp; amp; Plauger,&#34;编程风格的元素&#34;。


使用atexit()

上面的代码已更新为在所有退出路径中包含getch()clrscr()(并删除#include <ctype.h>,因为其中的所有功能都不再使用)。重复写出两个函数调用是一件麻烦事。避免该问题的另一种方法是编写一个在程序退出时调用的函数;您使用atexit()函数注册它,该函数在<stdlib.h>

中声明
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

static void wait_at_exit(void)
{
    getch();
    clrscr();
}

static int get_a_number(const char *prompt)
{
    int value;
    printf(prompt);
    if (scanf("%d", &value) != 1)
    {
        printf("I said number.\n");
        exit(1);
    }
    return value;
}

int main(void)
{
    atexit(wait_at_exit);
    int val_a  = get_a_number("Write a number A=");
    int val_b  = get_a_number("Write a number B=");
    int result = val_a + val_b;
    printf("%d + %d = %d\n", val_a, val_b, result);
    return(0);
}

main()中的代码假定您可以任意混合语句(例如atexit())和声明(例如int val_a)。这是标准C的当前(2011)和旧版本(1999)允许的,但标准C的原始(1989)版本不允许这样做。我相信MSVC遵守原始标准。如果是这样,您可能需要将main()写为:

int main(void)
{
    atexit(wait_at_exit);
    {
    int val_a  = get_a_number("Write a number A=");
    int val_b  = get_a_number("Write a number B=");
    int result = val_a + val_b;
    printf("%d + %d = %d\n", val_a, val_b, result);
    }
    return(0);
}

内部块(内部大括号)内的代码是否应该缩进一个额外的级别是一个很好的讨论点。您始终可以在括号中包含的代码块的开头声明变量,这样我就可以将调用写入atexit(),然后定义并使用这些变量。

如果你读到敏捷编程,你会发现其中一个咒语是:

  • 干 - 不要重复自己

上面重写以避免重复getscr()次调用可被视为该咒语的应用。与get_a_number()函数类似;它适用于DRY口头禅。


再试一次

如果您希望程序返回并在用户输入错误时再次提示,则需要修改get_a_number()功能。并且,因为函数变得更加复杂,所以肯定需要为作业使用函数。您还遇到scanf()的问题;它不允许你每行强制执行一个数字。如果您在响应第一个提示时键入1 2,则第一次调用get_a_number()将读取一个空白,停止解析(并将空白放回以供重用),并返回值1 。对get_a_number()的第二次调用将输出提示,但返回值为2,而用户不再输入任何内容。如果这不是您想要的行为,那么您必须安排阅读整个第一行并扫描该数字,然后丢弃其余部分。令人担忧的另一个原因。如果用户键入a而不是1,则scanf()将永远不会读取任何内容;它将进入循环,发现a无效为数字,返回0转换完成,您的代码可能会以无限循环结束,提示输入并读取a 。这类问题是许多人(特别是我)避免使用scanf()的原因。

避免上述大多数问题的最简单方法是fgets()sscanf()

enum { MAX_LINELENGTH = 4096 };
enum { MAX_ATTEMPTS   = 10   };

static int get_a_number(const char *prompt)
{
    int value;
    char line[MAX_LINELENGTH];
    int count = 0;
    while (fputs(prompt, stdout) != EOF &&
           fgets(line, sizeof(line), stdin) != 0)
    {
        if (sscanf(line, "%d", &value) == 1)
            return value;
        if (count++ > MAX_ATTEMPTS)
        {
            printf("I give in; I don't understand what you're typing\n");
            exit(1);
        }
        printf("I said please enter a number.\n");
    }

    printf("Oops: I got EOF or an error; goodbye!\n");
    exit(1);
}

这涵盖了很多问题。通常,回显错误的数据是个好主意。你可以这样做:

        printf("I didn't understand what you meant when you typed:\n%s\n"
               "I said please enter a number.\n", line);

这使用&#39;相邻字符串连接&#39;,并向用户显示程序收到的内容,这通常可以帮助用户理解错误。

我使用fputs(prompt, stdout)代替printf(prompt)来输出提示。两者都可以使用(printf(prompt) != 0将是测试),但fputs()略有优势,因为如果有人设法颠覆您的程序并在提示中获得%,{ {1}}将尝试处理未传递的参数,而printf()根本不解释其参数字符串。在这里,差异可以忽略不计;我可以合理地认为我过于复杂化了。但是程序的安全性也是你最终学习的东西,并且诸如&#34;之类的技术不会使用fputs()&#34;可以帮助您的程序更具弹性。 (实际上,复杂性是解释;代码非常简单。)

如果在printf(variable)之后和fflush()之前有fputs(),那么另一个详细的讨论点是什么?&#39;可以添加额外的条件&#39; fgets()到循环控件,但在实践中通常是不必要的。如果标准输入来自终端之外的其他东西并且标准输出转到文件,那么它只会产生影响。所以,我把它留了出来。

循环中的计数器可防止出现严重错误 - 程序退出而不是永远停留,令无法正确输入数据的用户感到沮丧。你有没有一个程序弹出一个对话框,你只能在其中点击“确定”。按钮(即使它不正常),然后再次使用同一个盒子,一遍又一遍地回来?我希望不是为了你。我有;当它发生时它会让我感到沮丧。

你可以看到为什么在fflush(stdout) == 0 &&中只用一次而不是两次写代码是有利的;这将是很多重复。

答案 1 :(得分:1)

您需要检查scanf()是否已成功读取应阅读的输入。通常,您应该在阅读后确认读取成功。 scanf()返回成功读取的输入数,这些输入基本上映射到它可以成功读取的格式说明符的数量。例如,你应该检查

if (scanf("%d, &val_a) == 1)) {
    ...
}

读取的值可能是或不是“数字”。通常,根据isdigit(),它可能不是数字,因为此函数仅对字符'0''1',...,'9'的整数值返回true。测试scanf()是否可以读取所有格式说明符应避免此问题。

如果scanf()失败,您可以阅读一个字符(使用例如fgetc())并再次重试或阅读整行(使用gets()但使用{ {1}})。您可能还希望打印输入不符合预期的消息。

答案 2 :(得分:0)

你的isdigit功能都错了。它意味着接收一个char参数,但在你的情况下,int会被转换,你可能会说降级。

该功能基于字母的ASCII码link here。你可以尝试这样看看。

char c = '1';
if(isdigit(c))
   printf("digit");

此外,您需要的是do{ /* something */ } while(boolean);

答案 3 :(得分:0)

你误解了isdigit函数的使用。它确定字符是否是诸如“0”,“1”,“2”等数字。但是,您已经使用scanf函数将输入转换为整数。因此,只需删除对isdigit的调用,然后检查scanf的返回代码,它将告诉您用户是否输入了有效数字。