1

If I'm not wrong, library function int fscanf(FILE *stream, const char *format, ...) works exactly the same as function int scanf(const char *format, ...) except that it requires stream selection. For example if I wanted to read two ints from standard input the code would look something like this.

int first_number; 
int second_number; 
scanf("%d%d", &first_number, &second_number);

There's no point of me adding newline character in between format specifiers even though the second number is entered in next line of input? Function just looks for next decimal integer right? What happens when I enter two characters instead of ints? Why the function sometimes doesn't work if there's a space between format specifiers?

In addition to that. When reading from file with fscanf(..), lets says the txt file contains next lines:

P6
255
1920 1080

Do I need to specify next line characters in fscanf(..)? I read it like this.

FILE *input = ..
char type[2];
int tr;
int width; int height;
fscanf(input, "%s\n", &type);
fscanf(input, "%d\n" &tr);
fscanf(input, "%d %d\n", &width, &height)

Is there a need for \n to signal next line? Can fscanf(..) anyhow affect any other functions for reading files like fread()? Or is it a good practice to just stick to one function through the whole file?

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
enuliver
  • 33
  • 4
  • 3
    No, don't add a newline character to `scanf`format string. Specifier `%d` automatically filters all whitespace from the input. Please see [What is the effect of trailing white space in a scanf() format string?](https://stackoverflow.com/questions/19499060/what-is-the-effect-of-trailing-white-space-in-a-scanf-format-string) Most format specifiers for `scanf` automatically filter leading whitespace, but `%c` and `%[]` and `%n` do not. They read *every* character, unless you add a space before the `%` which instructs `scanf` to filter leading whitespace here too. – Weather Vane Jun 11 '22 at 18:04
  • The scanf functions treat whitespace (including newline) and a delimiter between numbers. They don't treat non-whitespace (eg letters) as whitespace so that is why you need to include a way to skip over them in your code if text is included. – Hogan Jun 11 '22 at 18:06
  • and always restrict the input like `fscanf(input, "%1s", type);` Remove the `&` too. And don't use such miserably tiny buffers without a good reason: there's no $fee, rather the reverse: buffer overflow will *cost* you a lot of time. – Weather Vane Jun 11 '22 at 18:08
  • "Is it a good practice to just stick to one function through the whole file?" Yes, because different input functions handle whitespace in different ways. – Weather Vane Jun 11 '22 at 18:11
  • 3
    If you have line-based input, use `fgets` to get a line and `sscanf` or something else like `strtol` to parse it. And always check the return value of any `scanf` function. – Cheatah Jun 11 '22 at 18:18
  • 1
    I’m voting to close this question because it asks about information that is in the ordinary documentation for the routines and that should be learned by reading that documentation or reading a C primer or textbook rather than by asking on Stack Overflow. – Eric Postpischil Jun 11 '22 at 19:04

2 Answers2

2

scanf(...) operates like fscanf(stdin, ....).

Unless '\n', ' ', or other white spaces are inside a "%[...]", as part of a format for *scanf(), scanning functions the same as if ' ', '\t' '\n' was used. (Also for '\v, '\r, '\f.)

// All function the same.
fscanf(input, "%d\n" &tr);
fscanf(input, "%d " &tr);
fscanf(input, "%d\t" &tr);

There's no point of me adding newline character in between format specifiers even though the second number is entered in next line of input?

All format specifiers except "%n", "^[...]", "%c" consume optional leading white-spaces. With "%d" the is no need other than style to code a leading white-space in the format.


Function just looks for next decimal integer right?

Simply: yes.


What happens when I enter two characters instead of ints?

Scanning stops. The first non-numeric input remains in stdin as well as any following input. The *scanf() return value reflects the incomplete scan.


Why the function sometimes doesn't work if there's a space between format specifiers?

Need example. Having spaces between specifiers is not an issue unless the following specifier is one of "%n", "^[...]", "%c".


When reading from file with fscanf(..), .... Do I need to specify next line characters in fscanf(..)?

No. fscanf() is not line orientated. Use fgets() to read lines. fscanf() is challenging to use to read a line. Something like

char buf[100];
int cnt = fscanf(f, "%99[^\n]", buf);
if (cnt == 0) {
  buf[0] = 0;
}
if (cnt != EOF) {
  cnt = fscanf(f, "%*1[^\n]");
}

I read it like this. ... fscanf(input, "%s\n", &type); fscanf(input, "%d\n" &tr); ....

"it" as in a line is not read properly as "%s", "%d", "\n" all read consume 0, 1, 2, ... '\n' and other white-spaces. They do not read a line nor just the 1 character of the format.

Further "\n" does not complete upon reading 1 '\n', but continues reading all white-spaces until a non-white-space is detected (or end-of-file). Do not append such to the end of a format to read the rest of the line.

If want to read the trailing '\n', code could use int cnt = fscanf(input, "%d%*1[\n]" &tr);, but code will not know if it succeeded in reading the trailing '\n' after the int. It will have simply read it if it was there. Could use other formats, but really, using fgets() to read a line is better.


Is there a need for \n to signal next line?

No, as a format "\n" reads 0 or more whites-spaces, not just new-lines.


Can fscanf(..) anyhow affect any other functions for reading files like fread()?

Yes. All input function affect what is available next for other input functions. Mixing fread() and fscanf() is challenging to get right.


is it a good practice to just stick to one function through the whole file?

It certainly is simpler. I recommend to use input functions as building blocks for a helper function to handle your file input.


Tip: Read lines with fgets(), then parse. Set fscanf() aside until you understand why it has so much trouble with unexpected input.

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Thank you for clearing things up. I normally use fgets() which as you mentioned makes things simpler. But in some cases I cannot predict how long the line will be so fgets is a no go. Anyway thanks for the comprehensive answer. – enuliver Jun 11 '22 at 20:01
  • @anon Curious, How do you expect to use `fscanf()` with a _cannot predict how long the line will be_ concern? – chux - Reinstate Monica Jun 11 '22 at 22:26
  • Let's say I open a file. I know that there's two numbers written right at the begging (as chars). These numbers could be of different lengts each time. Also they can be left/right or also inbetween padded with extra spaces. Let's say that numbers would fit into integer. With fscanf I could easily read the numbers into two variables. And if i were to use fgets I would have to counter in these extra spaces right? I might be off on how the fgets works but I think that case described above is better suit for fscanf. – enuliver Jun 11 '22 at 23:43
  • @anon Given "I know that there's two numbers written" --> `char buf[100]; fgets(buf, 100, f);` is certainly enough. If you want to handle a pathologic first line with 100s of leading spaces then it is not, but then if the first line was empty and the next line had the number, using `fscanf("%d", &a);` would fail to indicate the first line was empty. Both approaches have select malformed inputs that are difficult to handle. IMO, `fgets()` and then parsing is easier to catch more of them. Perhaps your experience will differ. Good luck. – chux - Reinstate Monica Jun 12 '22 at 01:46
1

The %d conversion specifier tells scanf and fscanf to skip over any leading whitespace, then read up to the first non-digit character, so you don’t need to put a newline between the two %d in the scanf call - in fact, if you do that, it means you have to have a newline between your inputs, not just blanks.

Most conversion specifiers skip over leading whitespace - the only ones that don’t are %c and %[, so you’ll want to be careful when using them.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • 1
    Technically `"%n"` doesn't discard leading whitespace either :) – David C. Rankin Jun 11 '22 at 19:27
  • @DavidC.Rankin: :p~~~ – John Bode Jun 11 '22 at 19:48
  • "if you do that, it means you have to have a newline between your inputs, not just blanks." is amiss. With input `3-4\n` or `3 -4\n`, `int a,b; int c = scanf("%d\n%d", &a, &b); printf("%d %d %d\n", a,b,c);` nicely prints `3 -4 2\n`. Same as if format was `"%d%d"`. No in-between white-space needed on input. – chux - Reinstate Monica Jun 11 '22 at 22:32