我无法清空stdin。如何在C中清空stdin?

24

如何清空标准输入stdin

为什么下面的代码片段无法正常工作?

#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <fcntl.h>

int main() {
    int i = 0, j = 0, sat;
    char arg[256];
    char *argq;
    argq = malloc(sizeof(char) * 10);
    
    printf("Input the line\n");
    i = read(0, arg, sizeof(char) * 9);
    arg[i - 1] = '\0';
    fflush(stdin);
    
    i = read(0, argq, sizeof(char) * 5);
    argq[i - 1] = '\0';

    puts(arg);
    puts(argq);
    
    return 0;
}

如果我输入11个字符,只应该读取其中的9个,但是stdin中剩余的两个字符没有被清除,会在argq中再次被读取。为什么?

输入:123 456 789

输出:

123 456
89

为什么我会得到输出值为89


20
因为fflush()仅定义用于输出流。 - anon
1
你可以随时创建自己的函数来丢弃输入字符。如果你将其命名为“ignore”,那么你就可以更接近C++流了。;-) - Thomas Matthews
请参阅C语言如何清空输入缓冲区? - Gabriel Staples
8个回答

45

我认为fflush只能用于输出流。

你可以尝试在Linux上使用fpurge__fpurge。请注意,fpurge不是标准的且不可移植,它可能不可用。

来自Linux fpurge手册页面:通常情况下,希望丢弃输入缓冲区是错误的。

清空stdin最便携的解决方案可能类似于以下内容:

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

1
不错 - 不知道有fpurge()这个函数。 - mob
5
或者更简单的写法是:scanf("%*[^\n]%*c"); 你当然可以将其合并到现有的 scanf 格式字符串的末尾,以便在使用 scanf 处理一部分内容后丢弃该行的剩余部分。 - R.. GitHub STOP HELPING ICE
@R.. 或许你可以更详细地解释一下提供的正则表达式,即使已经过去了五年。或者提供一个链接,可以澄清它。 - Ungeheuer
@Ungeheuer:这不是正则表达式。应该在scanf文档中记录%[转换说明符。 - R.. GitHub STOP HELPING ICE
1
@R..GitHubSTOPHELPINGICE: 如果在 stdin 中只有一个挂起的换行符,而没有待匹配的 [^\n] 字符类,则 scanf("%*[^\n]%*c"); 将不会丢弃换行符。 - chqrlie


11
int c;
while((c = getchar()) != '\n' && c != EOF);

这是我清除输入缓冲区的方法。


2
这样做可能会有潜在的缺陷。比如说,之后你想要使用 fgetsstdin 中读取数据,但是此时输入框为空,你必须先按下 enter 键才能开始实际输入。 - J.G

3
如何清空标准输入(stdin)缓存?不要尝试清空输入流,这将会引发未定义行为,只有输出流可以清空。

2
如果您使用GLIBC,您可以手动处理stdin指针。
void flush_stdin(){
    unsigned long* tmpstdin = (unsigned long*)stdin;
    unsigned long* oldbuf = (unsigned long*)*(tmpstdin+4);
    free((void*)oldbuf);
    *tmpstdin=(unsigned long)0xfbad2088;
    tmpstdin+=1;
    memset(tmpstdin,'\x00',64);
}

@chrqlie 你说得对。在释放旧缓冲区之前,当然应该检查stdin是否已初始化。检查oldbuf是否为0x0,可以修复任何错误,以防您尝试释放未初始化的堆指针。 - Zopazz

2

您正在使用'\0'覆盖arg中的最后一个元素。该行应改为arg[i]='\0';(在错误和边界检查之后,您还需要进行检查。)

其他人已经对刷新部分进行了评论。


0

在Linux中,如果要清除标准输入(stdin),就会遇到某些情况下命令会开始等待输入的情况。解决方法是将所有的std::cin替换为readLineToStdString():

void readLine(char* input , int nMaxLenIncludingTerminatingNull )
{
    fgets(input, nMaxLenIncludingTerminatingNull , stdin);

    int nLen = strlen(input);

    if ( input[nLen-1] == '\n' )
        input[nLen-1] = '\0';
}

std::string readLineToStdString(int nMaxLenIncludingTerminatingNull)
{
    if ( nMaxLenIncludingTerminatingNull <= 0 )
        return "";

    char* input = new char[nMaxLenIncludingTerminatingNull];
    readLine(input , nMaxLenIncludingTerminatingNull );

    string sResult = input;

    delete[] input;
    input = NULL;

    return sResult;
}

这也将允许您在 std::cin 字符串中输入空格。

-2
在Windows中,您可以使用rewind(stdin)函数。

为什么不在代码中附上经过测试的代码片段呢?因为 rewind 不是 OP 想要实现的目标。 - Haseeb Mir

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