Introduction
In the previous tutorial, we talked about inheritance which is a way to reuse variables and methods that are common to multiple classes. It also allows for a class hierarchy to be set up so that methods can work on the most generic class possible.
In this tutorial, we are going to discuss reading and writing data to a file. Reading and writing to a file is a required aspect of most programs since it will usually need to interface with external data.
Code
Let’s start by writing a file. This file will have a small header on it that describes how many values are stored in the file. Then comes that number of doubles:
const double PI = 3.14159;
int main()
{
// Create some data
int points = 11;
double *data = new double[points]();
for(int i = 0; i < points; i++)
{
data[i] = sin(i/((double) points - 1)*2*PI);
}
// Write the file
FILE* file = fopen("file.dat", "wb");
fwrite(&points, sizeof(int), 1, file);
fwrite(data, sizeof(double), points, file);
fclose(file);
// Print out what was written
for(int i = 0; i < points; i++)
{
cout << data[i] << endl;
}
delete[] data;
return 0;
}
In order to make the data stored in the file a little more interesting, a sine wave was generated. This has the benefit of being easily checked for accuracy, but not so simple that we could trick ourselves into thinking that the code to read the file was working when it was not. (An example of this might be writing a file of all zeros, and then trying to read it back it.)
Now, that we have some data generated, we are on to the new and exciting part: writing data to the file. The first order of business is to open up a file to write to. To do this we use the fopen (File OPEN) command. The first argument is the name of the file to open. The second is how to open the file. “wb” means to open the file in write mode and that the data that will be written to it is binary.
The fwrite command does the actual writing of data to the file. The first thing that we want to write to the file is the number of data points that will be stored in the file, which we have stored in the points variable. Looking at the arguments for fwrite, it wants a pointer to the data that we wish to write. At first this seems like a problem. We have an integer, we don’t have a pointer to an integer. Remembering back to the pointers tutorial, we can easily get the address of the integer, by using the & operator. The next two arguments describe the data to be written. The first one is how big each value is, and the second is how many values there are. The last argument is the file to write the data to.
Writing the data to the file is very similar to writing the header. In fact it is a little easier because we have a pointer to the data that we want to write.
The last part of writing the data to the file is closing it when we are done. This is extremely important! Just like deleting the data that has been allocated from new, this should be done every time. It is staggering how many programs I have seen crash because they hit the limit of the number of simultaneously open files that the OS would allow. In every case, the program was done with the file, but they forgot to close the file.
The last section just writes the data out to the screen. This way we know what was in the file without having to dig through the binary data with a tool like od.
Now, that we have a file to read in, let’s read that file in:
int main()
{
// Read the file
FILE* file = fopen("file.dat", "rb");
int points = 0;
fread(&points, sizeof(int), 1, file);
double *data = new double[points];
fread(data, sizeof(double), points, file);
fclose(file);
// Print out file contents
for(int i = 0; i < points; i++)
{
cout << data[i] << endl;
}
delete[] data;
return 0;
}
Just like writing a file, the first thing that we need to do is open up the file. This time we will open the file for reading binary data by setting the second parameter to “rb”. For more information about the various modes that a file can be opened into, check out this link.
Now before we can allocate an array to store the data into, we need to know how much data is going to be in the file. Luckily, when we wrote the data, the first thing that we wrote out was how much data is stored in the file. So, we read this in. Notice that we are using a trick very similar to how we wrote it out. We declare a plain old integer and then we get the address of it by using the & operator. The arguments to the fread function are very similar to the fwrite function as well.
Once we have the size of the array, we allocate enough memory to store it using the new operator. After we have a place to store it, we read the data in using the fread function. Always remembering to close the file after we are done with it.
The last section of code writes out the values that are read into the program, so that we can check against the values that we wrote out. Everything checks out, so we know that we were successfully able to read and write data to a file.
Another thing to mention is that the data contained in the file doesn’t have to be basic data types. Structures are another great option. Here’s a slightly modified version of the program where we write the header out using a struct:
const double PI = 3.14159;
typedef struct FileHeader
{
int Points;
int Year;
int Month;
int Day;
} FileHeader;
int main()
{
// Create some data
FileHeader header;
header.Points = 11;
header.Year = 2013;
header.Month = 4;
header.Day = 30;
double *data = new double[header.Points]();
for(int i = 0; i < header.Points; i++)
{
data[i] = sin(i/((double) header.Points - 1)*2*PI);
}
// Write the file
FILE* file = fopen("structFile.dat", "wb");
fwrite(&header, sizeof(FileHeader), 1, file);
fwrite(data, sizeof(double), header.Points, file);
fclose(file);
// Print out what was written
for(int i = 0; i < header.Points; i++)
{
cout << data[i] << endl;
}
delete[] data;
return 0;
}
The reader code would need similar modifications.
Summary
In this tutorial, we learned about reading and writing data to a file.
In the next tutorial, we will learn about strings. Strings are a way to store text. Strings can be thought of arrays of characters, but since they are so common, we give them a special name. It might be surprising to realize, but we have been using strings since the very first tutorial, when we wrote “Hello Raspberry Pi!”.
If you have any questions or comments about what was covered here, post them to the comments. I watch them closely and will respond and try to help you out.
Top Comments