0

For a school assignment, I have to read in a string that has at least one but up to three variables(named command, one, and two). There is always a character at the beginning of the string, but it may or may not be followed by integers. The format could be like any of the following:

i 5 17 
i 3
p
d 4

I am using fgets to read the string from the file, but I'm having trouble processing it. I've been trying to use sscanf, but I'm getting segfaults reading in a string that only has one or two variables instead of three.

  • Is there a different function I should be using?
  • Or is there a way to format sscanf to do what I need?

I've tried sscanf(buffer, "%c %d %d", command, one, two) and several variations with no luck.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 5
    `sscanf()` returns the number of specified values it successfully scans. To understand why you're getting segfaults, you'll need to provide representative code. – Peter Feb 21 '18 at 17:31
  • 1
    It think that in general using `sscanf()` to parse a line from a file should not be done, but in fact you can do something like `switch (sscanf(line, "%c%d%d", &character, &integers[0], &integers[1])) { case 1: /* just the first char */ break; case 2: /* first char and a single integer */ break; case 3: /* full set of items */ break;`. – Iharob Al Asimi Feb 21 '18 at 17:38
  • For the general case where there isn't a small upper bound on the number of values on a line, consider the techniques in [Using `sscanf()` in a loop](https://stackoverflow.com/questions/3975236/how-to-use-sscanf-in-loops). – Jonathan Leffler Feb 21 '18 at 17:52

1 Answers1

3

sscanf is probably up to this task, depending on the exact requirements and ranges of inputs.

The key here is is that the scanf family functions returns a useful value which indicates how many conversions were made. This can be less than zero: the value EOF (a negative value) can be returned if the end of the input occurs or an I/O error, before the first conversion is even attempted.

Note that the %c conversion specifier doesn't produce a null-terminated string. By default, it reads only one character and stores it through the argument pointer. E.g.

char ch;
sscanf("abc", "%c", &ch);

this will write the character 'a' into ch.

Unless you have an iron-clad assurance that the first field is always one character wide, it's probably better to read it as a string with %s. Always use a maximum width with %s not to overflow the destination buffer. For instance:

char field1[64]; /* one larger than field width, for terminating null */
sscanf(..., "%63s", field1, ...);

sscanf doesn't perform any overflow checks on integers. If %d is used to scan a large negative or positive value that doesn't fit into int, the behavior is simply undefined according to ISO C. So, just like with %s, %d is best used with a field width limitation. For instance, %4d for reading a four digit year. Four decimal digits will not overflow int.

Kaz
  • 55,781
  • 9
  • 100
  • 149
  • 1
    @Peter It in fact reads multiple characters, determined by the with. One is the default. – Kaz Feb 21 '18 at 17:33
  • @Kaz is correct. The confusing thing is that it has different semantincs than with `printf()` specifier. – Iharob Al Asimi Feb 21 '18 at 17:37
  • @Kaz - you haven't specified width. For `%c` as is, the width defaults to `1`. – Peter Feb 21 '18 at 17:37
  • @Peter Thanks for pointing that out, but I feel that since I already said that the width defaults to one in the very first revision of my answer, and in a comment replying to you, the topic of "`%c` width defaults to one" has now been beat to death. Perhaps it is so momentous that it bears repetition? – Kaz Feb 21 '18 at 17:45
  • Thanks very much-- I had an issue with a pointer as well but this definitely clarified the function for me and I was able to fix the problem and store the variables properly. – Megan Peeler Feb 21 '18 at 17:47
  • @coderredoc `%c` is sometimes used for extracting a field (as a string) when the leading-whitespace-skipping behavior of `%s` is undesirable. You zero-fill the target array; e.g. `char field[64] = { 0 }` and read no more than one less, which ensures null termination. – Kaz Feb 21 '18 at 18:56