2

I'm learning how to write and read files in C, and I wrote a text using this code

 FILE *f = fopen("testingText.txt", "w");
 char *text = "This is text1...";
 fwrite(text, sizeof(char), strlen(text), f );
 fclose(f);

and when I read the content of this file and print it using this code

 FILE *f = fopen("testingText.txt", "r");
 fseek(f, 0, SEEK_END);
 unsigned int size = ftell(f);
 fseek(f , 0, SEEK_SET);
 char *content = (char *)malloc(size);

 fread(content, sizeof(char), size, f);
 printf("File content is...\n%s", content);


 free(content);
 fclose(f);

it gives the result with strange things like these

File content is... This is text1...Path=C:*┬#æ╩eò*

and when I run the code again it gives different strange things.

  • 4
    Whoever is teaching you to use `fseek()`/`ftell()` to get the size of a file is wrong. [For example](https://port70.net/~nsz/c/c11/n1570.html#7.21.9.4p2): "For a text stream, its file position indicator contains unspecified information, usable by the fseek function for returning the file position indicator for the stream to its position at the time of the ftell call; **the difference between two such return values is not necessarily a meaningful measure of the number of characters written or read**." See https://stackoverflow.com/questions/8236/how-do-you-determine-the-size-of-a-file-in-c – Andrew Henle Jan 19 '22 at 09:19
  • 1
    Nevermind with `unsigned int size = ftell(f);`, what happens if the file is larger than can be held in an `unsigned int`? – Andrew Henle Jan 19 '22 at 09:20

2 Answers2

5

There is no null terminator in the file so you'll need to add that manually before printing what you've read from the file.

Example:

char *content = malloc(size + 1);               // +1 for the null terminator
size_t chars_read = fread(content, 1, size, f); // store the returned value
content[chars_read] = '\0';                     // add null terminator
printf("File content is...\n%s\n", content);    // now ok to print
Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
4

The following line is wrong:

printf("File content is...\n%s", content);

Using printf with the %s conversion format specifier requires a null-terminated string. However, your string is not null-terminated.

In order to print a sequence of characters that is not null-terminated, you can write the following instead:

printf( "File content is...\n%.*s", (int)size, content );

Or you can add a terminating null character manually, with the following line:

content[size] = '\0';

However, this will write to the memory buffer content out of bounds, because you did not allocate any space for the null terminating character. Therefore, you should allocate one additional byte in the malloc function call.

Another problem is that using ftell is not a reliable way to determine the length of the file. The ISO C standard does not guarantee that this will work.

For example, on Microsoft Windows, this will give you the length of the file in binary mode (even when the file is opened in text mode). However, the length of the file in text mode is different, because \r\n line endings get translated to \n on Microsoft Windows.

Therefore, if you want to read the content of a text file of unknown length, it would probably be better to read one line at a time in a loop, using the function fgets:

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
    FILE *fp;
    char line[100];

    //attempt to open file
    fp = fopen( "testingText.txt", "r" );

    //verify that file is open
    if ( fp == NULL )
    {
        fprintf( stderr, "error opening file!\n" );
        exit( EXIT_FAILURE );
    }

    printf( "File content is...\n" );

    //print one line per loop iteration
    while ( fgets( line, sizeof line, fp ) != NULL )
    {
        //the following code will work even if "fgets" only
        //reads a partial line, due to the input buffer not
        //being large enough

        //print the line to standard output
        fputs( line, stdout );
    }

    //cleanup
    fclose( fp );
}
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39