Accessing Files in C

C Tutorials from Roots - Part 24

Chrys Forcha
Introduction
This is part 24 of my series, C Tutorials from Roots. In this part of the series, we see how to access files in C. In assume that you have read the previous parts of the series before reading this one, because the knowledge here is based on knowledge gained from the previous parts of the series. We shall consider only text files and those, which are in the working directory (so we shall not need to bother about directory path).

Note: If you cannot see the code or if you think anything is missing (broken link, image absent), just contact me at forchatrans@yahoo.com. That is, contact me for the slightest problem you have about what you are reading.

The FILE Object and Stream
In order for you to use a file in the hard disk or some other drive, you have to do what is called opening the file. With this the content of the file is copied into memory. This area in memory that has the content of the file is called a stream. Whatever you want to do to the file (modifying the file, adding text to the end of the file, or just reading the file) you do it to the stream. After that you have to do what is called closing the file. When a file is closed, the content of the stream is copied to the file in the disk. Any modification of stream content or adding of text to the end of the stream is reflected in the file in the disk after closing.

A file may not exist in the disk. This means you have to create it. For this purpose, you still have to use the opening process (see below). A stream for the newly (not existing) opened file is created. You send information to the stream. When you close the file, effectively closing the stream, the content of the stream is copied to the disk for the first time. Closing a file means putting an end to the association between the stream and the corresponding file in the disk, after the content of the stream has just been copied to the file, which might or might not have existed, in the disk.

When you open a file, a derived object is created in memory. This derived object is said to be of type, FILE. This FILE object has the stream of the corresponding file in the disk. When a file is closed, the corresponding FILE object in memory and its stream are no longer useful.

The functions to use for opening and closing files are in the stdio.h header (and corresponding library).

The fopen Function
To open a file, you need the fopen function. The syntax is,

FILE *fopen(const char * restrict filename, const char * restrict mode);

Basically the first argument is the name of the file in quotes and the second argument is a very short string in quotes. We shall see the meaning of the second argument below. The * that precedes the function name means the function returns a pointer. The FILE preceding the function syntax means the returned pointer points to a FILE object. This object would be created and it would have a stream for the corresponding file in the disk. This object of type FILE also corresponds to the file in the disk.

The mode argument, which is the second argument of the fopen function, can have any of the following strings (the meanings of the strings are given):

"r": open text file for reading
"a" open text file to append (add) new text at the end of the file; if the file did not exist in the disk, a new one will be created and save with the content you put in the corresponding stream
"w": open a file for writing; if the file had content, erase everything and replace with new content from program (stream); if the file did not exist, create a new one and save with the content you put in the corresponding stream
"r+": open text file for update (reading and writing anywhere in the stream and save at closing)
"w+": same as "w" above, but for update
"a+": same as "a" above but for update

Note: for simplicity, FILE and Stream are considered as the same things.

When a file is opened with the fopen function, every text from the file in the disk is copied into the corresponding stream. You can then edit the stream. When you close the file the content of the stream is copied to the file in disk. Then the corresponding FILE object and its stream are no longer useful. The syntax to close a file is:

int fclose(FILE *stream);

This fclose function takes as argument the pointer returned by the fopen function. FILE is actually a struct, but do not worry much about that for this basic tutorial.

In order to use the fopen and fclose functions you need to include the stdio.h header. In fact all the functions we shall use to access files in this tutorial come from the stdio.h header file.

In order for us to demonstrate the functions for accessing files we need a text file in the disk. Open you text editor and type the following lines in it, pressing the Enter key at the end of each line:

This is the first line.
This is the second line.
This is the third line.

Save the document with the name, myfile.txt in the working directory.

Read and try the following code, which deals with the fopen and fclose functions; there will be no output from the code.

#include

int main()
{

FILE *fileObj = fopen("myfile.txt", "r");

fclose(fileObj);

return 0;
}

The name of the file in the disk is myfile.txt, in the working directory. It is opened for reading, "r". The identifier of the pointer returned by the fopen function is fileObj. After opening the file, it is closed with the fclose function using, fileObj.

Unable to open File
It is possible that when you want to open a file, the file may be in an area of the disk with bad sectors. In that case the opening processes of the fopen function will not work. If the fopen function succeeds in opening a file, it returns a pointer to the FILE object, created in memory. If it fails for whatever reasons (e.g. bad sectors in disk), it returns a null pointer. The null pointer has a pre-declared identifier called, NULL. So the above code is better written as follows:

#include

int main()
{

FILE *fileObj;
fileObj = fopen("myfile.txt", "r");

if (fileObj != NULL)
{
//do whatever you want with the stream of the file.
}
else
{
printf("Error: File could not be opened.");
}

fclose(fileObj);

return 0;
}

Read and try the above code. So you test whether the return pointer is NULL. If it is not, you do whatever you want to do with the stream of the file. If it is, you print an error message to the user.

File Position Indicator
When a file is opened successfully, a stream for the file is established. There is what is called a file position indicator. When a file is just opened, everything being equal, this indicator points to the beginning of the file stream. Whatever you do to the stream will happen at the character or line the file position indicator is pointing to.

The fgets Function
This function (fgets) reads a maximum number of characters before the end of a line (\n) or end-of-file. You can use it to read a whole paragraph. In text files a line is text that ends with the newline (\n) character. To have a paragraph in text file you simple have to write a long line. As you are typing the long line, it will be wrapping on its own; press the Enter key on the keyboard when you think it is long enough to be a paragraph. The Enter key inserts the newline character without you seeing it. The syntax of the fgets function is:

char *fgets(char * restrict s, int n, FILE * restrict stream);

The function takes three arguments. The first is a pointer to a string array that will have the text in the line read. The pointer should have been declared in the code before this line. The second argument is the maximum number of the characters that you think can be read from a line. The fgets function will not read past the newline character or end of file. To read a paragraph, put this number at 1500, because I do not think any paragraph is longer than 1500 characters (including the spaces from keyboard spacebar).

The last argument for this function is the pointer to the file object returned by the fopen function. If you use 1500 for the second argument, after reading a line (paragraph) the fgets function causes the file position indicator to point to the beginning of the next line (paragraph). The fgets function reads the newline (\n) character at the end of a line, together with the previous characters.

If a reading error is encountered or the end-of-file is reached, the fgets function returns a null pointer (NULL). If a reading error occurs, the string stored at the string array is indeterminate.

The fputs Function
This function is like the opposite of fgets. The syntax is:

int fputs(const char * restrict s, FILE * restrict stream);

The first argument is a pointer to a string, which should have been declared and assigned. The second argument is a pointer to the corresponding FILE object for the file in disk. There is no maximum number of characters here. If you want to put (write) a paragraph, then end the string to be written with the newline character (\n). The fputs function places the file indicator just after what it has written. If a write error occurs fputs returns an int whose identifier is EOF.

Read and try the following code, which illustrates the use of the functions mentioned above, except the fputs function. The file in disk to use for this is myfile.txt.

#include

int main()
{

FILE *fileObj;
fileObj = fopen("myfile.txt", "r");
char line[1500];

if (fileObj != NULL)
{
fgets(line, 1500, fileObj);
printf(line);
fgets(line, 1500, fileObj);
printf(line);
}
else
{
printf("Error: File could not be opened.");
}

fclose(fileObj);

return 0;
}

Because of "r" for the fopen mode argument, the file was opened for reading only.

ftell and fseek functions
Above I mentioned that the fgets and fputs functions will cause the file indicator to point to the next character to be accessed in the stream. After the fgets and fputs functions have been executed, you have to use the ftell function to know the position and you also have to use the fseek function to fix the position of the indicator. If you do not do these you may not be able to modify the text based on the file position indicator.

The fputs function replaces the characters that it meets on its way; it also replaces the newline character. In order to update (edit or modify) an existing file, the mode argument for the fopen function should be "r+". The following code uses all the functions we have learned so far. The left part of the second line of the saved file, myfile.txt, which is in the working directory, is replaced. Read and try the code (see explanation below):

#include

int main()
{

FILE *fileObj;
fileObj = fopen("myfile.txt", "r+");
char line[1500];

if (fileObj != NULL)
{
fgets(line, 1500, fileObj);
long int indicator = ftell(fileObj);
fseek(fileObj, indicator, SEEK_SET);
char *ALine = "Here is line B.";
fputs(ALine, fileObj);
}
else
{
printf("Error: File could not be opened.");
}

fclose(fileObj);

return 0;
}

Use your operating system to open the file, myfile.txt and note that the left part of its second line has been replaced by "Here is line B."

The return value for the ftell function is "long int". This is a variation of the object type, int. Do not worry about that for this basic tutorial. In the above code the identifier I made for the return value of ftell is indicator. For text files, this identifier has to be the second argument of the next function, which is the fseek function. The third argument for the ftell function is the pointer of the FILE object of the corresponding file in disk.

The fseek returns an int, but do not worry about that for this basic tutorial. Its first argument is the pointer of the FILE object of the corresponding file in disk. Its second argument for simple accessing of text files is the return value of the ftell function. Its last argument for accessing of text files is the identifier of a constant, which is SEEK_SET.

The feof function
The feof function for end-of-file is used to detect whether the file position indication has reached the end of the stream, which corresponds to the end of file in disk. This function has one argument, which is the pointer to the FILE object corresponding to the file in disk. The return value of this function is true (of type _Bool) when the end of file is reached. Remember, zero is the _Bool value for false.

Consider an example where you have to be reading the contents of a file using the fgets function. You will need to know when the end of file has reached in order to stop reading; else you program will incur an error. Read and try the following code that illustrates this:

#include

int main()
{

FILE *fileObj;
fileObj = fopen("myfile.txt", "r");
char line[1500];

if (fileObj != NULL)
{
while (feof(fileObj) == 0)
{
fgets(line, 1500, fileObj);
printf(line);
}
}
else
{
printf("Error: File could not be opened.");
}

fclose(fileObj);

return 0;
}

Note: if you pressed the Enter key of the keyboard (as I asked you to) after typing the last line in the file, myfile.txt, then the last line would be printed twice at the output. Any blank lines in the file after you pressed the Enter key would also be printed. To avoid this kind of problems, do not press the Enter Key after you type the last line in your text file (e.g. myfile.txt) of interest.

Well, there is more to accessing files than I have given. In order to do serious editing of text files you need to learn the string library (string.h) and may be a few other things concerning accessing files.

We have come to the end of this part of the series. We are toward the end of the series. Let us stop here and continue in the next part of the series.

Chrys

Published by Chrys Forcha

I have more than 10 years experience in computer programming, software, electronics and telecommunications. I have a First Degree in Electronics and a Master's Degree in Technical Education. As well a...  View profile

To comment, please sign in to your Yahoo! account, or sign up for a new account.