8

I'm trying to figure out exactly how scanf works. If I code something like this:

scanf("%s %d %f", name, &age, &wage);

enter this as standard input:

james 20 34000.25

and then print out name, age, and wage respectively, I get exactly what one would expect, the same thing as I put into standard input. However, if I enter something like this:

scanf("%s/%d/%f", name, &age, &wage);

and I enter this as standard input:

james/20/34000.25

I get the string james/20/34000.25 for the string value and 0 and 0.00000 respectively for the integer and float values. I thought scanf would treat the slashes the same as it would treat the spaces in the first version. How would I get it so if a user enters a slash separated value, I can appropriately assign the values to variables?

user1427661
  • 11,158
  • 28
  • 90
  • 132
  • 1
    There's something *crucial* missing in every answer I ever see about scanf. How do you tell whether or not the `%s`, `%d`, `%f`, `%[^/]` or `%[^ /] format specifications were successfully matched? What happens if the user simply presses CTRL+Z in Windows, or CTRL+d in Linux to close stdin? scanf *returns* and your program is happy to use garbage values? What does scanf *return*? *Scanf should do this! Why does it do that, instead?* Because [the manual](http://pubs.opengroup.org/onlinepubs/009695399/functions/scanf.html) said so. Have *you* read the manual? – autistic Mar 11 '13 at 03:03
  • ... and in some cases, the return value isn't ignored; It's compared against -1 to check for file errors, but not against any *positive* number to check for conversion errors! -sigh- Why ask questions before attempting to read the manual? The manual answers your questions before they become problems; Every problem you spend an hour diagnosing has a manual you could have read to solve fifty other problems (in addition to this one). Not to mention dragging other man hours into it... Why were manuals written, if not to be read by those learning about the relevant functions? – autistic Mar 11 '13 at 03:06
  • It's very difficult to learn form the manual when you are not confronted with the actual problem. I read more than most programmers do, but I still rarely feel like I know something until I've had to debug it and wrestle with it personally. Besides, the C manual isn't exactly a paragon of clarity, and it helps to have real people point out what is specifically wrong with MY code. Nonetheless, thanks for the info! – user1427661 Mar 11 '13 at 03:10
  • 1
    Wrong. The actual problem with your code is that you don't know how to use scanf, yet you're using it anyway. A lot of people have already had the same problem as you, and there are more and more and more problems following it which you'll need to ask questions about if you continue to learn using this method. The manual is there to solve your problem, and tell you how to use it correctly. Find the part of the scanf manual that talks about the 's' format specifier, and tell me what it says. Find the part of the scanf manual that talks about the return value, and tell me what it says. – autistic Mar 11 '13 at 03:14
  • Dude, the thought that I could even do the whole slash thing came from a man entry in the first place: "White space (such as blanks, tabs, or newlines) in the format string match any amount of white space, including none, in the input. Everything else matches only itself." I thought "everything else matches only itself" meant that I could put a %s followed by a /, scanf would match it, and move onto the next modifier. Not every reading of the manual can be perfect the first time around, and I still say that reading the manual BEFORE a problem arises has limited utility. – user1427661 Mar 11 '13 at 03:29

2 Answers2

8

%s matches non-whitespace characters (and it's a greedy match: it's not going to backtrack to see if some other match is possible) To match non-forward slash characters, use %[^/]

(also, note that the space character (match zero or more whitespace characters) in the scanf string has a very different behavior from a non-space non-percent character, such as '/' (match exactly '/')

Cubbi
  • 46,567
  • 13
  • 103
  • 169
3

To match both the space-separated and the slash-separated inputs, you'll need a modestly complex format string:

if (scanf("%[^ /]%*1[ /]%d%*1[ /]%f", name, &age, &wage) == 3)
    ...data was read properly...
else
    ...something went wrong...

The first conversion specification is a scan set that accepts a sequence of non-blanks, non-slashes (so it will stop at the first blank or slash). It would be best to specify an upper bound on how many characters will be accepted so as to avoid stack overflow; for example, if char name[32];, then %31[^ /] (note the off-by-one). The second conversion specification %*1[ /] accepts a single character (1) that is either a blank or slash [ /], and does not assign it to any variable (*). The third conversion specification is a standard numeric input, skipping leading blanks, allowing for negative numbers to be entered, etc. The fourth conversion specification is the same as the second, and the fifth is a standard format for a float (which means that 34000.25 with 7 significant digits is at the outer end of the range of representable values).

Note that the 'something went wrong' part has a difficult time reporting the error coherently to the user. This is why many people, myself included, recommend against using scanf() or fscanf() and prefer to use fgets() or perhaps POSIX getline to read a line from the user and then use sscanf() to analyze it. You can report the problems much more easily. Also note that the return value from scanf() is the number of successful assignments; it does not count the conversion specifications that include *.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278