chrisgjh chrisgjh - 1 year ago 154
C Question

how to use both scanf and fgets to read a file

I need to read following text file:

2 2
Kauri tree
0 0 W S
0 1 E N

I wanted to use the
to get the first line and use the
for the second and the third line then use the
again for the rest of the lines.

I wrote the code like this:

#include <stdio.h>

#define NUM_OF_CHAR 2

int main()
int node, edge;
scanf("%d %d", &node, &edge);

FILE* fp;
fp = stdin;

char* str[NUM_OF_CHAR];

for (int i = 0; i < node; i++) {
fgets(str[i], 2, fp);
printf("%s", str[0]);

the input I entered was:

2 2

I got
Segmentation fault

I saw a similar question here, a person mentioned that I can call
once for getting the first line but ignore it and then use
again to get the second line. But I don't know how to do it.

Answer Source

Local variables defined inside functions will, unless explicitly initialized, have an indeterminate value. For pointers that means they are pointing at a seemingly random location. Using any uninitialized variable, except to initialize it, leads to undefined behavior.

What happens here is that fgets will use the (uninitialized and seemingly random) pointer and use it to write into the memory it points to. This memory is in most cases not belonging to you or your program, and might even overwrite some other important data. This can lead to crashes, or other weird behavior or results.

The simplest solution is to make str an array of arrays of characters, like

#define NUM_OF_STRINGS 2
#define STRING_LENGTH 64
fgets(str[i], sizeof str[i], stdin);

You need to make sure that STRING_LENGTH above is enough to fit every string including the newline and the string terminator. In the case of what I show above with it being 64 that means you can have lines of at most 62 characters.

Now as for the other problem I pointed out, with the first call to fgets reading an empty line.

If you have the input

2 2

The input is stored in a buffer in memory, and then scanf and fgets reads from this buffer. The buffer will, with the above input, look something like this

|  2 |  2 | \n |  h |  e |  l |  l |  o | \n |

After the scanf call read the two numbers the input buffer looks like

| \n |  h |  e |  l |  l |  o | \n |

So what the very first call to fgets in the loop will see is the newline. So it reads that newline and then it's done, leaving the string "hello\n" in the buffer for the second call to fgets.

There are a few ways to solve this problem. The one I personally prefer is to use fgets universally to read lines, and if you need a simple parsing of the line, then use sscanf (note the leading s, also please see here for a good reference of all scanf variants) to do so.

Another way is to simply read characters from the input, one character at a time, and discarding them. When you read a newline, stop the loop and continue with the rest of the program.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download