On the computer’s physical disk information is stored in files. This post will teach you how to open, write, read and close a file in C (examples included).




Today you’re going to learn how to open, read and write files using C.

Until now, you’ve learnt how to manage temporary data, but what if you wanted to save some pieces of information when you close your program?

To do that you have to store them on your computer’s physical disk.

Since variables are memorised on the RAM and this one is a temporary form of memory, each time you close your program, you lose all of its data.

On the other hand, files are kept even if you shutdown your machine, therefore you’ll learn how to save important information there.

Creating and opening files


Creating and opening a file are similar processes.

They both use the following function:

Where:

  • filename is a string which indicates the file’s location. When you look for the syntax of a function you can find the const char * type, that usually stands for a string.
  • mode is another string that can take only certain fixed values – which are listed in a table below. This parameter indicates how the machine should read and write the file (e.g.: read-only, new file, etc…)
  • It returns a pointer to a structure called FILE ( all uppercase ) which is defined in the stdio.h library and which contains some information about the file, such as its current reading/writing position – obviously, in order to not read the same character or word forever, a program needs to ‘remember’ where and what it’s reading.

There are 6 possible modes ( plus other 6 for binary versions):

Mode Description
“r” It opens a file just for reading (it starts from the beginning and the file must exist).
“w” It creates a file (if it doesn’t exist) or it deletes all the previous content (if the file exists). It can only write on it (starting from the beginning).
“a” It opens a file and it writes on it starting from its end. All the previous content is NOT deleted. If the file doesn’t exist, it’s created.
“r+” It opens a file for both reading and writing (the file must exist).
“w+” It opens a file for both reading and writing. If the file exists, it deletes all its previous content, otherwise it creates it.
“a+” It opens a file for both reading and writing. If the file exists, it starts writing from the end, otherwise it creates it. It can read from any position, but it can write only at the end.

The following chart can explain better the differences between the six main modes:

Differences between file opening modes

Binary vs text modes

By adding a ‘b’ to one of the 6 main modes, you can open a file in binary mode.

Therefore you get “rb”, “wb”, “ab”, etc…

When you open a file in binary mode, it’s content is read as it is, while when you open it in text mode ( without the ‘b’ ), some characters, depending on your operative system, are converted into others (e.g.: on Windows ‘\n’ is translated to ‘\r\n’).

The FILE variable

When you open a file, its data are stored in a variable type defined in stdio.h and called FILE.

In order to read or write on that file, you have to use a pointer to the FILE type.

For example, the following code opens a file called “Hello.txt” in read-only mode and assigns its information to a pointer called file:

If you look at the chart above, you can see that you can open a file with the “r” mode only if it exists.

What happens when the file doesn’t exist?

The function fopen will return a NULL pointer and so that would be the value of file.

In fact, if you want to check if your program managed to open a file, you can use the following code:

Default streams


First of all, what is a stream in programming?

A stream is a connection between a program and a source/device. All input and output data are transferred through streams.

The connection between your program and a file is a stream, as well as its connections with the command prompt.

Both files and terminal use streams

The FILE type doesn’t contain anything, but the information of a stream.

Even printf and scanf use streams, more precisely standard ones.

There are the three standard streams, which allows a program to communicate with the terminal.

Stream Description
stdout Output to terminal
stdin Input from terminal
stderr Output to terminal (used for errors)

printf writes on the stdout stream, while scanf reads from the stdin stream.

Later in this post we will see how to use a custom stream with those functions.

Closing a file


Usually all files are closed when the program stops, but when it crashes, it can happen that some files stay open.

Therefore it’s good practice to close them by code when you don’t need them.

To do that, use the following function:

Where file is the pointer you got from fopen.

Reading from a file


We’re going to see 4 ways to read from a file:

  • fgetc
  • fscanf
  • fgets
  • fread

Each function reads something

fgetc

This function reads one character from a stream/file.

Syntax:

Where stream is the file to read.

It returns the character read.

Remember that in C characters are represented by integers, so you can assign the result of fgetc either to an int or a char variable.

Example:

fscanf

This is a version of scanf that allows you to specify which stream to use.

Syntax:

It is identical to scanf, apart from a new parameter at the beginning: stream.

fgets

It reads a line from a stream, but it stops when it reaches the maximum number of character, specified by the programmer

Syntax:

Where:

  • line is the variable (string) in which you want to memorise the line read.
  • length is the size of the array that you’re using for the previous parameter.
    Since a string must end with a null terminator, fgets reads a maximum of length-1 characters and the last element of the array is set to ‘\0’.
  • stream is the file to read.

I’ve already talked about fgets, so if you want more information read this post’s section: Learn C programming: Strings and Characters : Reading and Writing strings

fread

It reads a specified amount of data from a file.

Syntax:

Where:

  • size_t is a data type defined in stdio.h. It contains the size of a variable or a type, measured in bytes.
    Program: How many bytes do I have to read?
    size_t: 10

    Got it? Good.
  • pointer is the array in which you want to save the data read.
  • size is the size in bytes of each element of pointer
  • members is the number of elements to read.
  • stream is the file to read.

This function returns the total amount of bytes read and, if the operation was successful, it’s equal to size*members.

Note: if fread reached the file’s end, it can have returned a value different from size*members, because, obviously, there is nothing more to read.

If you don’t know the size of a data type, you can get it through the sizeof macro, which syntax is:

For example sizeof(char) returns 1.

Read more about sizeof from GeeksforGeeks

Example:

Writing on a file


We’re going to see 4 methods to write on a file:

  • fputc
  • fprintf
  • fputs
  • fwrite

Each function writes something

fputc

This function writes one character on a file or stream.

Syntax:

Where:

  • ch is the character to write
  • stream is the file in which ch has to be written

fprintf

It’s the version of printf that allows you to specify the stream to use.

Syntax:

It works just like printf, but you can set where to write the string.

fputs

It writes a string to a stream.

Syntax:

Where:

  • string is the string to write
  • stream is the file in which string has to be written

fwrite

It writes a specified amount of data on a file.

Syntax:

Where:

  • pointer is the array that contains the data to write.
  • size is the size in bytes of each element of pointer
  • members is the number of elements to write.
  • stream is the file to write.

This function, just like fread, returns the amount of bytes written.

However, in this case, if the value returned isn’t equal to size*members, there’s definitively an error.

Other useful functions: feof, fseek and ftell


In this section, we’re going to see how to:

  • check the program reached the file’s end;
  • change the position of the stream’s indicator;
  • get the current position of the indicator.

feof

When the program reaches the end of a file, it gets a value called EOF (End-of-file).

The function feof can tell if the program reached the EOF and it returns a non-zero value if it did.

Remember: a non-zero value in C means true.

If you want to check if your program got to the end of a file, use this code:

fseek

When you read or write on a file, the indicator advances automatically.

If you want to move it to another position and start reading/writing from there, you have to use the function fseek.

Its syntax is:

Where:

  • stream is a stream
  • offset is the number of bytes to offset from whence
  • whence is the position from which offset is added. It can only take the three value listed below.

Possible values of whence:

Value Description
SEEK_SET Beginning of stream
SEEK_CUR Current position of stream‘s indicator
SEEK_END End of stream

ftell

This function returns the current position of the indicator of a stream.

Like feof and fclose, it just requires a stream to work.

Bonus example: getting the size of a file

Since there isn’t a function to get the size of a file, you can use fseek and ftell to do it yourself.

It’s simple: first you move the file’s indicator to the end using fseek, then you get its position in bytes through ftell.

Eventually, you can use fseek again to move the indicator where it was before.

Let’s analyse today’s program


Today’s program copies the content of an existing file “from.txt” into a new file, called “to.txt”.

#define FILE_1 "from.txt"

It defines a constant called FILE_1 with value “from.txt”.

If you want to know more about constant, you can have a look at this post: Learn C programming: Data types, Modifiers and Qualifiers

FILE *f1 = fopen(FILE_1, "r")

This line uses the function fopen to open the file FILE_1 in read-only mode. Then, the information about the new stream is assigned to the pointer f1.

Since FILE_1 contains only the file’s name and extension, the document must be in the same directory of your program. If you want to open a file in another directory, you have to use its path – that long string that start with “C:\\” or something similar.

if(f1 == NULL)

This condition checks if there was an error during opening. If there was, f1 must be equal to NULL.

fprintf(stderr, "Can't open %s\n", FILE_1)

I used the version of printf that you’ve learnt today to write an error message to the errors’ stream ( stderr ), even if the result would be the same as the one got from a normal printf.

return 1

If you’ve read Learn C programming: Functions | basics, recursion and pointers, you should already know that the return statement makes the program exit a function.

When used in main() it ends the whole program.

int length = fread(content, sizeof(char), MAX, f1)

This line reads 500 (MAX) elements of size 1 (the size of a character) from f1 and it puts them in the array content.

Done that, since “from.txt” may contain less than 500 characters, it sets length to the number of bytes actually read.

fwrite(content, sizeof(char), length, stdout)

As you can see, we can use fwrite to print something on the terminal, by using the standard stream stdout.

Therefore, this function prints a length number of elements from the array content.

I think that, in the case of fwrite, it’s important to know how many bytes you are writing, otherwise you might find random characters on your screen (or file).

if(fwrite(content, sizeof(char), length, f2)==length)

The fwrite part is similar to what we’ve just seen, so let’s focus on the condition.

In this line I am checking if the number of bytes written in f2 is the same I told the program to write.

If it is, the operation has been successful, otherwise there has been a problem of some kind.

Note: I wrote only length as second part of the condition, but, if you remember, fwrite returns size*length. However, the size of a character is 1 and length*1 is still length.

C and C++ comparison


When I compared the C and C++ versions of the previous posts’ programs, there were only few differences.

This time, however, the code changes almost completely, so you should read another guide if you’re interested in learning how to manage files in C++.

Here I wrote some links that may help you:

Conclusion


Today you’ve learnt how to manage files in C.

Now you can open, create, read and write – and not only – a file.

Next week’s post will be the last one of the “Learn C” guide and it’ll be about… I won’t spoil it to you, but it concerns memory.

Don’t worry, this blog’s mission is far from finished, because after C you’ll learn other programming languages, just like most great programmers do – and it’ll probably be a lot easier thanks to all the concepts that you learnt here.

I’m also thinking to write a tutorial on making a small game for the terminal and then send it on the newsletter, so subscribe to it!

In the meantime, you may want to read other posts, in order to learn something that you might have skipped or missed. You can find them at the bottom or on the side of this page.

As always, if you have any questions, ask them in a comment and if this post has been useful for you, spread the word – you can use the sharing buttons below.

Anyway, the post is finished, have a good day and we will see next week!

From Zephyro it’s all, Bye!

Categories: Learn C

Leave a Reply

%d bloggers like this: