9

First of all, other questions about usage of sscanf do not answer my question because the common answer is to not use sscanf at all and use fgets or getch instead, which is impossible in my case.

The problem is my C professor wants me to use scanf in a program. It's a requirement. However the program also must handle all the incorrect input.

The program must read an array of integers. It doesn't matter in what format the integers for the array are supplied. To make the task easier, the program might first read the size of the array and then the integers each in a new line.

The program must handle the inputs like these (and report errors appropriately):

  1. 999999999999999...9 (numbers larger than integer)
  2. 12a3 (don't read this as an integer 12)
  3. a...z (strings)
  4. 11 aa 22 33\n all in one line (this might be handled by discarding everything after 11)
  5. inputs larger than the input array

There might be more incorrect cases, these are the only few I could think of.

If the erroneous input is supplied, the program must ask the user to input again until the correct input is given, but the previous correct input must be kept (only incorrect input must be cleared from the input stream).

Everything must conform to C99 standard.

Chris
  • 44,602
  • 16
  • 137
  • 156
evodevo
  • 479
  • 1
  • 7
  • 14

5 Answers5

11

The scanf family of function cannot be used safely, especially when dealing with integers. The first case you mentioned is particularly troublesome. The standard says this:

If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the object, the behavior is undefined.

Plain and simple. You might think of %5d tricks and such but you'll find they're not reliable. Or maybe someone will think of errno. The scanf functions aren't required to set errno.

Follow this fun little page: they end up ditching scanf altogether.


So go back to your C professor and ask them: how exactly does C99 mandate that sscanf will report errors ?

cnicutar
  • 178,505
  • 25
  • 365
  • 392
  • 1
    Technically, it can, provided you meet the requirements shown above. scanf fixed size into a string, which is well-defined, then check it, and sscanf again to do the (now provably valid) conversion. A little ridiculous, but it meets the requirement of using scanf without breaking; – davenpcj Jul 31 '14 at 15:08
2

Well, let sscanf accept all inputs as %s (i.e. strings) and then program analyze them

mikithskegg
  • 806
  • 6
  • 10
  • 2
    Plain `%s` in `scanf` === potential buffer overflow. – ThiefMaster Feb 26 '12 at 21:45
  • 3
    @ThiefMaster: Yes, but not necessarily in `sscanf`, as long as the destination is bigger than the source. On the other hand, `%s` gives you whitespace-delimited words, not the whole string. – Keith Thompson Feb 27 '12 at 01:10
  • @ThiefMaster Using a field width like "%100s" (eg: 100 char limit) can be used to avoid overflow. – davenpcj Jul 31 '14 at 15:12
  • @davenpcj: Should a number with 100 leading zeros (e.g. "000000000.....0001") be handled correctly? – Brendan Sep 05 '18 at 22:42
  • @Brendan: in OP's request, yes, "handled correctly" here means an error must be generated, or if the requirements allow those numbers (unclear), the value must be accepted. To use this answer's approach without buffer overrun, a field width limit must be used. For any inputs which exceed the size of the buffer, input must be discarded up to the next newline. If the requirements allow, inputs of all leading zeros (optionally with a - sign) could be detected and accepted. – davenpcj Dec 05 '18 at 17:32
1

If you must use scanf to accept the input, I think you start with something a bit like the following.

int array[MAX];
int i, n;
scanf("%d", &n);
for (i = 0; i < n && !feof(stdin); i++) {
    scanf("%d", &array[i]);
}

This will handle (more or less) the free-format input problem since scanf will automatically skip leading whitespace when matching a %d format.

The key observation for many of the rest of your concerns is that scanf tells you how many format codes it parsed successfully. So,

int matches = scanf("%d", &array[i]);
if (matches == 0) {
   /* no integer in the input stream */
}

I think this handles directly concerns (3) and (4)

By itself, this doesn't quite handle the case of the input12a3. The first time through the loop, scanf would parse '12as an integer 12, leaving the remaininga3` for the next loop. You would get an error the next time round, though. Is that good enough for your professor's purposes?

For integers larger than maxint, eg, "999999.......999", I'm not sure what you can do with straight scanf.

For inputs larger than the input array, this isn't a scanf problem per se. You just need to count how many integers you've parsed so far.

If you're allowed to use sscanf to decode strings after they've been extracted from the input stream by something like scanf("%s") you could also do something like this:

while (...) {
    scanf("%s", buf);
    /* use strtol or sscanf if you really have to */
}

This works for any sequence of white-space separated words, and lets you separate scanning the input for words, and then seeing if those words look like numbers or not. And, if you have to, you can use scanf variants for each part.

Dale Hagglund
  • 16,074
  • 4
  • 30
  • 37
  • What about using scanf('%c', c) instead of getchar()? That would be safe and fulfill the requirement, wouldn't it? – evodevo Mar 05 '12 at 17:27
  • True, but `scanf("%c", &c)` is an expensive way to emulate `getchar()`. Is the assignment really about this sort of trivial syntactic use of `scanf`? I think @cnicutar's comment really nails a central issue: given the quoted excerpt of the standard, it's impossible to use `scanf` safely to parse overflowing interger inputs. – Dale Hagglund Mar 05 '12 at 23:07
  • @evodevo: `scanf('%c', c)` is a good example for why format strings should, in general, be avoided in C++. Too bad one can't bookmark comments :D – Sebastian Mach Mar 30 '12 at 12:05
0

The problem is my C professor wants me to use scanf in a program. It's a requirement. However the program also must handle all the incorrect input.

This is an old question, so the OP is not in that professor's class any more (and hopefully the professor is retired), but for the record, this is a fundamentally misguided and basically impossible requirement.

Experience has shown that when it comes to interactive user input, scanf is suitable only for quick-and-dirty situations when the input can be assumed to correct.

If you want to read an integer (or a floating-point number, or a simple string) quickly and easily, then scanf is a nice tool for the job. However, its ability to gracefully handle incorrect input is basically nonexistent.

If you want to read input robustly, reliably detecting incorrect input, and perhaps warning the user and asking them to try again, scanf is simply not the right tool for the job. It's like trying to drive screws with a hammer.

See this answer for some guidelines for using scanf safely in those quick-and-dirty situations. See this question for suggestions on how to do robust input using something other than scanf.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
-1

scanf("%s", string) into long int_string = strtol(string, &end_pointer, base:10)

konke
  • 1
  • 1