CroCo
CroCo

Reputation: 5741

Why "%d" is not equivalent to " %d" as a format string in scanf

I'm reading a book and solving some problems. The question is

For each of the following pairs of scanf format strings, indicate whether or not the two strings are equivalent. If they're not, show how they can be distinguished.

(a) "%d" veruss " %d"

(b) "%d-%d-%d" versus "%d -%d -%d"

(c) "%f" versus "%f "

(d) "%f,%f" versus "%f, %f"

My solution is (a) they are equivalent since scanf discards the white space. For (b) they are not equivalent since scanf matches - with white space . For (c), they are not equivalent since scanf will put back the white space in the buffer. For (d), they are equivalent since scanf discards white space. According to the Chegg solutions, all preceding questions are not equivalent. Am I wrong? In this post, I would like to make sure that my answers are correct in comparison with Chegg solutions. I've already read the book and I have decent knowledge about scanf.

Upvotes: 5

Views: 821

Answers (2)

chux
chux

Reputation: 154218

"%d" and " %d" are the same per OP's reasoning.

They are certainly the same with expected numeric input like "123" and " 456". A remaining consideration would be where is the FILE pointer on failure as with "abc" versus " xyz"? "%d" by itself, first consumes leading white-space. So no difference.

... A conversion specification is executed in the following steps: C11dr §7.21.6.2 7

Input white-space characters ... are skipped, unless the specification includes a [, c, or n specifier. §7.21.6.2 8

then the conversion of text to numeric input (for "%d") happens.

Below code demonstrates equivalence.

void next_testi(const char *s, const char *fmt, const char *pad) {
  rewind(stdin);
  int i = 0;
  int count = scanf(fmt, &i);
  int next = fgetc(stdin);
  printf("format:\"%s\",%s count:%2d, i:%2d, next:%2d, text:\"%s\"\n", //
      fmt, pad, count, i, next, s);
}

void next_test(const char *s) {
  FILE *fout = fopen("test.txt", "w");
  fputs(s, fout);
  fclose(fout);

  freopen("test.txt", "r", stdin);
  next_testi(s, "%d", " ");
  next_testi(s, " %d", "");
  puts("");
}

int main() {
  next_test("3");
  next_test(" 4");
  next_test("");
  next_test(" ");
  next_test("+");
  next_test(" -");
  next_test("X");
  next_test(" Y");
}

Output

format:"%d",  count: 1, i: 3, next:-1, text:"3"  // scanf() return value 1:success
format:" %d", count: 1, i: 3, next:-1, text:"3"

format:"%d",  count: 1, i: 4, next:-1, text:" 4"
format:" %d", count: 1, i: 4, next:-1, text:" 4"

format:"%d",  count:-1, i: 0, next:-1, text:""  // scanf() return value EOF, next is EOF
format:" %d", count:-1, i: 0, next:-1, text:""

format:"%d",  count:-1, i: 0, next:-1, text:" "
format:" %d", count:-1, i: 0, next:-1, text:" "

format:"%d",  count: 0, i: 0, next:43, text:"+" // scanf() return value 0
format:" %d", count: 0, i: 0, next:43, text:"+"

format:"%d",  count: 0, i: 0, next:45, text:" -"
format:" %d", count: 0, i: 0, next:45, text:" -"

format:"%d",  count: 0, i: 0, next:88, text:"X"
format:" %d", count: 0, i: 0, next:88, text:"X"

format:"%d",  count: 0, i: 0, next:89, text:" Y"
format:" %d", count: 0, i: 0, next:89, text:" Y"

Upvotes: 2

Sourav Ghosh
Sourav Ghosh

Reputation: 134396

You are correct, with a little modification to option (c) reasoning.

"scanf will put back the white space in the buffer."

I don't really understand your version, but actually, scanf() literally matches the input with the supplied format string, and alongside %f conversion specifier, a single non-leading whitespace needs to be matched with any number of whitespaces, until a non-whitespace is read.

Quoting C11, chapter §7.21.6.2

A directive composed of white-space character(s) is executed by reading input up to the first non-white-space character (which remains unread), or until no more characters can be read. The directive never fails.

Upvotes: 1

Related Questions