【问题标题】:C - Trying to scanf hex/dec/oct values to check if they're equal to user inputC - 尝试扫描十六进制/十进制/八进制值以检查它们是否等于用户输入
【发布时间】:2017-11-09 00:14:29
【问题描述】:

所以,我是 C 的新手,这是我第一次上课的项目。我基本上需要一个程序来询问用户他想要多少个问题,然后检索一个可以是 oct/dec/hex(它是随机的)的最大 8 位正数,然后要求用户将其转换为随机基数。例如,如果我得到一个十进制数,它会随机要求我将其转换为十六进制或八进制。在每个问题的结尾,它会说明我的转换是对还是错,并在程序结束时显示我做对了多少问题。

一切正常,直到我开始输入随机字母/字符时,它要求我转换为十六进制以外的字符。例如,如果它要求我将八进制转换为十进制,如果我输入一个字母,它有时会说它是对的,它还会跳过问题并继续循环,直到它得到一个十六进制。

我真的不知道我能做什么。这是我的代码:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
int main()
{
    int rightanswers = 0;
    int answer;
    int nquestions;

    printf("Number of questions:");
    scanf("%d", &nquestions);

    srand((unsigned int) time(NULL));
    unsigned char questions[nquestions];

    for (int i=1; i<=nquestions; i++)
    {
        questions[i] = (rand()%255)+1;
        int randomnumb = (rand()%6)+1;
        switch(randomnumb)
        {
            case 1:
                printf("\nConvert 0%o to base 10:", questions[i]);
                scanf("%d", &answer);
                if (answer == questions[i])
                {
                    rightanswers++;
                    printf("Right!");                                       
                }
                else
                {
                    printf("Wrong!");                       
                }
                break;
            case 2:
                printf("\nConvert 0%o to base 16:", questions[i]);
                scanf("%x", &answer);
                if (answer == questions[i])
                {
                    rightanswers++;
                    printf("Right!");                                       
                }
                else
                {
                    printf("Wrong!");                       
                }
                break;
            case 3:
                printf("\nConvert %d to base 8:", questions[i]);
                scanf("%o", &answer);
                if (answer == questions[i])
                {
                    rightanswers++;
                    printf("Right!");                                       
                }
                else
                {
                    printf("Wrong!");                       
                }
                break;
            case 4:
                printf("\nConvert %d to base 16:", questions[i]);
                scanf("%x", &answer);
                if (answer == questions[i])
                {
                    rightanswers++;
                    printf("Right!");                                       
                }
                else
                {
                    printf("Wrong!");                       
                }
                break;
            case 5:
                printf("\nConvert 0x%x to base 8:", questions[i]);
                scanf("%o", &answer);
                if (answer == questions[i])
                {
                    rightanswers++;
                    printf("Right!");                                       
                }
                else
                {
                    printf("Wrong!");                       
                }
                break;
            case 6:
                printf("\nConvert 0x%x to base 10:", questions[i]);
                scanf("%d", &answer);
                if (answer == questions[i])
                {
                    rightanswers++;
                    printf("Right!");                                       
                }
                else
                {
                    printf("Wrong!");                       
                }
                break;  
        }
    }
    printf("\nYou got %d conversions right!", rightanswers);
    return 0;
}

【问题讨论】:

  • 如果您从不验证scanf返回,那么您如何有信心从那时起处理有效数据?
  • @DavidC.Rankin 如何验证 scanf 的返回?我正在尝试查找它,但我越来越困惑!
  • 示例if (scanf("%d", &amp;answer) != 1) { /* handle error */ } 或更好,int rtn; rtn = scanf("%d", &amp;answer); if (rtn == EOF) { /* user canceled input (ctrl+d) */ } else if (rtn == 0) { /* handle matching or input failure */ } else if (rtn == 1) { /* good input -- do your thing */ }

标签: c


【解决方案1】:

从我的 cmets 继续。 scanf(和家族)给新的 C 程序员带来了无穷无尽的用户输入问题,因为与 scanf 相关的许多陷阱。主要是因为转换说明符在处理前导 whitepace 时表现不同(例如 spacetabnewline 等)以及程序员未能验证 return强>。返回验证很关键,因为输入缓冲区中剩下的内容(例如stdin)取决于转换是否成功。

scanf 返回成功转换的次数。 (例如scanf ("%s %d", strvar, &amp;intvar))包含2 转换说明符%s & %d)。如果字符串和整数成功转换并存储在提供的变量中,则返回2。小于等于 匹配失败输入失败 或用户通过使用 Ctrl+D 手动生成 EOF (或 Ctrl+Z on windows)。

如果转换失败,可能是由于匹配失败(输入类型和转换说明符不匹配)或输入失败(每个输入都没有足够的输入)转换说明符),从 stdin 读取停止,不再从 输入缓冲区 读取更多字符,并且所有字符都被留下 - 只是等待破坏您对 scanf 的下一次调用。

此外,您必须考虑在每次调用scanf 后留在输入缓冲区 中的'\n'(由用户按Enter 生成)。一些格式说明符会消耗前导空格,而其他(例如字符格式说明符不会),事实上%c会很乐意使用'\n'留在stdin 作为您的下一个输入

具体来说,数字格式说明符(例如%d, %x, %o, %lf, ...)都将忽略前导空格,因此您不必在下一次scanf 调用之前专门删除空格。对于你所做的所有其他人。对stdin 中剩余(或可能剩余)的内容进行说明对于使用scanf 进行输入至关重要。否则,您只是要求输入似乎被跳过或......无限循环。 (您可以按照自己制作格式字符串的方式处理空格)

这就是为什么像fgets 这样的面向行的 输入函数是处理用户输入的推荐方法。它将读取并包括尾随'\n',它可以简单地检查用户提供的所有输入字符是否正确读取。 (验证用户输入后,您可以从fgets(或POSIX getline)填充的缓冲区中解析您需要的任何内容

但是由于您会多次遇到scanf,因此花时间阅读(并理解)man scanf 是非常值得的。是的,读起来有点枯燥,但它是唯一能准确解释scanf 陷阱在哪里的东西。

考虑到这一点,下面提供了两个示例(修改您的代码),向您展示如何使用scanf 处理输入。 nquestions 的第一次阅读仅显示了使用 scanf 验证整数输入、检查返回、处理用户取消(生成 EOF)以及最后清空任何剩余字符的通用方法987654356@ 简单地从stdin 读取直到找到'\n'(由用户按Enter 生成)或EOF

用户输入的其余部分由辅助函数getintvalue 处理(因为您不想在代码中一遍又一遍地复制验证代码)。 getintvalue 将要显示的提示和 格式字符串 作为参数,但本质上与在函数中为 nquestions 所做的事情相同。

其他变化。 questions[] 不需要数组。一个简单的int 值就可以了。您不需要在switch 的每个case 中重复代码。 (已移至最后)。其余的更改和问题在 cmets in-line 中得到解决,如下:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define PRMTSZ 128  /* if you need a constant define one */

/* or use an enum to define several */
enum { NSWITCH = 6, QMAX = 256 };

/* function to get integer value from user.
 * prompt user with 'prompt', read value with format 'fmt'.
 * returns int value or EOF on user cancelation of input.
 */
int getintvalue (const char *prompt, const char *fmt);

/* simple function to empty remaining chars from stdin */
void empty_stdin();

int main (void) {

    int rightanswers = 0,
        nquestions = 0;

    srand (time(NULL));

    /* example input loop -- loop continually until valid input or EOF */
    for (;;) {
        int rtn = 0;

        printf ("Number of questions: ");
        if ((rtn = scanf ("%d", &nquestions)) == EOF) {
            fprintf (stderr, "warning: user canceled input.\n");
            return 1;
        }
        empty_stdin();  /* remove all remaining chars from stdin */

        if (rtn == 1)   /* good input */
            break;

        /* handle matching or input failure */
        fprintf (stderr, "error: invalid input.\n");
    }

    /* loops are ZERO based in C */
    for (int i = 0; i < nquestions; i++)
    {
        /* declarations 1st in each block, for C89 portability - Win7, etc. */
        char prompt[PRMTSZ] = "";               /* buffer for prompt */
        int randomnumb = rand() % NSWITCH,      /* values 0 - 5 */
            question = rand() % QMAX,           /* values 0 - 255 */
            answer = 0;

        switch (randomnumb)
        {
            case 0:
                sprintf (prompt, "\nConvert 0%o to base 10: ", question);
                /* let's use a getintvalue to validate user int input */
                if ((answer = getintvalue (prompt, "%d")) == EOF)
                    return 1;
                break;
            case 1:
                sprintf (prompt, "\nConvert 0%o to base 16: ", question);
                if ((answer = getintvalue (prompt, "%x")) == EOF)
                    return 1;
                break;
            case 2:
                sprintf (prompt, "\nConvert %d to base 8: ", question);
                if ((answer = getintvalue (prompt, "%o")) == EOF)
                    return 1;
                break;
            case 3:
                sprintf (prompt, "\nConvert %d to base 16: ", question);
                if ((answer = getintvalue (prompt, "%x")) == EOF)
                    return 1;
                break;
            case 4:
                sprintf (prompt, "\nConvert 0x%x to base 8: ", question);
                if ((answer = getintvalue (prompt, "%o")) == EOF)
                    return 1;
                break;
            case 5:
                sprintf (prompt, "\nConvert 0x%x to base 10: ", question);
                if ((answer = getintvalue (prompt, "%d")) == EOF)
                    return 1;
                break;
            default:
                fprintf (stderr, "error: something went wrong in switch.\n");
                goto badswitch;
                break;
        }
        if (answer == question) {
            rightanswers++;
            printf ("Right!\n");                                       
        }
        else
            printf ("Wrong!\n");

        badswitch:;
    }

    /* always end with '\n' for POSIX compiant EOF */
    printf("\nYou got %d conversions right!\n", rightanswers);

    return 0;
}

int getintvalue (const char *prompt, const char *fmt)
{
    int value = 0;

    /* input loop -- loop continually until valid input or EOF */
    for (;;) {
        int rtn = 0;

        printf ("%s: ", prompt);
        if ((rtn = scanf (fmt, &value)) == EOF) {
            fprintf (stderr, "warning: user canceled input.\n");
            return rtn;
        }
        empty_stdin();  /* remove all remaining chars from stdin */

        if (rtn == 1)   /* good input */
            break;

        /* handle matching or input failure */
        fprintf (stderr, "error: invalid input.\n");
    }

    return value;
}

void empty_stdin()
{
    int c;

    do
        c = getchar();
    while (c != '\n' && c != EOF);
}

以下是显示在程序不同阶段正确处理错误输入或用户取消的各种验证运行。

一切顺利时的示例

$ ./bin/inttestscanf
Number of questions: 4

Convert 218 to base 16: : da
Right!

Convert 0325 to base 10: : 213
Right!

Convert 0xe to base 10: : 14
Right!

Convert 0x39 to base 8: : 71
Right!

You got 4 conversions right!

用户取消问题数量时的示例

$ ./bin/inttestscanf
Number of questions: foo
error: invalid input.
Number of questions: bar
error: invalid input.
Number of questions: warning: user canceled input.

用户输入无效输入时的示例

$ ./bin/inttestscanf
Number of questions: 4

Convert 023 to base 16: : no good
error: invalid input.

Convert 023 to base 16: : 13
Right!

Convert 0xc7 to base 8: : foo
error: invalid input.

Convert 0xc7 to base 8: : 307
Right!

Convert 0353 to base 16: : eb
Right!

Convert 0x76 to base 10: : f8
error: invalid input.

Convert 0x76 to base 10: : 118
Right!

You got 4 conversions right!

注意:想想如果scanf 需要一个十六进制值并且用户输入一个以a-f 开头的任意字符串会发生什么?)

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

【讨论】:

  • 我现在很开明!感谢您的时间和解释!
  • 很高兴我能帮上忙。如果答案对您有用并回答了您的问题,您可以通过选择左上角来接受答案。祝你编码顺利。
【解决方案2】:

如果scanf 遇到无法分配的内容,它会停止并返回。您尝试分配的变量不会被更改 - 但您会检查它的值,就像它已被读取一样。它只会包含之前的任何东西,或者随机的东西。

要使用scanf,您需要始终检查其返回值,它会告诉您分配了多少变量。只有当此计数显示您的变量真正分配时,您才应该访问它的内容。

请注意,在 scanf 失败后,“不可读”数据仍会在输入流中,下一次 scanf 将尝试再次读取相同的数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-04
    • 1970-01-01
    • 2014-12-01
    • 2018-06-09
    • 1970-01-01
    • 2015-12-12
    • 2014-03-11
    • 2011-08-21
    相关资源
    最近更新 更多