Introduction
Reading and writing files in C isn’t as difficult as it sounds. A few simple loops are all you really need. That said, it’s nice to have a few helper functions ready to drop into a project.
Before we write anything we need to think about the choice between fopen
and open
.
The major differences are fopen
is portable, part of the C standard, and unbuffered.
While open
is technically not portable it’s ubiquitous across *nix systems. Even
Windows as _open
. That said, on Windows you really should use the win32 API functions
and not the _
prefixed POSIX compatibility ones.
I’m going to use fopen
because it is 100% portable (assuming the compiler
supports C89 or higher).
For reading a file I’m going to read into a string builder because it’s makes the process very easy. This is going to read the entire file into memory so care should be taken before using this read function.
Includes
#include <stdlib.h>
#include <stdio.h>
#include <str_builder.h>
Read
The heart of this function is a do...while
loop which keeps reading in chunks
of the file and putting them into the string builder. The chunk size could be
changed and optimized based on the expected data and system the application
will be running on. However, 8192 is a typical multiple of a disk block size
so it should be fairly efficient.
unsigned char *read_file(const char *filename, size_t *len)
{
FILE *f;
str_builder_t *sb;
char *out;
char temp[8192];
size_t mylen;
size_t r;
if (len == NULL)
len = &mylen;
*len = 0;
if (filename == NULL || *filename == '\0' || len == NULL)
return NULL;
f = fopen(filename, "r");
if (f == NULL)
return NULL;
sb = str_builder_create();
do {
r = fread(temp, sizeof(*temp), sizeof(temp), f);
str_builder_add_str(sb, temp, r);
*len += r;
} while (r > 0);
if (str_builder_len(sb) == 0) {
fclose(f);
str_builder_destroy(sb);
return NULL;
}
fclose(f);
out = str_builder_dump(sb, NULL);
str_builder_destroy(sb);
return (unsigned char *)out;
}
Write
Writing the file takes the data directly and can either truncate or
append to the file. Much like read, the main logic is a do...while
loop that moves
though the data.
size_t write_file(const char *filename, const unsigned char *data, size_t len, bool append)
{
FILE *f;
const char *mode = "w";
size_t wrote = 0;
size_t r;
if (filename == NULL || *filename == '\0' || data == NULL)
return 0;
if (append)
mode = "a";
f = fopen(filename, mode);
if (f == NULL)
return 0;
do {
r = fwrite(data, sizeof(*data), len, f);
wrote += r;
len -= r;
data += r;
} while (r > 0 && len != 0);
fclose(f);
return wrote;
}