Reading from Files in C Using Fread

fread cWhen working in the C programming language, you can use the standard library function fread() to read binary data from a file stream and store it in an array or other block of memory. This function is available in most implementations of C that allow access to the operating system’s file system. On GNU/Linux operating systems, which have device drivers mapped to the file system, fread() is able to read from hardware devices (particularly character-based devices) too.

In this explanation of the fread() function, it is assumed that you are familiar with the basics of programming in C on your chosen operating system or platform. Beginners may find it useful to take a short course in basic C programming, which explains the essential concepts such as variables, functions, and arrays. C Programming for Beginners at Udemy.com is a more detailed introduction to C, and includes further examples of fread() and C’s other file-handling routines.

Opening and Closing Files

Before you can read from a file, you must open it. And once you have finished using the file, you should close it to allow other applications access. The two functions you use here are fopen() and fclose(). Although the focus of this explanation is on fread(), not general file-handling functions, a brief introduction to these routines might be helpful.

fopen() opens a file for reading or writing, and returns a pointer to a file stream. It accepts two arguments: the first is the file name (optionally including the file path) of the file to be opened; the second is the access mode, which can be one of six settings. The access mode used in the sample code included with this article is “r”, which opens a file for reading. The file must exist when using this argument.

fclose() closes the file and releases any locks. It accepts one argument: the pointer returned by a successful call to fopen().

Reading from a Text File

The example C code below reads the first 200 characters from the specified text file, and writes the information to the console (or shell). To use the code, you should first place a text file in the same directory as your application, or create one using a text editor such as Notepad or Vi.

A detailed look at the call to fread() in this example follows. The source code is from a Windows console application project created in Microsoft Visual Studio, and may require slight adjustments to work in your C compiler – particularly the declaration of the main() function. You should be aware that the compiler is set to use ANSI C89 and so local variables must be declared at the start of functions.

/*
 *
 * Udemy.com
 * Reading from Files in C Using Fread
 *
*/

#include 

int main( int argc, const char* argv[] ) {
    FILE *rm;
    char buf[201];

    printf("\r\nUdemy.com - Reading from Files in C\r\n");

    rm = fopen("ReadMe.txt", "r");
    if (rm != NULL) {
        fread(buf, 1, 200, rm);
        buf[(sizeof buf)-1] = 0;
        printf("%s", buf);
        fclose(rm);
    }
    else
        printf("File Not Found.\r\n");
}

fread() has the following function definition:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

The first argument that it accepts is a pointer to an area of memory that will be hold the data read from the file. In this example, the code specifies an array of chars (buf) as the destination.

The size argument defines the size of a single element being read. The char data type is a single byte, and so the value used here is 1.

nmemb specifies how many elements (of the size specified) to read from the file. A value of 200 reads up to 200 characters from the file.

Finally, the stream argument specifies which file stream to read from – usually the pointer returned from a call to fopen().

There are a few other important points to note in the code above.

  1. The return value of fopen() is checked against null to ensure that the file was opened successfully.
  2. The code does not check that the call to fread() was successful and whether it returned the number of characters requested.
  3. The final element in the buffer array is set to 0 to ensure that the string is null-terminated, prior to sending the result to the console using printf().

If the file contains fewer bytes than the number requested, the call to fread() reads as many values as it can and returns a size_t value indicating how many were read. The example below defines a buffer array of 2000 characters, and then shows how many were actually read from the file stream.

/*
 *
 * Udemy.com
 * Reading from Files in C Using Fread
 *
*/

#include 

int main( int argc, const char* argv[] ) {
    FILE *rm;
    char buf[2000];
    int res;

    printf("\r\nUdemy.com - Reading from Files in C\r\n");

    rm = fopen("ReadMe.txt", "r");
    if (rm != NULL) {
        res = fread(buf, 1, 2000, rm);
        printf("Read %u bytes.\r\n", res);
        fclose(rm);
    }
    else
        printf("File Not Found.\r\n");
}

Reading the Entire File

For small files, such as when loading configuration settings or resources from an external file, it can be useful to read the entire file in one go. To do this, you must first calculate the size of the file in bytes. This often involves library functions – such as fstat(), fseek(), and ftell() – or using the operating-system-specific file handling routines for your target platform. The general approach is the same regardless of how you calculate the size:

  1. Calculate the size of the file to be read.
  2. Use malloc() to allocate an area of memory large enough to hold the data.
  3. Call fread(), specifying the pointer return by malloc() as the destination and the size of the file as the nmemb argument.
  4. Remember to close the file and release the memory allocated by malloc() when you have finished working with the data.

For many applications, it is preferable to loop and read a smaller number of bytes until the end of the file is reached. You can either loop until the returned value of fread() is less than the number of bytes requested, or use the feof() function to detect the end of the file as used here:

rm = fopen("ReadMe.txt", "r");
if (rm != NULL) {
    while (!feof(rm)) {
        res = fread(buf, 1, (sizeof buf)-1, rm);
        buf[res] = 0;
        printf("%s", buf);
    }
    fclose(rm);
}

Reading Structs from Files

All of the preceding examples have been based on the idea of reading bytes from a file stream and storing the results in an array or other block of memory. In C, all data types are made up of at least one byte (with many types consisting of multiple bytes in a set order). However, the memory used by all types can be accessed one byte at a time, and that means it is possible to read any kind of value from a file, provided that it has been saved correctly.

To continue with the examples below, create a new text file using Notepad, Vi, or similar. Enter only four characters – ABCD – with no spaces and no line breaks. Then save the file as “Test.txt” in the directory where the compiler places your compiled executable. The code below reads a short (a 16-bit integer) from the text file by specifying the element size (2) and using the address of a short variable as the destination pointer.

/*
 *
 * Udemy.com
 * Reading from Files in C Using Fread
 *
*/

#include 

int main( int argc, const char* argv[] ) {
    FILE *tf;
    short res;

    printf("\r\nUdemy.com - Reading from Files in C\r\n");
    printf("Size: %u\r\n", sizeof res);

    tf = fopen("Test.txt", "r");
    fread(&res, sizeof res, 1, tf);
    fclose(tf);
    printf("Value: %u\r\n", res);
}

On Windows, the value output will be 16,961 if the first two characters in the text file were A (0x41) and B (0x42). The two bytes read from the file overwrite the two bytes in memory used by the short, giving res the new value 0x4241 (the Intel x86 architecture is little-endian).

Overwriting the area of memory used by a data type is not limited to single variables. This final code sample reads the same four characters from the text file, and overwrites the memory used by a struct. In this way, entire structures can be loaded from external files with very little code.

/*
 *
 * Udemy.com
 * Reading from Files in C Using Fread
 *
*/

#include 

#pragma pack(1)
struct TEST {
    char first;
    short second;
    char third;
} myTest;
#pragma pack()

int main( int argc, const char* argv[] ) {
    FILE *tf;

    printf("\r\nUdemy.com - Reading from Files in C\r\n");

    tf = fopen("Test.txt", "r");
    fread(&myTest, 1, 4, tf);
    fclose(tf);
    printf("First: %c\r\n", myTest.first);
    printf("Second: %u\r\n", myTest.second);
    printf("Third: %c\r\n", myTest.third);
}

When C compilers create structures, they often pad them to a size that is convenient for them to work with. To successfully read the test structure from the text file, you may need to instruct the compiler to use a different packing scheme – in this case, using the pragma directive pack(1) to tell the compiler not to pad the structure.

The process for setting a structure’s packing mode may differ among C compilers.

More File Handling with C

While fread() is one of the most common functions used for reading from files, there are other functions that can be used, and many more functions that you will need to become familiar with in order to work with the computer’s file system from your C programs.

Learn more about the common C functions for file handling in Learn C Programming Language at Udemy.com.