File input and output¶
On a unix system, and by extension in a C program, a file is just a
sequence of char
values. They arrive in a stream and can be
read in (or written out) by a program using tools that are already pretty
familiar from our console input and output.
In fact, printf
, puts
, scanf
, etc. all work on
files, but the files are implied—every C program automatically has three
files open, the standard input, output, and error message streams. You can
open new streams connected to files in the filesystem with fopen
,
and close them when you’re done with fclose
. In the mean time,
you can use fprintf
, fputs
, fscanf
, etc.,
the same as the familiar functions but with an explicit file parameter.
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *f;
f = fopen("output", "w");
if (!f) {
puts("Couldn't open output file");
exit(EXIT_FAILURE);
}
fputs("hello, world", f);
fclose(f);
}
The call to fopen
takes two arguments: first, the filename,
and second, a mode indicating what you want to do with that file. This
example uses mode "w"
for ‘writing’; in this mode, the file
is created if it does not already exist, and any contents previously in
it are erased, so you have a clean slate to write output to. For various
reasons, opening a file can fail, and this example handles that with a
conditional that will handle failure by printing a message and exiting
(the exit
function and the EXIT_FAILURE
code are brought
in from stdlib.h
).
The fputs
function is similar to puts
, but rather than
writing the message to the standard output stream, it takes a second
parameter indicating which file stream to use, in this case, the one we
just opened. (Also, unlike puts
, fputs
doesn’t implicitly
add a newline, so you have to use \n
if you want one.)
Input and output to streams is buffered, so the message isn’t necessarily written to the hard drive immediately. When you close or explicitly flush the buffer, the file will reconcile with the buffer. If you forget to close a file, there won’t be much harm in a program this simple, since open files are implicitly closed when the program ends anyway, but it is a good habit to get into to always have a plan to explicitly release resources you have allocated.
The following program reads in two numbers from a file and prints their sum.
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *f;
int a;
int b;
f = fopen("input", "r");
if (!f) {
puts("Can't open input file");
exit(EXIT_FAILURE);
}
if (fscanf(f, "%d %d", &a, &b) != 2) {
puts("Didn't get both numbers");
exit(EXIT_FAILURE);
}
fclose(f);
printf("%d + %d = %d\n", a, b, a + b);
}
This time, a file is opened with mode "r"
, for ‘reading’.
The fscanf
function is identical to scanf
except you
specify which file to scan from as the first argument. This code checks
the return value to make sure that the input was valid; the scanf
family of functions return how many conversions were successful, and
the format "%d %d"
expects two decimal numbers with whitespace
between them so the return value should be 2. When you test this program,
try running it with no file named input
, or with invalid input
in the file (you can create it with your text editor). When the file
actually does exist and begin with two numbers, this program will scan
them in, close the file once it’s no longer needed, and print out the sum.
If you ever need to explicitly mention one of the standard streams as
a FILE *
argument, they are stdin
(standard input),
stdout
(standard output), and stderr
(standard error).
The manual pages for e.g. printf, scanf, puts, or getline will mention variations that include the ones that accept file parameters.