scanf 如何使用空格?

4

我是一名数学专业的学生,正在学习C语言的基础编程知识。我需要一个程序来读取由数组组成的输入,数组的组成部分必须满足一定的条件。我希望程序能够询问用户数组的组成部分。然后,用户应该输入这些组成部分,并用空格分隔它们。细节并不重要,重要的是让主要问题得到解决。下面我们来举个更简单的例子:假设我想要一个有6个元素的数组,但不希望出现数字4。那么我尝试了以下代码:

#include <stdio.h>

int main(void) {
    int a[6];
    printf("enter components: ");
    int i;
    for (i = 0; i < 6; i++) {
        scanf("%d", &a[i]);
        if (a[i] == 4) printf(" \n\n4 is not allowed, try again\n\n");
    }
    for (i = 0; i < 6; i++) {
        printf("%d ", a[i]);
    }
}

如果我编译并运行它,例如输入以下内容:
1 2 3 4 5 6

我会得到错误消息,但只有在按下回车键后才会出现,也就是在输入完所有六个组件之后(不是在第四次按空格后立即出现)。因此,以下是我的问题(我正在寻找不使用字符串或指针的解决方案,除非没有这样做的可能性):

  1. Is there a way to get the program to read a component (and to act accordingly) straight after its subsequent space has been entered? I'm guessing there isn't because scanf only works after the user presses enter, and not space, correct?

  2. If there isn't, is there a way to get the program to read the components all at once after having pressed enter at the end, but letting the user pick up from the last right component? For example, with the above input, I would like the program to display something like this:

    4 is not allowed
    1 2 3 _
    

    so that the user can correct his/her input (possibly changing the first three digits as well).

抱歉如果这个问题太蠢了!谢谢你的帮助!


编辑:感谢众多热心回答,你们都非常有帮助!很遗憾我只能接受一个答案。


抱歉,但错误处理和检查输入错误是不同的事情。如果您不反对的话,我可以进行编辑。 - Gangnus
C 标准库中没有支持这个功能的内容,你需要使用一个更多或者更少依赖于平台的库,比如 ncurses。你的目标平台是什么? - ruakh
@ruakh 我在Windows上使用Code Blocks;我只使用了标准库和数学库,不知道其他的库。 - Emilio Ferrucci
5个回答

2
在for循环中,每次迭代后计数器会自动加一。如果您输入了无效的输入,应该防止计数器增加。为此,当您输入无效数据时,只需在您的代码中添加i--;即可。
#include <stdio.h>

int main(void) {
    int a[6];
    printf("enter components: ");
    int i;
    for (i = 0; i < 6; i++) {
        scanf("%d", &a[i]);
        if (a[i] == 4){
           printf(" \n\n4 is not allowed, try again\n\n");
           i--;
        }
    }
    for (i = 0; i < 6; i++) {
        printf("%d ", a[i]);
    }
}

Mir,这种方法会让他陷入无限循环中...因为a[i]将一直保持为4! - Yevgeny Simkin
谢谢你的回答。这样做不会只是用它后面的组件覆盖错误的组件吗?我尝试运行了它,但没有得到改进。 - Emilio Ferrucci
2
为什么?循环开始处有一个 scanf。 @mir-milad-hosseiny,你的代码是正确的,但你没有回答问题。 Emilio,你是对的。这是你代码中另一个问题的更正。 - asaelr

1

这与您的终端处于“cooked”模式有关。直到用户按下回车键,字符才会被发送到程序。


好的,谢谢,这就是我想象中的。不过,选项2仍然有可能,不是吗? - Emilio Ferrucci
在 cooked mode 下,用户无法编辑您打印的内容。如果关闭 cooked mode,则任何事情都有可能发生。例如,请查看 libreadline - Ben Voigt
谢谢,我会研究一下,但我怀疑我不能在我的作业中使用它。 最后一件事:如果用户在输入每个组件后按回车键,我可以让程序立即检查每个组件,但是如果用户用空格分隔组件并在末尾按回车键,则没有办法(仅使用stdio.h)让用户从第一个错误的组件重新开始输入。正确吗? - Emilio Ferrucci
1
@Emilio:嗯,如果你数一下有多少是正确的,你可以让他们从那里重新输入。让他们编辑先前接受的内容更加困难。 - Ben Voigt

1
请查看下面的代码:
#include <stdio.h>

int main(void) {
    int a[6];
    int i;
    bool hasError = false;
    int errorIndex = 0;
    do{
        hasError = false;
        printf("enter components: ");
        for (i = 0; i < errorIndex; i++)
            printf("%d ", a[i]);

        for (i = errorIndex; i < 6; i++) {
            scanf("%d", &a[i]);
            if (a[i] == 4 && hasError == false){
                printf(" \n\n4 is not allowed, try again\n\n");
                hasError = true;
                errorIndex = i;
            }
        }
    }while(hasError == true);
    for (i = 0; i < 6; i++) {
        printf("%d ", a[i]);
    }
}

在第6行中,bool是什么意思?我的编译器不理解它(未声明),而我也不理解(抱歉,我是一个真正的初学者)。 - Emilio Ferrucci
3
boot 是一个布尔类型。你需要包含 <stdbool.h> 以使用它。(或者你可以将 bool 改为 int,并将 false 改为 0 - asaelr
1
太好了,这个完美地解决了问题!我猜我得改变被采纳的答案了。 - Emilio Ferrucci
1
谢谢,希望对你有用。 - Mir Milad Hosseiny

1

在Mir Milad Hosseiny的答案基础上进行改进(我错误地将其识别为无法控制的无限循环...实际上它正是我在评论中描述的无限循环)...

我会编写一个小函数,其中包含“白名单”(您想要的内容)或“黑名单”(您不想要的内容),并检查每个值是否属于列表中。这样,您可以保留一个单独的位置,存储您愿意接受的值或您不愿意接受的值,因此您的主要函数不会因异常或包含在“if”中而变得非常混乱。

所以你的代码应该是

if(isAllowed(a[i]){
   myList[j] = a[i]; //j is your alternate counter
}

谢谢,这很有道理。有没有办法让程序打印出myList,以便用户可以修改它? - Emilio Ferrucci
好的,Ben Voigt刚回答了这个问题。 - Emilio Ferrucci
1
对,我的意思是你可能想要实际使用那6个数字做些其他事情(而不仅仅是将它们打印回来)。所以,你最终会得到一个userInput[6]数组,它将存储这些数字。因此,我的建议是将scanf放在一个无限循环中(不基于任何int),并保持在其中,直到你有了6个有效值。只有当你有一个有效值时才增加有效值索引,并在userInput数组中使用该索引。 - Yevgeny Simkin

1
你可以像这样做:
int i,a[6];
for (int i=0;i<6;i++) {
 scan: scanf("%d",&a[i]);
}
for (int i=0;i<6;i++) if (a[i]==4) {
 printf("4 is not allowed. re-enter the last %d numbers\n",6-i);
 goto scan;
}

请注意,在大多数情况下,最好避免使用goto,但在这种情况下,我认为它很自然。
如果你真的想要,你可以打印出第一个i个数字(在goto之前),但这很复杂(并且依赖于平台)让用户更改这些数字。

好的,这仍然需要用户手动重新输入最后的6-i个组件。 - Emilio Ferrucci
我以为这就是你想要的。如果不是的话,将第二个循环体改为: printf("4 不允许。重新输入第 %d 个数字\n",i+1); scanf("%d",&a[i]); i--;i-- 是为了重新检查 a[i] - asaelr
是的,抱歉,那就是我想要的。我的意思是用户需要输入前i个组件,但仔细想想这并不必要,因为它们已经存储在a[i]中了。谢谢,没有打印前i个组件的可能性,同时允许用户编辑它们,这似乎是最好的解决方案! - Emilio Ferrucci

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接