0

I have an integer 'n' that is responsible for amount of numbers to enter later on. I need to check for any incorrect inputs here. The first scanf for 'n' works fine, but the second has some flaws. The case is following:

n = 3 (e.g.) - doesn't matter

Then I want to scanf: 1 2 3 4

It will be scanned first 3 values but I need to throw out an error.

Same goes with: 1 2 3.5 - (last number is float but it still reads 3)

and with a char: 1 2 3g

if (scanf("%d", n) == 1 && getchar() == '\n') {
        if (n > NMAX || n < 0) {
            error = 1;
            return;
        }
        for (int i = 0; i < n; i++) {
            if (scanf("%d", p) == 1) {

The ONLY correct input is the exact same amount of integer numbers (equals to 'n') to scan in a for loop.

UPD: I can use only <stdio.h> library as well as only scanf.

Arzental
  • 103
  • 6
  • 6
    Use only `fgets()` for user input (possibly followed by `strtol()`) – pmg Jul 19 '21 at 12:03
  • `getchar() == '\n'` could be false while the input is still correct. For example is the user inputted two valid inputs on the same line, separated by space. Or just pressed the tab or spacebar a couple of times before the `Enter` key. And while using `fgets` followed by other parsing methods (like e.g. `sscanf` or `strtol`) is usually the best way, it still is hard to handle the first example (multiple valid input on a single line). – Some programmer dude Jul 19 '21 at 12:06
  • @pmg mb, didn't include in the question that I can only use library. what is more, scanf is more preferable (it was in a template to the task). – Arzental Jul 19 '21 at 12:07
  • 2
    `fgets()` is part of the Standard Library, its prototype is in `` header – pmg Jul 19 '21 at 12:08
  • FYI: `` is not a library... it's a collection of prototypes (and types, and constants, ...) of parts of the Standard Library. – pmg Jul 19 '21 at 12:09
  • Ik, told about `strtol()` – Arzental Jul 19 '21 at 12:10
  • 3
    You can parse the string from `fgets()` manually instead of with `strtol()` ... or even with `sscanf()`. Point is: `scanf()` is a bad tool to get user input ... it can do it in some well-behaved environments (typing `"3.5"` when scanf expects an integer is not a well-behaved environment), but it is lacking (a lot!) when dealing with bad input. – pmg Jul 19 '21 at 12:12
  • 1
    @Arzental if you are not allowed to use anything else than `scanf` you're out of luck. `scanf` is a poor choice for user input. It hasn't been designed for that. – Jabberwocky Jul 19 '21 at 12:16
  • How about multiple spaces bwtween 2 numbers? Is that allowed? – Support Ukraine Jul 19 '21 at 12:30
  • How about multiple spaces after last number? Is that allowed? – Support Ukraine Jul 19 '21 at 12:30
  • @Jabberwocky - So, exactly _[what is scanf() designed to do](https://www.google.com/search?client=firefox-b-1-d&q=What+is+scanf%28%29+designed+to+do%3F)_ if not accept input from `stdin`. (usually keyboard.) BTW, I am not suggesting anything here other than even though it may be a woefully inept method for some user input, it _is_ designed for precisely that :) – ryyker Jul 19 '21 at 12:44
  • @ryyker well, yes it is supposed tu read user input, but it assumes the user inputs no unexpected data. – Jabberwocky Jul 19 '21 at 12:54
  • 1
    @ryyker: The hint is in the name: *scan **formatted**.* It is designed to read well-defined data files, such as those previously written via `printf()`. ;-) – DevSolar Jul 19 '21 at 12:57
  • 1
    `scanf` is good at performing quick-and-dirty user input with no error checking. If you work at it you can reasonably do a little bit of error checking. But trying to do full, robust user input, with error checking, with `scanf`, is either impossible, or is so complicated and confusing and difficult that it's just not worth it. If you want to do robust user input with error checking, the right way is to [use something other than `scanf`](https://stackoverflow.com/questions/58403537). – Steve Summit Jul 19 '21 at 13:16
  • 1
    If you must use `scanf`, if you are not allowed to use, say, `fgets` followed by `strtol`, this is a counterproductive exercise. It is the computer programming equivalent of saying "You must drive these screws, and you must use a hammer, you may not use a screwdriver". – Steve Summit Jul 19 '21 at 13:19
  • Are you allowed to use `fgets`, and then use your own version of `atoi` or `strtol`, that you write yourself? That's what I'd do, if someone held a gun to my head and told me I had to solve this problem using only functions found in ``. – Steve Summit Jul 19 '21 at 13:20
  • *I can use only `` library as well as only `scanf`.* So, unfortunately, you have a choice: Do you want to learn to program in C well, or do you want to get a good grade on this assignment? Solving this assignment, using only `scanf`, will not teach teach you anything useful about C programming; all it will teach you is irrelevant details about the useless dead end that is `scanf`. `scanf` is square training wheels for beginning C programmers, to be discarded as soon as possible and never used again. – Steve Summit Jul 19 '21 at 13:26
  • I think I'll go to `fgets()` option. As I understood, I need to read `char` type and then convert it to `int` in my case? – Arzental Jul 19 '21 at 13:31
  • @Arzental `fgets` will read a whole line as a *string* (`char []`, or `char *`, depending on how you think about it). And then, yes, you need to convert string to `int`. The simple-but-flawed way to convert string to `int` is `atoi()`, and the better way is `strtol()`. (But they're declared in ``, so they may be off limits to you.) – Steve Summit Jul 19 '21 at 14:58

3 Answers3

2

Create a helper function to read an int and validate it.

The key is that to validate input, form a helper function that you can improve as needed, like using fgets() rather than scanf() once the poor "only scanf" requirement is removed.

// Sample
// Return 1 on success
// Return EOF on end-of-file
// Else return 0
int read_int(int *i, int min, int max, int line) {
  long long num;
  int result = scan("%18lld", &num);  // Accept many out of int range input
  if (result == EOF) return result;  // or if (result < 0)

  if (line) {
    // consume the rest of the line
    int ch;
    while ((ch = getchar()) != '\n' && ch != EOF) {
      if (!isspace(ch)) { // or `ch != ' ' && `ch != '\t' && ...
        result == 0; 
      }
    }
  }

  if (result == 1) {
    if (num < min || num > max) {
      return  0; 
    }
    *i = (int) num; 
  }

  return result;
}
    

Sample usage

if (read_int(&n, 0, NMAX, 1) != 1) {
  error = 1;
  return;
}
for (int i = 0; i < n; i++) {
  if (read_int(&p, INT_MIN, INT_MAX, i + 1 == n) != 1) {
    error = 1;
    return;
  }
  ...

Note: read_int() does not catch all errors, just many of them. Easy enough to improve, once all OP's limitations and goals are known.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

Try using scansets with scanf. %1[\n] will scan up to one character that is a newline. %99[ \t] will scan up to 99 characters that are space or tab. If the character is not a newline, space or tab, it is replaced in the input stream.
If scanf with %d can't scan an int, it will return 0. scanf could also return EOF.
fgets and parse with strtol or others is the better solution.

#include <stdio.h>

int main ( void) {
    char space[100] = "";
    char newline[2] = "";
    int number = 0;
    int count = 0;
    int quantity = 3;

    printf ( "enter %d integers\n", quantity);
    while ( 1) {
        if ( 1 == scanf ( "%d", &number)) {
            ++count;
        }
        else {
            printf ( "could not parse an integer\n");
            break;
        }
        scanf ( "%99[ \t]", space);
        if ( 1 == scanf ( "%1[\n]", newline)) {
            if ( count == quantity) {
                printf ( "scanned %d integers\n", quantity);
                break;
            }
            else if ( count > quantity) {
                printf ( "too many integers\n");
                break;
            }
            else printf ( "enter another integer\n");
        }
    }
    return 0;
}
user3121023
  • 8,181
  • 5
  • 18
  • 16
  • This is a nice answer in its way, in that it perfectly demonstrates something I was saying in another comment: "trying to do full, robust user input, with error checking, with `scanf`, is either impossible, or is so complicated and confusing and difficult that it's just not worth it." This program works well enough, and meets the requirements -- but it is contorted, unreadable, and unmaintainable. (Apologies for not upvoting, but I can't upvote a mess like this, even though it answers the question and proves a point.) – Steve Summit Jul 19 '21 at 15:16
  • 1
    To scan and discard all space, tab, could use `scanf ( "%*[ \t]");`. – chux - Reinstate Monica Jul 20 '21 at 18:21
-2

Check if this works

while(n-1)
{
    scanf("%d ",p);
    n-=1;
}
scanf("%d",p);
//After this you can scan again to check if there is anything extra in input
//and throw error accordingly
  • `scanf` to read an integer is the easy part. That wasn't the question. It's the checking for anything extra, and throwing errors, that is the hard part. (Also this code will go into an infinite loop if there is, say, a character 'x' mixed in with the input numbers.) – Steve Summit Jul 19 '21 at 15:20