Files
This section contains information on opening a file, reading from a file, and writing to a file. I only cover how to interact with text files – it is also possible to read from and write to binary files.
Whenever you are doing file I/O, you need to add:
#include <stdio.h>
Opening a File
Before we can interact with a file, we need to open it. The fopen
function lets us open files for different kinds of input and output. Here’s the prototype:
FILE* fopen(char filename[], char mode[])
The FILE*
return type means that the function is returning the address of a FILE
object. We’ll learn more about pointers in the next section. If the file could not be opened, fopen returns NULL
.
Here, filename
is a string representation of the filename, such as “data.txt”. fopen
searches the current directory for the file if no absolute path is given. The string mode
specifies what type of operations you want to do on the file.
Here are the different options for the mode:
Mode | Description |
---|---|
“r” | Open for reading (file must exist) |
“w” | Open for writing (overwrites old data) |
“a” | Open for appending (creates file if necessary) |
“r+” | Open for reading and writing (file must exist) |
“w+” | Open for reading and writing (overwrites old data) |
“a+” | Open for reading and appending (opens at end of file) |
For example, we can open the file “data.txt” for reading, and print an error if we were unsuccessful:
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
printf("Error opening file\n");
}
After we are done reading from a file or writing to a file, we must close the file with the fclose
function. Here’s the prototype:
int fclose(FILE* fp)
To close data.txt, we’d do:
fclose(fp);
Reading from a File
There are two major functions for reading from a file – fscanf
and fgets
. fgets
works exactly like we’ve seen before, except now we specify a FILE*
instead of stdin
. fscanf
works exactly like scanf
, except we first specify the FILE*
. We’ll start with fscanf
:
int fscanf(FILE *stream, char str[], variable addresses...)
fscanf
, like scanf
, returns the number of variables that were correctly read in. If it was unable to read any more input, the EOF
constant is returned. Thus we can compare the return value of fscanf
to EOF
to see if we’ve reached the end of the file.
Suppose the file data.txt looks like this (a bunch of names and ages, each on separate lines):
Bob 20
Jill 15
Tony 17
Lisa 22
We want to read this file, and print something like “Bob is 20 years old” to the console for each person in the file. Here’s how:
FILE *fp = fopen("data.txt", "r");
char name[20];
int age;
if (fp != NULL) {
while (fscanf(fp, "%s %d", name, &age) != EOF) {
printf("%s is %d years old\n", name, age);
}
fclose(fp);
}
Now, lets try to do the same thing with the fgets
function. Here’s the prototype:
char[] fgets(char s[], int size, FILE *stream)
fgets
reads a string from a specified file into the s
array. The size
parameter specifies the size of the string – it will not write past the end of the array. It returns a reference to the string that was read. If no string was read (specifying an error or the end of file), NULL
is returned.
fgets
will attempt to read size-1
characters unless it reaches a newline or the end of the file.
Here’s the same example repeated with fgets
:
FILE *fp = fopen("data.txt", "r");
char name[20];
char buf[30];
int age;
if (fp != NULL) {
while (fgets(buf, 30, fp) != NULL) {
//parse the current line
char *token = strtok(buf, " ");
strcpy(name, token);
//get the age
token = strtok(buf, " ");
age = atoi(token);
printf("%s is %d years old\n", name, age);
}
fclose(fp);
}
As we saw when using fgets
to read from stdin
, it WILL store the newline character at the end of each string when reading from a file (assuming there is still room in the array). You may want to use strcspn
to overwrite the \n
with a \0
.
Reading files with fscanf
is usually simpler (since it doesn’t involve parsing lines), but it is more error-prone than fgets
.
Writing to a File
The primary function for writing to a file is fprintf
. This function works exactly like printf
, but the first argument is now a FILE*
. Here’s the prototype:
int fprintf(FILE* fp, char str[], variables to print...)
Here is an example that will ask the user to input 10 numbers. Each number will be written on a separate line to the file out.txt:
FILE *fp = fopen("out.txt", "w");
if (fp != NULL) {
int num, i;
for (i = 0; i < 10; i++) {
printf("Type a number: ");
scanf("%d", num);
fprintf(fp, "%d\n", num);
}
fclose(fp);
}