Computer Programming 1 (Python) • Problem Solving & Mini-Projects

File I/O Programs — I can write a program that reads input, produces an output file, and documents assumptions.

Learn to build Python programs that read data from files, process it, and write meaningful results to new files.

A Python coding terminal with a program flow and laptop.
Inside this lesson 10 sections 5 questions 8 figures

SECTION 1

Historical Context & Motivation

Before graphical user interfaces and cloud storage existed, computers communicated with the outside world almost entirely through files. Early programmers fed instructions to machines on punched cards—physical cards with holes that represented data. The computer would read these cards, crunch numbers, and then punch results onto new cards or print them to paper. This fundamental pattern of reading input, processing it, and producing output is the backbone of nearly every program ever written.

1890s • Punched Card Systems

Herman Hollerith invents punched cards for the U.S. Census, creating one of the first systems where data was read from a physical medium and processed by machine.

1960s • Magnetic Tape & Disk Files

Computers begin storing data on magnetic tape and disks, allowing programs to read and write files electronically rather than mechanically. The concept of a 'file' as a named collection of data becomes standard.

1972 • The C Language & stdio

The C programming language introduces a standard I/O library (stdio) that formalizes how programs open, read, write, and close files. These ideas influence virtually every language that follows, including Python.

1991 • Python Is Born

Guido van Rossum releases Python, which simplifies file I/O with built-in functions like open(), read(), and write(). Python makes file handling accessible to beginners without sacrificing power.

2020s • Data Pipelines Everywhere

Modern data science, web apps, and automation all rely on reading input files (CSV, JSON, logs) and producing output files. File I/O remains one of the most practical skills in programming.

Today, whether you are building a grade calculator that reads student scores from a spreadsheet or a script that generates a report, you are using the same input-process-output pattern that has driven computing for over a century. The central question this lesson addresses is: How do you write a Python program that reliably reads data from a file, transforms it, writes results to a new file, and clearly documents the assumptions behind your design?

SECTION 2

Core Principles of File I/O

File I/O in Python revolves around a few essential ideas. Understanding these principles gives you a mental framework you can apply to any file-handling task, whether you are reading a simple text file or generating a complex report. Let's break these core ideas down.

1

Open → Use → Close

Every file operation follows a three-step lifecycle: you open the file to create a connection, use it to read or write data, and then close it to free system resources. Python's with statement handles closing automatically.
2

File Modes

When you open a file, you specify a mode that controls what you can do. Common modes include "r" (read), "w" (write—creates or overwrites), and "a" (append—adds to the end).
3

Text vs. Binary

Files are either text files (human-readable characters like .txt or .csv) or binary files (raw bytes like images or executables). In this course we focus on text files.
4

Assumptions & Documentation

Every file-based program makes assumptions—about the file's format, encoding, delimiter, and whether it exists. Documenting these assumptions with comments and docstrings makes your program understandable and maintainable.
5

Error Handling

Files can be missing, corrupted, or locked by another program. Using try/except blocks lets your program handle errors gracefully instead of crashing unexpectedly.
✦

KEY TAKEAWAY

Think of a file like a library book. You check it out (open), read or write notes in it (use), and then return it (close) so others can use it. If you forget to return it, the library (your operating system) can't manage its resources properly. Python's with statement is like an automatic return slip—it guarantees the book goes back on the shelf.

SECTION 3

Visualizing the File I/O Pipeline

The diagram below shows the complete lifecycle of a file I/O program. Data flows from an input file through your Python program's processing logic, and then out to an output file. Notice how assumptions are documented at the top and error handling wraps the entire process.

📝 DOCUMENTED ASSUMPTIONS: file format, encoding, delimiter, data types, file existenceINPUT FILEscores.txtAlice,92Bob,85Carol,78open("r")PYTHON PROGRAM1. Read lines2. Split by comma3. Convert to int4. Calculate averageopen("w")OUTPUT FILEreport.txtAlice: 92 (A)Bob: 85 (B)Avg: 85.0⚠ ERROR HANDLING (try / except)FileNotFoundError → "Input file not found"ValueError → "Invalid data in file"PermissionError → "Cannot write to file"Graceful error messages keep users informed and prevent crashes.
The complete file I/O pipeline: data flows from the input file (left) through the Python program (center) to the output file (right). Assumptions are documented at the top, and error handling wraps the whole process.

Notice the three distinct zones in the diagram. The input file on the left contains raw data in a specific format—in this case, comma-separated name-score pairs. The Python program in the center performs a sequence of transformations: reading each line, splitting it at the comma, converting the score to an integer, and computing an average. Finally, the output file on the right holds the processed results in a new, more useful format. The dashed boxes remind us that every robust program documents its assumptions up front and wraps file operations in error-handling blocks.

SECTION 4

How File I/O Works in Python

Python provides a clean, readable syntax for working with files. The built-in open() function is the gateway to all file operations. It returns a file object—a special object that gives you methods for reading or writing. Let's examine the key patterns.

Reading a File

READING PATTERN

withopen(filename,"r")asf:data=f.read()with open(filename, "r") as f: data = f.read()

filename — a string path to the file; "r" — read mode; f — the file object; f.read() — reads the entire file as one string. Alternatives: f.readline() reads one line, f.readlines() returns a list of all lines.

Writing to a File

WRITING PATTERN

withopen(filename,"w")asf:f.write(text)with open(filename, "w") as f: f.write(text)

"w" — write mode (creates a new file or overwrites an existing one); f.write(text) — writes the string text to the file. Use "a" mode instead if you want to append without erasing existing content.

The with Statement

CONTEXT MANAGER

with open(...) as variable: # file is open here # file is automatically closed here

The with keyword creates a context manager that guarantees the file closes when the indented block ends—even if an error occurs. This is the recommended way to handle files in Python.

Iterating Line by Line

LINE-BY-LINE READING

withopen(filename,"r")asf:forlineinf:process(line.strip())with open(filename, "r") as f: for line in f: process(line.strip())

A file object is iterable, meaning you can loop through it directly. Each iteration returns the next line as a string. The .strip() method removes leading and trailing whitespace, including the newline character \n at the end of each line.

⚠

Important: Write Mode Erases!

Opening a file in "w" mode will completely erase the existing contents of that file before writing. If you accidentally open your input file in write mode, you will lose all the data. Always double-check your mode string before running your program.

SECTION 5

Documenting Assumptions

Every file-based program makes assumptions about the data it works with. A program that reads student grades might assume each line has exactly two values separated by a comma, that scores are integers between 0 and 100, and that the file uses UTF-8 encoding. If any of these assumptions are wrong, the program could crash or produce incorrect results. Documenting your assumptions clearly is just as important as writing the code itself, because it helps anyone who uses or maintains your program understand what inputs it expects.

ANATOMY OF A WELL-DOCUMENTED FILE I/O PROGRAMMODULE DOCSTRING"""Grade Report Generator — Reads scores.csv and produces report.txt"""ASSUMPTIONS (as comments)# Assumption 1: Input file is UTF-8 encoded CSV# Assumption 2: Each row has format: Name,Score# Assumption 3: Scores are integers 0–100# Assumption 4: Input file exists in same directoryCONSTANTS & CONFIGURATIONINPUT_FILE = "scores.csv" OUTPUT_FILE = "report.txt" DELIMITER = ","FUNCTION DOCSTRINGSdef read_scores(filename): """Read CSV file, return list of (name, score) tuples."""ERROR HANDLINGtry: data = read_scores(INPUT_FILE)except FileNotFoundError: print(f"Error: {INPUT_FILE} not found.")
A well-documented Python file I/O program includes a module docstring, clearly labeled assumption comments, named constants, function docstrings, and error handling.

Let's look at the five key zones in the diagram above. At the very top of your file, a module docstring (a triple-quoted string) describes what the program does in one or two sentences. Below that, numbered assumption comments spell out exactly what your program expects about the input—file format, data types, delimiters, and file location. Named constants (written in ALL_CAPS by convention) make it easy to change filenames or settings without hunting through your code. Each function gets its own docstring explaining what it does. Finally, error handling with try/except catches predictable problems and displays helpful messages.

  • ✓ File format — What type of file is it? (CSV, plain text, tab-separated)
  • ✓ Encoding — Is it UTF-8, ASCII, or something else?
  • ✓ Delimiter — What separates the values on each line? (comma, tab, space)
  • ✓ Data types — Are values strings, integers, or floats?
  • ✓ File location — Is the file in the same folder as the script, or somewhere else?
  • ✓ Header row — Does the first line contain column names or actual data?

SECTION 6

Worked Example: Grade Report Generator

Let's build a complete program that reads student scores from a CSV file, calculates letter grades, and writes a formatted report to a new file. We will follow all of the best practices discussed so far: using with statements, documenting assumptions, handling errors, and using clear variable names.

Grade Report Generator

  1. 1

    Step 1 — Define Assumptions and Constants

    Start your program with a module docstring and explicit assumption comments. Define constants for the input and output filenames so they are easy to change later. """Grade Report Generator — Reads scores.csv and writes report.txt.""" # Assumption 1: scores.csv is in the same directory as this script # Assumption 2: Each line has format: Name,Score # Assumption 3: Scores are integers from 0 to 100 # Assumption 4: File uses UTF-8 encoding with no header row INPUT_FILE = "scores.csv" OUTPUT_FILE = "report.txt"
    Four assumptions documented; two constants defined.
  2. 2

    Step 2 — Write a Function to Read the Input File

    Create a function that opens the input file, reads each line, splits it at the comma, and returns a list of (name, score) tuples. Include a docstring. def read_scores(filename): """Read CSV file, return list of (name, score) tuples.""" scores = [] with open(filename, "r") as f: for line in f: name, value = line.strip().split(",") scores.append((name, int(value))) return scores
    Function returns: [("Alice", 92), ("Bob", 85), ("Carol", 78)]
  3. 3

    Step 3 — Write a Function to Assign Letter Grades

    Create a helper function that converts a numeric score to a letter grade using standard thresholds. def get_letter_grade(score): """Return letter grade for a numeric score (0-100).""" if score >= 90: return "A" elif score >= 80: return "B" elif score >= 70: return "C" elif score >= 60: return "D" else: return "F"
    get_letter_grade(92) → "A", get_letter_grade(85) → "B"
  4. 4

    Step 4 — Write a Function to Generate the Report

    Create a function that takes the list of scores, formats each entry, calculates the class average, and writes everything to the output file. def write_report(filename, scores): """Write formatted grade report to a text file.""" total = sum(score for _, score in scores) average = total / len(scores) with open(filename, "w") as f: f.write("GRADE REPORT\n") f.write("=" * 30 + "\n") for name, score in scores: grade = get_letter_grade(score) f.write(f"{name}: {score} ({grade})\n") f.write("=" * 30 + "\n") f.write(f"Class Average: {average:.1f}\n")
    Output file contains formatted report with letter grades and average.
  5. 5

    Step 5 — Tie It Together with Error Handling

    In the main section of your program, call the functions inside a try/except block to handle missing files or invalid data gracefully. try: data = read_scores(INPUT_FILE) write_report(OUTPUT_FILE, data) print(f"Report written to {OUTPUT_FILE}") except FileNotFoundError: print(f"Error: {INPUT_FILE} not found.") except ValueError: print("Error: Invalid data format in input file.")
    Program either writes report.txt successfully or prints a clear error message.
✦

KEY TAKEAWAY

Think of writing a file I/O program like preparing a recipe for someone else to follow. You list the ingredients (assumptions about your input file), explain each step (functions with docstrings), and describe what the finished dish (output file) should look like. If someone uses the wrong ingredient, a good recipe warns them—that's your error handling.

SECTION 7

Strengths, Limitations & Common Pitfalls

File I/O is incredibly useful, but it comes with trade-offs. Understanding the strengths and limitations helps you decide when file-based programs are the right tool for the job and when you might need a more advanced approach like a database.

AspectStrengthsLimitations
SimplicityText files are easy to create, inspect, and share. You can open them in any text editor.No built-in structure—you must parse and validate the format yourself.
PortabilityText files work on every operating system without special software.Large files can be slow to process because the whole file may need to be read.
Data PersistenceData saved to a file survives after the program closes, unlike variables stored in RAM.Multiple programs writing to the same file simultaneously can cause data corruption.
Error PronenessPython's try/except makes it straightforward to catch and recover from file errors.Using wrong modes (e.g., "w" instead of "a") can accidentally erase data permanently.
ScalabilityPerfect for small to medium datasets (thousands of records).For millions of records, a database is more efficient and supports searching.

Strengths vs. limitations of text-based file I/O

Common Pitfalls

  • Forgetting .strip() — Each line read from a file includes a newline character ( ). If you don't strip it, string comparisons and conversions may fail silently.
  • Opening input as "w" — This erases the file immediately. Always verify your mode before running the program.
  • Hardcoding filenames inside functions — Pass filenames as parameters instead. This makes your functions reusable.
  • Skipping error handling — If the file doesn't exist, your program crashes with a traceback. A try/except turns this into a friendly message.
✦

KEY TAKEAWAY

File I/O is like using a notebook versus a filing cabinet. A notebook (text file) is quick and simple—great for jotting down a few pages of information. But if you need to organize thousands of documents with search and retrieval, you need a filing cabinet (a database). For this course, the notebook is exactly the right tool.

SECTION 8

Connection to Advanced Topics

The file I/O skills you are learning now form the foundation for many advanced programming topics. As you progress in computer science, you will encounter more powerful ways to store and process data, but they all build on the same core principles of opening, reading, writing, and closing data streams.

This Course (File I/O Basics)Advanced Topic
Reading plain text files line by lineParsing structured formats like JSON, XML, and YAML with specialized libraries
Writing formatted output to .txt filesGenerating HTML reports, PDF documents, or Excel spreadsheets with libraries like pandas
Using try/except for file errorsCustom exception classes and logging frameworks for production applications
Storing data in CSV filesUsing relational databases (SQL) or NoSQL databases for scalable data storage
Documenting assumptions in commentsWriting formal specifications, unit tests, and data validation schemas

How file I/O basics connect to advanced programming concepts

One particularly exciting next step is the csv module built into Python. While we have been splitting lines manually at commas, the csv module handles tricky cases like values that contain commas inside quotation marks. Similarly, the json module lets you read and write structured data that web APIs use to communicate. These are natural extensions of the skills you are building right now, and you will likely encounter them in a second programming course or in a data science elective.

💡

Looking Ahead

In AP Computer Science Principles and college-level courses, you will work with data pipelines that chain multiple file operations together: reading raw data, cleaning it, transforming it, and writing results—sometimes across many files. The reading-processing-writing pattern you are mastering here is the building block for all of it.

SECTION 9

Practice Problems

Question 1: CONCEPTUAL

Explain the difference between opening a file in "r" mode, "w" mode, and "a" mode. Why is it dangerous to accidentally use "w" on your input file?
Show answer
Read mode ("r") opens an existing file for reading without modifying it. Write mode ("w") creates a new file or completely erases an existing file's contents before writing. Append mode ("a") opens a file and adds new content to the end without erasing what's already there. Using "w" on your input file would delete all the original data the instant the file is opened, before you have a chance to read any of it. This is one of the most common and devastating mistakes in file I/O programming.

Question 2: BASIC CALCULATION

Given a file called temps.txt that contains one temperature per line (e.g., 72, 68, 75, 80), write Python code that reads all the temperatures and prints the average. Use a with statement.
Show answer
Here is the solution: with open("temps.txt", "r") as f: temps = [int(line.strip()) for line in f] average = sum(temps) / len(temps) print(f"Average temperature: {average:.1f}") The with statement opens the file in read mode and automatically closes it. A list comprehension reads each line, strips whitespace, and converts the string to an integer. The average is calculated by dividing the sum by the number of entries. Note that .strip() is essential to remove the newline character from each line before converting to an integer.

Question 3: INTERMEDIATE

A file called students.csv contains lines in the format Name,Score1,Score2,Score3. Write a program that reads this file and writes a new file called averages.txt where each line shows the student's name and their average score. Include assumption comments in your solution.
Show answer
A complete solution would be: # Assumption: students.csv has no header row # Assumption: Each line has format Name,Score1,Score2,Score3 # Assumption: All scores are valid integers with open("students.csv", "r") as infile: with open("averages.txt", "w") as outfile: for line in infile: parts = line.strip().split(",") name = parts[0] scores = [int(s) for s in parts[1:]] avg = sum(scores) / len(scores) outfile.write(f"{name}: {avg:.1f}\n") This program nests two with statements to keep both files open simultaneously. The input line is split at commas, the first element becomes the name, and the rest are converted to integers. The average is computed and written to the output file with one decimal place. The three assumption comments at the top document the expected input format.

Question 4: APPLIED

You are building an inventory tracker for a small store. The file inventory.csv contains lines like Widget,50,2.99 (product name, quantity, unit price). Write a complete program with documented assumptions that reads this file, calculates the total value of each product (quantity × price), and writes a report to inventory_report.txt showing each product's total value and a grand total at the bottom. Include error handling.
Show answer
Here is a full solution: """Inventory Report Generator""" # Assumption 1: inventory.csv is in the same directory # Assumption 2: Format is Product,Quantity,Price (no header) # Assumption 3: Quantity is an integer, Price is a float # Assumption 4: File uses UTF-8 encoding INPUT_FILE = "inventory.csv" OUTPUT_FILE = "inventory_report.txt" try: grand_total = 0 lines_out = [] with open(INPUT_FILE, "r") as f: for line in f: name, qty, price = line.strip().split(",") total = int(qty) * float(price) grand_total += total lines_out.append(f"{name}: {int(qty)} x ${float(price):.2f} = ${total:.2f}") with open(OUTPUT_FILE, "w") as f: f.write("INVENTORY REPORT\n") f.write("=" * 40 + "\n") for item in lines_out: f.write(item + "\n") f.write("=" * 40 + "\n") f.write(f"Grand Total: ${grand_total:.2f}\n") print(f"Report saved to {OUTPUT_FILE}") except FileNotFoundError: print(f"Error: {INPUT_FILE} not found.") except ValueError: print("Error: Invalid data in input file.") This solution demonstrates all best practices: a module docstring, four numbered assumptions, named constants, formatted output, and a try/except block that catches both missing-file and data-format errors. The grand total accumulates as each product line is processed.

Question 5: CRITICAL THINKING

A classmate writes a program that reads data.txt and writes results to data.txt (the same file). They claim this is fine because they read the data first, then write. However, sometimes their program produces an empty file. Explain what might be going wrong, why it's unreliable even when it seems to work, and propose a better approach that safely handles this scenario.
Show answer
The problem is timing and mode. If the classmate opens the file first in "r" mode, reads the data, closes it, and then opens it in "w" mode to write, this works in theory—but only if both operations complete without error. If the program crashes after opening in "w" mode but before writing, the original data is lost because write mode erases on open. If the classmate accidentally opens the file in "w" mode before reading, the data is destroyed immediately. A much safer approach is to write results to a temporary output file (like data_temp.txt), verify that the write succeeded, and then use Python's os.replace() to rename the temp file to the original name. This pattern ensures the original file is never at risk. This is the same strategy used by professional text editors and database systems.

SUMMARY

Lesson Summary

Summary

In this lesson, you learned how to build Python programs that follow the input → process → output pattern using file I/O. You explored how to use the open() function with different file modes ("r", "w", "a"), how the with statement ensures files are properly closed, and how to read data line by line using .strip() and .split() to parse structured text.

Equally important, you practiced documenting assumptions—about file format, encoding, delimiters, data types, and file location—using comments and docstrings. You learned to wrap file operations in try/except blocks for error handling, and you saw how to use named constants for filenames to keep programs flexible and maintainable. These skills—reading input, producing output, and communicating your design decisions—form the foundation for every data-driven program you will write.

Varsity Tutors • Computer Programming 1 (Python) • File I/O Programs