CS210 · Block 3 · Lesson 21

File I/O

fopen · fgets · fscanf · sscanf · error handling · text vs binary
At a glance: Programs become useful when they read and write data that outlives them. Today you meet the C standard library's file I/O routines: how to open a file, walk a cursor through its bytes, read structured records line by line, write new content, and handle every common thing that can go wrong. The hands-on practice is the File I/O walkthrough in Section 5. The lab applies it all to a flight log reader that parses real CSV records into structs.
Contents
  1. Lesson Objectives
  2. Assigned Readings
  3. Pre-Class Work
  4. Lesson Materials and Overview
  5. In-Class Work
  6. After Class
1. Lesson Objectives

By the end of this lesson, cadets will be able to:

  1. Open, read, write, and close files in a C program. (Outcome 2)
  2. Distinguish between text-mode and binary-mode file operations. (Outcome 2)
  3. Parse formatted input from files using fscanf and from strings using sscanf. (Outcome 2)
  4. Handle file operation errors appropriately. (Outcomes 3, 7)
2. Assigned Readings

Complete before class. These give you the vocabulary and the library calls. The lecture will assume you have read them.

Source What it covers Why read it
Beej's Guide to C, Chapter 9 (File Input/Output) fopen, fclose, modes; fgetc, fputc, fgets, fputs; fread, fwrite; fseek, ftell; feof and ferror The whole conceptual map for today. Read this through once and then again with code open beside it.
Beej's Library Reference, <stdio.h> The reference entries for fscanf and sscanf in particular. Format specifiers, conversion specifications, return values. fscanf and sscanf are how you parse structured records. The lab leans on sscanf heavily.

If a reading feels dry, skim once for landmarks, then read again for detail when you sit down for the lab. Reading docs is a course skill; we will keep practicing it.

3. Pre-Class Work

Before class, work through these three things. Total time: about 30 minutes.

  1. Read both assigned readings. Beej-C Chapter 9 first (concepts), then the <stdio.h> reference (the specifics). Stop and write down the answers to:
    • What does fopen return when the file does not exist?
    • What is the difference between fgetc returning a value and fgets returning a value?
    • Why is the return type of fgetc int and not char?
    • What does sscanf return on success, and what does it return when only some of the fields matched?
  2. Skim the lab README. You do not need to start the lab. Just know what you are signing up for: parsing flight log records, handling malformed input, summary output. The lab will make a lot more sense after the lesson and the walkthrough.
  3. Bring your laptop ready to compile and run C. The walkthrough lives in the browser, but the lab needs your terminal and your gcc.
4. Lesson Materials and Overview
Jump to

Open Lesson 21 slides →

Mental model: a file is bytes with a cursor

A file on disk is just a sequence of bytes. C's standard library gives you a FILE pointer, which is an opaque handle that points to a kernel object tracking three things about your access to that file:

  • Which file you have open
  • What mode you opened it in (read, write, append, read-plus-write)
  • A cursor: the position in the file where the next read or write will happen

Every operation either reads from the cursor and advances it, writes at the cursor and advances it, or moves the cursor without reading or writing (fseek). The walkthrough in Section 5 makes the cursor visible so you can watch it move.

fopen and fclose: getting a FILE pointer

FILE *f = fopen("flights.log", "r");
if (f == NULL) {
    fprintf(stderr, "cannot open flights.log\n");
    return 1;
}
/* ... read or write through f ... */
fclose(f);

Common modes:

Mode What it does
"r" Read. Fails if the file does not exist.
"w" Write. Creates or truncates the file. Anything that was there is gone.
"a" Append. Writes go to the end of the file. Creates if missing.
"r+" Read and write. File must exist.
"w+" Read and write, but starts by truncating. Almost always not what you want.

fclose flushes any pending writes and releases the file handle. Always close what you open. Forgetting fclose leaks resources and can lose unflushed writes if the process ends abnormally.

Reading: fgetc, fgets, fscanf

Three reading patterns, from finest grained to most structured:

  • fgetc(f) reads one byte and returns it as an int. On EOF, returns the special value EOF (which is -1). That is why the return type is int, not char: it needs to represent every possible byte plus EOF.
  • fgets(buf, n, f) reads up to n - 1 characters into buf, stopping at a newline or EOF. The newline is included in the buffer if it was read. Returns NULL at EOF. This is your line-by-line workhorse.
  • fscanf(f, fmt, ...) parses formatted fields directly from the file. Powerful but brittle: any deviation in the input throws off the parse and leaves the cursor at the failure point. Most C programmers prefer the two-step pattern of fgets + sscanf for that reason: read a whole line first, then parse it.

The canonical line-reading loop:

char line[256];
while (fgets(line, sizeof line, f) != NULL) {
    /* process one line */
}

sscanf: the parser you will use most

sscanf reads from a string instead of a file. Combined with fgets, it gives you robust line parsing. Format specifiers you need today:

Specifier Reads
%d Decimal integer.
%f Floating-point number into a float *. (Use %lf for double.)
%s Whitespace-delimited word. Dangerous without a width limit.
%7s Same, but at most 7 characters. The width is a buffer-overflow defense.
%[^,] All characters up to a comma. Useful for comma-separated records.
%4[^,] Up to 4 characters, no comma. Width-limited string of a non-default character set.

sscanf returns the number of fields it matched. Always check the return value to detect malformed input. Worked example: parsing a flight record

char tail[8], depart[5], arrive[5];
int  duration, fuel;

int matched = sscanf(line, "%7[^,],%4[^,],%4[^,],%d,%d",
                     tail, depart, arrive, &duration, &fuel);

if (matched != 5) {
    /* malformed line; skip and warn */
}

Writing: fputc, fputs, fprintf

  • fputc(c, f) writes one byte.
  • fputs(str, f) writes a string. Note: unlike puts, fputs does not add a newline. You include the \n yourself if you want one.
  • fprintf(f, fmt, ...) is printf but to a file. Same format specifiers as you already know.

An especially useful pattern: fprintf(stderr, ...) writes error messages to the standard error stream so they do not get mixed in with your program's normal output. The lab autograder checks stdout and stderr separately for exactly this reason.

Error handling: check every return value

Every file operation can fail. Things that go wrong in real programs:

  • The file does not exist (read mode).
  • You do not have permission to read or write it.
  • The disk is full (write mode).
  • The input file is malformed in unexpected ways.
  • The cadet at the keyboard typed the wrong filename.

The standard library tells you what went wrong through return values. The discipline is to check, every time:

Function Failure signal
fopen Returns NULL.
fgetc Returns EOF (also returned at end of file; use feof/ferror to distinguish).
fgets Returns NULL at EOF or on error.
fscanf, sscanfReturns count of matched fields. Compare to the count you expected.
fputc, fputs, fprintfReturns EOF or a negative number on write failure.
fclose Returns EOF on failure (rare but possible if a final write was buffered and now fails to flush).

Text mode vs binary mode

Add a "b" to the mode string to open in binary mode: "rb", "wb", "r+b".

On Linux and macOS, text and binary modes are identical; the distinction is a no-op. On Windows, text mode silently converts "\r\n" sequences in the file to "\n" in your buffer (and vice versa on write). Binary mode passes bytes through unchanged.

Rule of thumb: read CSV, logs, source code, and other text content in text mode. Read images, executables, and serialized data structures in binary mode. When in doubt, binary is safer because it never lies to you about what is in the file.

5. In-Class Work

Three things to do in class. Use the checklist to track your progress.

  • Work through the File I/O walkthrough below. Fourteen guided tasks covering fopen, fgetc, fseek, fgets, fputs, ftell, and fclose. The right pane shows the cursor walking through the file. About 25 minutes.
  • Get the Lab 21 starter from GitHub Classroom. Clone the repo, read the README, run make to confirm the starter compiles (it does; the TODO stubs are no-ops), and read flight.h carefully.
  • Begin Lab 21: Flight Log Reader. Implement parse_flight_line first; it is the function you can write entirely from what we covered today. Get the basic case passing, then tackle malformed input. Submit to Gradescope when you have at least the basic functional test passing.

File I/O Walkthrough

Fourteen tasks across four sections. Type each statement at the prompt. The terminal pane shows what your statement did; the right pane shows each open file as a row of bytes with the cursor marked underneath.

Before you begin

Enter your name. It will appear on the completion screen so you can include it in your screenshot for the Lesson 21 reflection on Blackboard.

CS210 Lesson 21 · File I/O Walkthrough

A guided tour through fopen, fgetc, fgets, fputs, fseek, ftell, and fclose. Type each statement on the input line. The terminal pane shows what your line did; the right pane shows the files and where the cursor sits in each.
Task 0 of 14
Section 1: Opening and reading a file
Current task
Loading...
Files in scope
No files open yet.
Type an fopen call on the left.
fio>
How this works: This is not a real filesystem. It is a teaching tool that simulates what file I/O calls do to a file's bytes and cursor position. The task card tells you what to type next; only the expected statement advances the walkthrough so the file state stays predictable. Mistype twice and the hint button will pulse. After the walkthrough finishes, free mode unlocks so you can experiment with extra fopens, fseeks, and reads.
6. After Class
  • Finish Lab 21 if you did not complete it in class. Submit to Gradescope before Lesson 22.
  • Read Beej-C Chapter 16 (Command-Line Arguments) for Lesson 22.
  • Replay the walkthrough in free mode. Open a file you create with fopen(..., "w"), write some bytes with fputc, then close and reopen for reading. The cursor behavior on freshly-written files is a common point of confusion.
  • Heads up: Quiz 8 (File I/O) is at Lesson 22, covering today's material. Be sure your reading and walkthrough are solid before then.
Need help? Schedule EI with your instructor. Bring your code, the exact command you ran, the exact output you got, and what you expected instead. Specific questions get unstuck quickly.