Question

This seems like it should be a simple thing but after hours of searching I've found nothing...

I've got a function that reads an input string from stdin and sanitizes it. The problem is that when I hit enter without typing anything in, it apparently just reads in some junk from the input buffer.

In the following examples, the prompt is "input?" and everything that occurs after it on the same line is what I type. The line following the prompt echoes what the function has read.

First, here is what happens when I type something in both times. In this case, the function works exactly as intended.

input? abcd
abcd
input? efgh
efgh

Second, here is what happens when I type something in the first time, but just hit enter the second time:

input? abcd
abcd
input?
cd

And here is what happens when I just hit enter both times:

input?
y
input?
y

It happens to return either 'y' or '@' every time when I run it anew. 'y' is particularly dangerous for obvious reasons.

Here is my code:

#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;
}

I've tried various methods (even the unsafe ones) of flushing the input buffer (fseek, rewind, fflush). None of it has fixed this.

How can I detect an empty input so that I can re-prompt, instead of this annoying and potentially dangerous behavior?

Was it helpful?

Solution

This part of cleanString

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

jumps over \0 when the string is empty.

You should add _str[i] != '\0' into the loop's condition.

To detect an empty string, simply check it's length just after the input:

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

(comparing with two because of '\n' which fgets puts into the end of buffer)

OTHER TIPS

Why do you have a bunch of variable names with leading underscores? That's nasty.

Anyway, the first thing you must do is check the return value of fgets. If it returns NULL, you didn't get any input. (You can then test feof or ferror to find out why you didn't get input.)

Moving on to cleanString, you have a while loop that consumes a sequence of non-printable characters (and you could use isprint for that instead of magic numbers), followed by a while loop that consumes a sequence of printable characters. If the input string doesn't consist of a sequence of non-printables followed by a sequence of printables, you will either consume too much or not enough. Why not use a single loop?

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

This is guaranteed to consume the whole string until the \0 terminator, and it can't keep going past the terminator, and it copies the "good" characters to temp. I assume that's what you wanted.

You don't even really need to use a temp buffer, you could just copy from str[i] to str[j], since j can never get ahead of i you'll never be overwriting anything that you haven't already processed.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top