0

I have this code, that prompts the user to enter a 2D vector, and I want to build in error protection so if a character, symbol, etc is entered instead of a character, it prints an error and prompts again. Assuming that this code works perfectly:

vect getVector(void)
{
    vect temp;
    bool flag = false; /* For repetition we use an 
                        * input flag initialized to false */

    while (flag == false)
    {
        printf("Enter a vector in the format x y: ");
        if((scanf("%f%f", &temp.x, &temp.y)) != 2)
            printf("  >Input Failure!\n");

        else
            flag = true; /* Input OK */

        clearBuffer(); /* A function I defined elsewhere */
    }

    return temp;
}

Entering something like "1.w 2.5" or "a.c 3.2" works fine with the error protection. However, entering "1.2 3.c" or "1.6 2.54p" (anything ending with a character) it simply ignores and discards that character. "1.2 3.c" reads "1.2 3.0", "1.6 2.54p" reads "1.6, 2.54" and so on.

How can I build in protection to detect this trailing character and print the error message? Hopefully I cana build something easily into what I've already got.

I've been suggested to store the input in a character string and test for the ASCII codes, and if they're in the range of numbers, then convert them back to float, but I have no clue how that could be done. Moreover I would like to know WHY the code ignores that trailing character, maybe I can then think of a solution myself. Maybe try detecting the character initially? Sorry if it's a silly question, I just can't wrap my head around it.

  • 2
    `scanf()` only reads as much input as needed to do the conversions requested in the format string. I guess there are ways to do what you want with `scanf()`, but much better would be a solution with `fgets()`, `strtok()` and `strtod()`. See my [beginners' guide away from scanf()](http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html) for some insight (it only handles integers, but I guess you can do the transfer to `strtod()`). –  Apr 24 '18 at 15:44
  • 2
    If you want to do any kind of decent error handling, just don't use `scanf`. – Steve Summit Apr 24 '18 at 15:44

2 Answers2

2

How can I read a character that is mistakenly typed at the end of an expecting floating point number?
I want to build in error protection

Time to stop using scanf() - everywhere. Yes it is possible to use scanf() and have robust error detection, but it is not productive.

Instead consider fgets() to read a line of user input @Felix Palmen and then parse the string.

Could use sscanf() and " %n" to detect extra garbage as below or use calls to strtof().

// return 1 on success
// return EOF on end-of-file or input error
int getVector(vect *v) {
  char buf[100];  // make this 2x expected max size
  vect temp;
  bool repeat = true;
  while (repeat) {
    printf("Enter a vector in the format x y: ");
    fflush(stdout);
    if (fgets(buf, sizeof buf, stdin) == NULL) {
      return EOF;
    }
    int n = 0;
    sscanf(buf, "%f%f %n", &temp.x, &temp.y, &n);
    // If scanning was incomplete or did not end at the end of the string, then fail
    if (n == 0 || buf[n]) {
      printf("  >Input Failure!\n");
    } else {
      repeat = false;
    }
  }
  *v = temp;
  return 1;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

You can if you want to do a bit more manual work.

The strtof() family of functions will give you a bit more error information, but they expect a string (char*) as input.

For that, you'll need read into a buffer first (scanf("%s", buffer) will work for this, but as noted, can be susceptible to buffer overrun errors, fgets() will protect against this), then read your two floats from that buffer with strtof() like this:

printf("Enter a vector in the format x y: ");
scanf("%s %s", buffer1, buffer2);

char *cursor = buffer1;
temp.x = strtof(cursor, *cursor);
if(cursor < (buffer1 + strlen(buffer1)))
  printf("  >Input Failure!\n");

cursor = buffer1;
temp.y = strtof(cursor, *cursor);
if(cursor < (buffer2 + strlen(buffer2)))
  printf("  >Input Failure!\n");

The scanf() call here is reading two whitespace (space, tab, newline, etc.) separated strings into buffer1 and buffer2. Given that this is scanf() you will need to ensure that there is enough space to read the two strings so that the computer doesn't just start writing too far.

The two calls to strtof() here each read leading whitespace followed by a valid floating point number and then advance cursor to the first character that is no longer a part of that floating point number. Therefore if it hits a character that is not part of a valid float, it won't reach the end of the string, meaning a bad input was received.

tdk001
  • 1,014
  • 1
  • 9
  • 16
  • Please don't suggest code with buffer overflows! What's wrong with `fgets()`? –  Apr 24 '18 at 16:07
  • btw, `%s` will not even work, OP expects two floats separated by whitespace. –  Apr 24 '18 at 16:08
  • My knowledge is honestly pretty limited - in my course we've also only learnt scanf() as input methods, so I'm attempting to write this without deviating too much from what we should know at this point. This isn't an assignment question, just a question from a textbook that we're using - at this point we should be able to answer it. – Nathan Locke Apr 24 '18 at 16:14
  • Also, a friend suggested using `char nextChar = getchar();` then using a test to see if `nextChar` is `'\n'` or not - I have no idea what he meant. Any light? – Nathan Locke Apr 24 '18 at 16:15
  • The more error checking you want to do, the closer to the actual input you will need to be. Using `getchar()` would allow you to read one character at a time and do however you please, but you would have to re-write all the number parsing that already exists in several methods available in the standard libraries, so why re-write perfectly good code? – tdk001 Apr 24 '18 at 17:24
  • Also, while `scanf()` works _for now_, don't get used to it. everyone else is right that it's got some serious problems. – tdk001 Apr 24 '18 at 17:25