c检测stdin的空输入

时间:2013-06-20 22:48:07

标签: c stdio

这似乎应该是一件简单的事情,但经过几个小时的搜索我什么都没发现......

我有一个从stdin读取输入字符串并清理它的函数。问题是当我点击输入而没有输入任何内容时,它显然只是从输入缓冲区中读取了一些垃圾。

在以下示例中,提示是“输入?”并且在同一行之后发生的所有事情都是我输入的内容。提示后面的行回显了函数读取的内容。

首先,这是我在两次输入内容时会发生的事情。在这种情况下,该功能完全按预期工作。

input? abcd
abcd
input? efgh
efgh

其次,这是我第一次输入内容时会发生什么,但只是第二次输入:

input? abcd
abcd
input?
cd

以下是我两次点击进入时发生的事情:

input?
y
input?
y

每次重新运行时,都会返回'y'或'@'。由于显而易见的原因,'y'特别危险。

这是我的代码:

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

#define STRLEN 128

int main() {
    char str[STRLEN];
    promptString("input?", str);
    printf("%s\n", str);
    promptString("input?", str);
    printf("%s\n", str);

    return EXIT_SUCCESS;
}

void promptString(const char* _prompt, char* _writeTo) {    
    printf("%s ", _prompt);
    fgets(_writeTo, STRLEN, stdin);
    cleanString(_writeTo);

    return;
}

void cleanString(char* _str) {
    char temp[STRLEN];
    int i = 0;
    int j = 0;

    while (_str[i] < 32 || _str[i] > 126) 
        i++;

    while (_str[i] > 31 && _str[i] < 127) {
        temp[j] = _str[i];
        i++;
        j++;
    }

    i = 0;
    while (i < j) {
        _str[i] = temp[i];
        i++;
    }
    _str[i] = '\0';

    return;
}

我尝试了各种方法(甚至是不安全的方法)刷新输入缓冲区(fseek,rewind,fflush)。没有人解决这个问题。

如何检测空输入以便我可以重新提示,而不是这种烦人且有潜在危险的行为?

2 个答案:

答案 0 :(得分:4)

这部分cleanString

 while (_str[i] < 32 || _str[i] > 126) 
      i++;
当字符串为空时,

跳过\0

您应该将_str[i] != '\0'添加到循环条件中。

要检测空字符串,只需在输入后检查它的长度:

do {
  printf("%s ", _prompt);
  fgets(_writeTo, STRLEN, stdin);
} while (strlen(_writeTo) < 2);

(与'\n'比较两个fgets放入缓冲区的末尾)

答案 1 :(得分:2)

为什么你有一堆带有前导下划线的变量名?那太讨厌了。

无论如何,您必须做的第一件事是检查fgets的返回值。如果它返回NULL,则表示您没有得到任何输入。 (然后,您可以测试feofferror,找出您未获得输入的原因。)

继续cleanString,你有一个while循环,它会消耗一系列不可打印的字符(你可以使用isprint代替魔术数字),然后是while循环消耗一系列可打印的字符。如果输入字符串不包含一系列非printables,后跟一系列printables,那么您将消耗太多或者不够。为什么不使用单个循环?

while(str[i]) {
    if(isprint(str[i]))
        temp[j++] = str[i];
    ++i;
}

这保证在\0终结符之前消耗整个字符串,并且它不能继续经过终结符,并且它将“好”字符复制到temp。我认为这就是你想要的。

你甚至不需要使用临时缓冲区,你可以从str[i]复制到str[j],因为j永远不会超过i你永远不会覆盖你尚未处理的任何东西。