How to read a file line by line in C?

I have a text file with up to 100 IP addresses, 1 per line. I need to read each address, as a string, into an array called a "list". First, I assume that the “list” should be a two-dimensional char array. Each IP address is 11 characters long, 12 if you include '\ 0', so I declared the list as follows:

char list[100][12];

Then I try to use fgets to read the stream:

  for (i = 0; i < 100; i++) { if (feof(stream)) break; for (j = 0; j < 12; j++) fgets(&list[i][j], 12, stream); count++; } 

To check if the lines are read correctly, I try to output them:

  for (i = 0; i < 5; i++) { for (j = 0; j < 11; j++) printf("%c", list[i][j]); printf("\n"); } 

After starting the program, it is clear that something is wrong. Being a newbie, I'm not sure what, but I assume that I am reading the file incorrectly. There are no errors. It compiles, but prints a weird address on two lines.

Edit:

I replaced the fgets code as follows:

 for (i = 0; i < 100; i++) { if (feof(stream)) break; fgets(list[i], 12, stream); count++; } 

Now it prints five lines, but they are "random" characters from memory.

+1
source share
7 answers

First, reading:

  for (j = 0; j < 12; j++) fgets(&list[i][j], 12, stream); 

You have a big problem here. This is an attempt to read a string in each subsequent character in your array.

All in all, I think you are doing this a lot harder than it should be. Think of your array as 100 lines, and fgets will work with the line at a time. This means that reading might look something like this:

 for (i=0; i<100 && fgets(list[i], 11, string); i++) ; 

There is another small detail: fgets() usually saves a new line at the end of each line. Thus, you may need to leave space for 13 characters (11 for the address, 1 for the new line, 1 for the NUL terminator), otherwise you can read the data in a temporary buffer and copy it to your list only after you disconnect the new line .

In your current code for printing lines, you work one character at a time, which may work, but is unnecessarily complicated. Several people have suggested using the% s printf transform, which is beautiful in itself. However, for this you need to simplify indexing a bit. Printing the first six addresses will look something like this:

 for (i=0; i<6; i++) printf("%s", list[i]); 
+6
source

Your fgets call reads up to 11 characters from the stream to the array. Therefore, you do not want to call it once for each character of each line.

Just think of these loops: with i = 0 and j = 0 it reads up to 11 characters to &list[0][0] . Then, when i = 0 and j = 1, it reads 11 more characters in &list[0][1] . This is wrong for two reasons: it overwrites the result of the last call and potentially writes more bytes than the list [0] can hold.

+4
source

The newline character causes fgets to stop reading, but is considered a valid character and therefore it is included in the line copied to str.

You can read the first 12 characters on the first call of fgets, then the second call will catch a new line, and the third call will receive the next line.

Try using fgets with a limit of 15 characters and expanding your buffer.

+1
source

The second cycle is not needed, and it damages your memory. You should do something like this,

 for (i = 0; i < 100; i++) { if (feof(stream)) break; fgets(&list[i][j], 12, stream); count++; } To check to see if the strings were read properly, I attempt to output them: for (i = 0; i < 5; i++) { printf("%s\n", list[i]); } 
+1
source

for (i = 0; i <100; i ++) {

  if (feof(fp)) break; fscanf(fp,"%s\n",list[i]); 

}

+1
source

I wrote a function to read lines. I think it should be safe.

Check: io_readline

https://github.com/arhuaco/junkcode/blob/master/junk/misc/atail.c

+1
source

Do not use feof() as a loop condition; it will not return to its original state until you try to read the end of the file, which means that your loop will execute too many times. Check the result of your input call (do you use fgets() or fscanf() ) to check if it succeeded, and then check feof() if you get an error condition.

 if (fgets(buffer, sizeof buffer, stream) != NULL) { // process the input buffer } else if (feof(stream) { // handle end of file } else { // handle read error other than EOF } 

fgets() reads entire lines, not single characters, so you don’t want to pass the address of every single character in your line. Call it like this:

 if (fgets(list[i], sizeof list[i], stream) != NULL) { // process input address } 

And now, for Bode, the usual spiel about arrays and pointers ...

When a majority expression is expressed in most contexts, the type of the expression is implicitly converted from "N-element array of T" to "pointer to T", and the value of the expression is the address of the first element of the array. The exceptions to this rule are that the array expression is the operand of the sizeof or & operators, or it is a string literal that is used as the initializer in the declaration. When you hear people say that “arrays and pointers are the same thing,” they distort this rule. Arrays and pointers are completely different animals, but they can be used interchangeably in some contexts.

Note that in the above code, I passed list[i] as the first argument to the fgets () function without any decoration (for example, the & operator). Despite the fact that the type list[i] is a “12-element char array”, in this context it is implicitly converted to the type “pointer to char”, and the value will be the address list[i][0] . Note that I also passed this expression to the sizeof statement. In this case, the type of the array expression is not converted to a pointer type, and the sizeof operator returns the number of bytes in the array type (12).

Just to nail it:

  Expression Type Implicitly converted to
 ---------- ---- ----
 list char [100] [12] char (*) [12] (pointer to 12-element array of char)
 list [i] char [12] char *
 list [i] [j] char N / A

What all this means is that fgets() will read up to the next 12 characters (assuming it doesn't get to a new line or EOF) and save it starting with list[i][0] . Note that fgets() will write a terminating null character (0) to the end of your line. Note also that if fgets() is encountered with a new line, but there is space in the target array, and the terminating nul, fgets() will store the terminating line of the new line before the nul character. Therefore, if your input file has a line like

 1.1.1.1\n 

then the contents of your input buffer after reading will be "1.1.1.1\n\0xxx" , where x is a random value. If you do not want to use a new line, you can use the strchr() function to find it and then overwrite it with 0:

 char *newline; ... if ((newline = strchr(input[i], '\n')) != NULL) { *newline = 0; } 

Since fgets() stops on the next new line, and since your input buffer is 12 characters long, you may run into a situation where you have a new line as the next input character in the file; in this case fgets() will only write this new line for the input buffer, so you will have some empty entries, which is probably not what you want. You can add extra bytes to the input buffer to avoid this situation.

Putting it all together:

 char list[100][13]; ... for (i = 0; i < 100; ++) { if (fgets(list[i], sizeof list[i], stream) != NULL) { char *newline = strchr(list[i], '\n'); if (newline != NULL) *newline = 0; printf("Read address \"%s\"\n", list[i]); count++; } else if (feof(stream)) { printf("Reached end of file\n"); break; } else { printf("Read error on input; aborting read loop\n"); break; } } 
+1
source

All Articles