【问题标题】:Scanf character in C [duplicate]C中的Scanf字符[重复]
【发布时间】:2017-02-16 01:32:26
【问题描述】:

由于某种原因,scanf 读取了字符,但即使字符有效,程序也不会继续通过用于验证字符的 while 循环。有时它有效,有时则无效。奇怪的是我没有做任何不同的事情。有什么想法吗?

do
{
    printf("Enter \"p\" if you want to sort and shuffle a list of players or enter \"s\" if you want to sort a list of slots: "); // prompt
    scanf("%c", &tmp);
} while (tmp != 'p' && tmp != 's');

新代码:

printf("Enter 'p' if you want to sort and shuffle a list of players or enter 's' if you want to sort a list of slots:"); // prompt
tmp = getchar();

printf("%c ",tmp);
if (tmp == 'p')
{
    size = readPlayerFile(players); // calling readPlayerFile function
    shufflePlayers(players, size); // call shufflePlayers function
    sortPlayers(players, size); // call sortPlayers function
}
else if (tmp == 's')
{
    printf("hello");
    size = readSlotFile(slots); // calling readSlotFile function
    sortSlot(slots, size); // call sortSlots function

}

【问题讨论】:

  • 您是否进行了任何基本调试?就像使用调试器和/或调试打印语句来找出 tmp 工作时和不工作时取什么值?
  • 它在我的系统上运行良好。
  • 在此之前你读了什么?它是否在输入中留下了换行符?您应该考虑使用if (scanf(" %c", &tmp) != 1) { …handle error… },其中%c 之前的空格并非偶然——它会跳过空格(空格、制表符、换行符)并等待输入其他内容(p 或s) .
  • 是的,我做了 printf("%c", tmp);在 scanf 之后但在 while 循环之前,它总是打印 p 但我不明白为什么它不会退出 while 循环。我现在尝试错误处理。谢谢

标签: c character scanf


【解决方案1】:

最简单的选择是在scanf() 的格式字符串中添加一个前导空格。例如,如果您想在输入错误时提示用户再次输入字符,您可以这样做:

#include <stdio.h>

int main(void)
{

    char tmp;
    printf("Enter \"p\" if you want to sort and shuffle a list of players "
           "or enter \"s\" if you want to sort a list of slots: "); // prompt
    while (scanf(" %c", &tmp) != 1 || (tmp != 'p' && tmp != 's')) {
        printf("Please enter \"p\" or \"s\": ");
    }
    printf("You chose %c\n", tmp);

    printf("Choose \"a\"scending or \"d\"escending sort: ");
    while (scanf(" %c", &tmp) != 1 || (tmp != 'a' && tmp != 'd')) {
        printf("Please enter \"a\" or \"d\": ");
    }
    printf("You chose %c\n", tmp);

    return 0;
}

不过,这种简单的方法存在问题。如果用户输入"aaap",那么结果输出将是:

Enter "p" if you want to sort and shuffle a list of players or enter "s" if you want to sort a list of slots: aaap
Please enter "p" or "s": Please enter "p" or "s": Please enter "p" or "s": You chose p
Choose "a"scending or "d"escending sort:

或者,更糟糕的是,如果用户输入了正确的第一个字符,然后是其他字符,例如"sfffa":

Enter "p" if you want to sort and shuffle a list of players or enter "s" if you want to sort a list of slots: sfffa
You chose s
Choose "a"scending or "d"escending sort: Please enter "a" or "d": Please enter "a" or "d": Please enter "a" or "d": You chose a

因为scanf() 在输入流中留下了不匹配的字符,所以必须在再次读取输入流之前处理这些字符。在使用" %c" 的简单解决方案中,初始空格会导致scanf() 跳过前导空白字符,但会拾取任何其他字符。而在现实世界中,您不能指望用户通过提供良好的输入进行合作。

一种典型的可移植解决方案是在循环中使用getchar() 以在输入操作后清除输入流。您可能需要稍微更改输入循环逻辑才能执行此操作:

#include <stdio.h>

...

char tmp;
int c;
int scanf_ret;

do {
    printf("Enter \"p\" if you want to sort and shuffle a list of players "
           "or enter \"s\" if you want to sort a list of slots: "); // prompt
    scanf_ret = scanf("%c", &tmp);
    while ((c = getchar()) != '\n' && c != EOF) {
        continue;                   // discard extra characters
    }
} while (scanf_ret != 1 || (tmp != 'p' && tmp != 's'));

在调用scanf() 之后,进入一个循环,读取并丢弃输入流中剩余的任何字符。请注意c 是int,并且EOF 在循环中被显式测试。 getchar() 函数可能会在发生错误时返回 EOF,或者如果用户从键盘发出 EOF 信号,或者如果输入已从文件重定向。在这种情况下未能测试EOF 将导致无限循环。

即使用户输入了不可预知的输入,该解决方案仍然有效,因为输入流总是在获得输入后被清除。请注意,格式字符串中不需要前导空格。另一种解决方案是使用fgets() 将一行输入读入缓冲区,并使用sscanf() 解析缓冲区。

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

...

char buffer[1000];
char tmp;
int sscanf_ret;

do {
    printf("Enter \"p\" if you want to sort and shuffle a list of players "
           "or enter \"s\" if you want to sort a list of slots: "); // prompt
    if (fgets(buffer, sizeof buffer, stdin) == NULL) {
        fprintf(stderr, "Error in fgets()\n");
        exit(EXIT_FAILURE);
    }
    sscanf_ret = sscanf(buffer, "%c", &tmp);
} while (sscanf_ret != 1 || (tmp != 'p' && tmp != 's'));

fgets() 函数通过换行符获取整行输入,或者如果缓冲区不足以容纳输入流中的所有字符,则从输入流中获取 (sizeof buffer) - 1 字符和 @987654345 @终结者。通过提供一个大尺寸的缓冲区,这应该可以正常工作。用户仍然可以输入大量字符,虽然这不会溢出缓冲区,但字符会留在输入流中以供下一次读取操作查找。为避免此类问题,如果还有多余的字符,则应在调用fgets() 后使用上面的getchar() 循环清除输入流。

所有这些技术都有自己的位置。显然最后两个比第一个更健壮,并且由于用户不太可能在输入提示符处输入 999 个字符(包括换行符),因此它们几乎处于同等地位。但是,为避免所有意外,最好在获得用户输入后显式清除输入流。

【讨论】:

  • 感谢我不知道的冗长解释!我摆脱了验证器,因为我仍然遇到问题,所以我只有提示和 tmp = getchar();并且无论出于何种原因它仍然无法工作,我尝试在阅读后直接打印字符,但它仍然无法工作。
  • 如果您还没有这样做,您可能需要打印一个换行符以将输出缓冲区刷新到屏幕:printf("%c\n", tmp);。如果您使用新代码在问题末尾添加编辑部分,我会看一下。
  • 谢谢我会尝试,现在我已经用新代码编辑了问题,我正在尝试简化它以尝试找到问题。奇怪的是它适用于将数字读取为字符但不适用于 p 或 s
  • 当我添加 \n 时它现在会打印出字符,但它不会运行其余代码
  • 我发现了问题,我正在洗牌一个数组,我正在使用 srand 和 rand 。当无法满足随机播放的条件时,它不会打印。谢谢!对不起所有的麻烦
【解决方案2】:

首先,scanf() 在输入缓冲区中留下一个\n,下次调用它时它仍保留在输入缓冲区中。您需要在格式说明符中添加一个空格:

scanf(" %c", &tmp)

跳过空白字符,可以是换行符、空格或制表符。

其次,您需要检查scanf()的返回,以确保只找到一个字符。

您的代码可能如下所示:

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

int main(void) {
    char temp;

    do {
        printf("Enter \"p\" if you want to sort and shuffle a list of players or enter \"s\" if you want to sort a list of slots: "); // prompt
        if (scanf(" %c", &tmp) != 1) {
            printf("Invalid character\n");
            exit(EXIT_FAILURE);
        }
    } while (tmp != 'p' && tmp != 's');

    return 0;
}

【讨论】:

    猜你喜欢
    • 2013-02-18
    • 2016-11-11
    • 1970-01-01
    • 2017-06-05
    • 1970-01-01
    • 1970-01-01
    • 2018-08-15
    • 2012-11-12
    相关资源
    最近更新 更多