MGU Computer Science Series — Programming with Python
Part V: Files, Data, and Practical Automation
Files · Paths · Reading · Writing · Persistence
Chapter 21 of 30
21
MGU Computer Science Series  ·  Programming with Python  ·  Library Textbook Edition

Chapter 21:
Files, Paths, and
Text Data

How programs read and write data that survives after they stop running — paths, the file system, opening and closing files, and the with statement

■ Part V of VII ■

■ 10 Sections  •  Chapter 21 of 30 ■

Builds on Chapters 1–20 — variables, control flow, functions, and data structures

Chapter Introduction and Learning Objectives

Every program you have written so far forgets everything the moment it stops running. Variables disappear. Results are gone. The next time you run the program, you start from zero. Files change that. A file is data that lives on disk. When your program reads a file, it pulls stored information into memory. When it writes a file, it saves information that will still exist after the program ends, after the computer restarts, and after months have passed.

This chapter teaches you how to work with text files in Python. You will learn how the file system is organized, how paths tell Python where files live, how to open and close files safely, how to read text from files, how to write and append text to files, and how to handle common file errors. By the end of this chapter, your programs will be able to store and retrieve real data.

Opening Question

A grade-tracking program needs to remember every student's score even after it closes. A note-taking app needs your notes to still be there tomorrow. A weather station logs a temperature reading every hour, forever. None of these programs can rely on variables alone — variables vanish when the program ends. What do all three have in common? Each one depends on files: data written to disk that outlives the program that created it. This chapter shows you exactly how that works in Python.

10Sections
11Objectives
20Key Terms
30Total Chapters

Learning Objectives

By the end of this chapter, you should be able to:

  1. 1Explain why programs use files and what persistence means.
  2. 2Describe how the file system organizes files into folders and identify the parts of a path.
  3. 3Distinguish between absolute paths and relative paths and choose the right one for a project.
  4. 4Open and close a file using open() and explain the purpose of each file mode.
  5. 5Use the with statement to open files safely and explain why it is preferred over manual opening and closing.
  6. 6Read file contents using .read(), .readline(), .readlines(), and direct iteration.
  7. 7Write text to a file using .write() and .writelines().
  8. 8Explain the difference between write mode and append mode and choose the correct one for a task.
  9. 9Explain how newline characters work in text files and manage them correctly when reading and writing.
  10. 10Identify and explain common file errors, including FileNotFoundError.
  11. 11Trace, debug, and write complete programs that read from and write to text files.

Sections at a Glance

Sections 21.1 – 21.3
The Territory: Why Files Matter, the File System, and Paths
Persistence · Folders · Absolute and Relative Paths

Why Programs Use Files

Think about programs you use every day. A text editor saves your writing. A spreadsheet saves your numbers. A chat app stores your message history. A game saves your progress. Every one of these programs uses files.

Python programs use files for many of the same reasons:

  • Persistence — saving information between runs so it is not lost
  • Input — reading data that was prepared before the program ran
  • Output — writing results that can be shared, printed, or opened later
  • Logging — recording what happened during a run for debugging or auditing
  • Configuration — reading settings that control how the program behaves
  • Data exchange — sharing information with other programs or people

Consider a grade-tracking program. Without files, the teacher must retype all the student grades every time she runs the program. With files, the grades are stored on disk. The program reads them at startup and writes updated grades when it finishes.

The shift from no-file programs to file-using programs is a big step. It means your programs can accumulate information over time, interact with data that already exists, and produce lasting results.

Study Note

Every program in Chapters 1 through 20 lost its data the moment it stopped. Files are the first tool in this book that breaks that limitation. Pay attention to how different this feels — it is a genuine turning point in what your programs can do.

Files, Folders, and Paths

Before writing any Python, you need a clear picture of how files are organized.

The File System

Your computer stores files in a file system — a hierarchical structure of folders (also called directories) and files. A folder can contain files and other folders. This nesting creates a tree of locations from a starting point called the root.

On Windows, the root of a drive looks like C:\. On macOS and Linux, the root looks like /. Everything else branches from there.

What a Path Is

A path is a string that describes where a file or folder is located. Think of it as the address of a file on disk.

C:\Users\Rosa\Documents\grades.txt       (Windows)
/home/rosa/documents/grades.txt          (macOS / Linux)

Paths use separators between each folder name. Windows uses backslashes (\). macOS and Linux use forward slashes (/). Python on Windows also accepts forward slashes in many situations, and pathlib (covered in Appendix J) handles this portably.

What a File Name Is

The file name is the last part of the path. It typically has a base name and an extension that suggests what type the file is:

grades.txt        base name: grades     extension: .txt
notes.md          base name: notes      extension: .md
report.csv        base name: report     extension: .csv

The extension is a convention. Python does not enforce what you put inside a .txt file, but good naming helps you and others understand the file's purpose.

Relative and Absolute Paths

There are two ways to describe a file's location.

Absolute Paths

An absolute path gives the full address from the root. It works regardless of where your program is running.

C:\Users\Rosa\Documents\grades.txt
/home/rosa/documents/grades.txt

Absolute paths are unambiguous. But they are fragile — if you move your project to a different computer or folder, the absolute path breaks.

Relative Paths

A relative path describes a location relative to the current working directory — the folder Python considers its base when it runs.

If Python is running in /home/rosa/projects/grades/, then:

grades.txt                     means /home/rosa/projects/grades/grades.txt
data/scores.csv                means /home/rosa/projects/grades/data/scores.csv
../notes.txt                   means /home/rosa/projects/notes.txt

The .. means "go up one folder level."

Which One to Use?

For beginner projects, relative paths are usually the right choice. Put your program file and your data files in the same folder, and use just the file name as the path. This keeps the project portable and simple.

# If grades.txt is in the same folder as your program:
filename = "grades.txt"

If you need subfolders:

filename = "data/grades.txt"
Good Habit

When you start a project, organize it before writing code. Create a folder for the project. Put your .py files and your data files in sensible locations inside that folder. Knowing where your files are before you code will save you frustration.

Sections 21.4 – 21.7
The Tools: Opening, Reading, Writing, and Appending
open() · Modes · .read() · .write() · Append Mode

Opening and Closing Files

Before Python can read or write a file, you must open it. Opening a file creates a connection between your program and the file on disk. Python calls this connection a file object.

The open() Function

The built-in open() function opens a file and returns a file object.

file = open("notes.txt", "r")

The first argument is the path. The second argument is the mode — a string that tells Python what you want to do with the file.

ModeMeaning
"r"Read. Opens an existing file for reading. Error if the file does not exist.
"w"Write. Creates a new file or overwrites an existing one.
"a"Append. Creates a new file or adds to the end of an existing one.
"x"Exclusive creation. Creates a new file. Error if it already exists.

If you omit the mode, Python defaults to "r".

Closing the File

When you are done with a file, you should close it. Closing releases the connection and ensures that any data waiting to be written actually gets saved to disk.

file = open("notes.txt", "r")
# ... do work with the file ...
file.close()

Forgetting to close a file can cause problems: data may not be saved, other programs may be locked out of the file, and your program may consume resources unnecessarily.

The with Statement — the Right Way to Open Files

Python gives you a cleaner and safer way to handle files: the with statement. It automatically closes the file when the block ends, even if an error occurs inside the block.

with open("notes.txt", "r") as file:
    # work with file here
    pass
# file is automatically closed here

The as file part gives the file object a name inside the block. Once the with block ends, the file is closed. You do not have to call file.close() yourself.

Rule

Always use the with statement when working with files. It is safer, cleaner, and shorter than manually opening and closing.

Reading Text Files

Once a file is open for reading, Python gives you several ways to get the content.

Reading the Entire File at Once with .read()

The .read() method returns the entire file as one string.

with open("message.txt", "r") as file:
    content = file.read()

print(content)

This is simple and works well for small files. For very large files, loading everything into one string uses a lot of memory.

message.txt
Hello, Rosa.
Your report is ready.
Please review it by Friday.
Output
Hello, Rosa. Your report is ready. Please review it by Friday.

Reading One Line at a Time with .readline()

The .readline() method reads one line and moves a pointer forward. The next call reads the next line.

with open("message.txt", "r") as file:
    line1 = file.readline()
    line2 = file.readline()

print(line1)
print(line2)
Output
Hello, Rosa. Your report is ready.

Notice the blank lines. Each line from the file already ends with a newline character (\n), and print() adds another. You can strip the trailing newline with .rstrip():

line1 = file.readline().rstrip("\n")

Or more commonly, .strip() removes all leading and trailing whitespace:

line1 = file.readline().strip()

Reading All Lines into a List with .readlines()

The .readlines() method returns a list where each element is one line from the file, including the newline character.

with open("message.txt", "r") as file:
    lines = file.readlines()

print(lines)
Output
['Hello, Rosa.\n', 'Your report is ready.\n', 'Please review it by Friday.\n']

This is useful when you want to work with all the lines as a list.

Iterating Over a File Directly — the Cleanest Approach

The most Pythonic way to read a file line by line is to iterate over the file object directly using a for loop. This is memory-efficient and clean.

with open("message.txt", "r") as file:
    for line in file:
        print(line.strip())
Output
Hello, Rosa. Your report is ready. Please review it by Friday.
Best Practice

For reading files line by line, use a for loop directly over the file object. It is readable, memory-efficient, and handles large files gracefully.

Worked Example
Counting lines and words in a file
line_count = 0
word_count = 0

with open("message.txt", "r") as file:
    for line in file:
        line = line.strip()
        line_count += 1
        words = line.split()
        word_count += len(words)

print("Lines:", line_count)
print("Words:", word_count)
Output
Lines: 3 Words: 11
  • Iteration 1line is "Hello, Rosa." → line_count becomes 1. words is ["Hello,", "Rosa."] → word_count becomes 2.
  • Iteration 2line is "Your report is ready." → line_count becomes 2. words is ["Your", "report", "is", "ready."] → word_count becomes 6.
  • Iteration 3line is "Please review it by Friday." → line_count becomes 3. words is ["Please", "review", "it", "by", "Friday."] → word_count becomes 11.
  • LessonIt is easy to misjudge a running total by eye. Tracing word counts line by line — 2, then 6, then 11 — catches mistakes that a quick mental estimate would miss. This is exactly why tracing tables matter.

Writing Text Files

Opening a file in "w" mode lets you write to it.

Warning

Opening an existing file in "w" mode destroys everything already in the file. The old content is gone immediately when you call open() with mode "w". There is no undo.

Writing with .write()

The .write() method writes a string to the file. It does not automatically add a newline — you must include \n yourself if you want line breaks.

with open("output.txt", "w") as file:
    file.write("Line one\n")
    file.write("Line two\n")
    file.write("Line three\n")
After running this, output.txt contains
Line one
Line two
Line three

Writing a List of Lines with .writelines()

The .writelines() method writes all strings in a list. Like .write(), it does not add newlines automatically.

lines = ["apple\n", "banana\n", "cherry\n"]

with open("fruits.txt", "w") as file:
    file.writelines(lines)
fruits.txt now contains
apple
banana
cherry
Worked Example
Generating a report file
names = ["Aisha", "Ben", "Clara"]
scores = [88, 74, 95]

with open("report.txt", "w") as file:
    file.write("Student Score Report\n")
    file.write("====================\n")
    for i in range(len(names)):
        line = f"{names[i]}: {scores[i]}\n"
        file.write(line)
    file.write("====================\n")
    average = sum(scores) / len(scores)
    file.write(f"Class average: {average:.1f}\n")

print("Report saved to report.txt")
Contents of report.txt
Student Score Report ==================== Aisha: 88 Ben: 74 Clara: 95 ==================== Class average: 85.7
  • What happensThe program does not print the report to the screen, except for the confirmation message. It writes it to a file that can be opened, shared, or printed later.

Appending Text

When you open a file in "w" mode, you erase what was there. When you open a file in "a" (append) mode, Python adds new content at the end of the existing file. If the file does not exist, Python creates it.

with open("log.txt", "a") as file:
    file.write("Program started at 9:00 AM\n")

Run this several times. Each run adds another line to log.txt. The old content is preserved.

Worked Example
A simple log file
import datetime

def log_event(message):
    timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    entry = f"[{timestamp}] {message}\n"
    with open("events.log", "a") as file:
        file.write(entry)

log_event("Program started")
log_event("User entered a name")
log_event("Program finished")
  • Each runAdds three new lines to events.log. The function opens the file, writes one entry, and closes it again — three separate with blocks in total.
  • After two runsevents.log contains six lines in total, each with its own timestamp, in the order the events occurred.
events.log after two runs
[2025-03-12 10:14:02] Program started [2025-03-12 10:14:02] User entered a name [2025-03-12 10:14:03] Program finished [2025-03-12 10:15:30] Program started [2025-03-12 10:15:31] User entered a name [2025-03-12 10:15:31] Program finished

This pattern — opening in append mode, writing a timestamped entry, closing — is the basis of log files in almost every real program.

When to Use Each Mode

Use "w" when: You want to create or completely replace a file.

Use "a" when: You want to add to an existing file without losing what is already there.

Sections 21.8 – 21.10
The Context: Safety, Newlines, and Common Errors
Context Managers · Newline Handling · Error Diagnosis

The with Statement

The with statement is Python's way of managing resources that need to be opened and closed properly. It is called a context manager.

with open("data.txt", "r") as file:
    content = file.read()

What happens step by step:

Execution Order
How a with block runs
  • Step 1Python calls open("data.txt", "r") and gets a file object.
  • Step 2The file object is assigned to file.
  • Step 3The code inside the block runs.
  • Step 4When the block ends — whether normally or because of an error — Python automatically calls file.close().

This guarantees the file is always closed, even if an exception occurs inside the block.

Compare the two approaches:

Without with — risky
file = open("data.txt", "r")
content = file.read()
file.close()       # If an error happens above, this never runs
With with — safe
with open("data.txt", "r") as file:
    content = file.read()
# file is always closed here

The with version is not just shorter. It is genuinely safer. Make it a habit from the start.

Opening Multiple Files at Once

You can open two files in one with statement using a comma:

with open("input.txt", "r") as infile, open("output.txt", "w") as outfile:
    for line in infile:
        outfile.write(line.upper())

This reads every line from input.txt and writes it in uppercase to output.txt.

Newlines and Text Formatting

Text files are sequences of characters. Lines end with a newline character, written in Python as \n. Understanding newlines prevents a lot of confusing bugs.

What Newlines Look Like in Python

When you read a line from a file, the newline character is included at the end:

with open("items.txt", "r") as file:
    line = file.readline()
    print(repr(line))

If items.txt contains apple, the output will be:

'apple\n'

The repr() function shows you the string exactly as Python stores it, including invisible characters. This is a useful debugging trick.

Stripping Newlines

Most of the time you want to remove the trailing newline before working with the line. Use .strip() or .rstrip("\n"):

with open("items.txt", "r") as file:
    for line in file:
        item = line.strip()
        print(item)

.strip() removes all leading and trailing whitespace — spaces, tabs, and newlines. This is usually what you want.

Adding Newlines When Writing

When you write to a file, Python does not add newlines for you. If you forget \n, all your output runs together on one line:

with open("test.txt", "w") as file:
    file.write("first")
    file.write("second")
    file.write("third")
Contents of test.txt
firstsecondthird

That is one long line, not three separate lines. Add \n to each:

with open("test.txt", "w") as file:
    file.write("first\n")
    file.write("second\n")
    file.write("third\n")
Contents of test.txt
first second third

Cross-Platform Newlines

Windows uses \r\n (carriage return + newline) as the line ending. macOS and Linux use \n. Python's open() handles this automatically in text mode — it converts \r\n to \n when reading and writes the platform-appropriate ending when writing. You do not usually need to think about this, but it is good to know if you ever see mysterious \r characters in your data.

Common File Errors

File operations can fail in many ways. Understanding the common errors helps you debug quickly and design programs that handle problems gracefully.

FileNotFoundError

This is the most common file error. It happens when you try to open a file for reading and the file does not exist at the path you specified.

with open("missing.txt", "r") as file:
    content = file.read()
Error
FileNotFoundError: [Errno 2] No such file or directory: 'missing.txt'

Common causes:

  • The file name is misspelled
  • The file is in a different folder than you think
  • The file has not been created yet
  • You used an absolute path on one computer but the file is not there on another

Debugging approach: Print the path before opening to confirm it is what you expect. Use os.path.exists() to check.

import os
path = "missing.txt"
print("File exists:", os.path.exists(path))

PermissionError

This happens when your program tries to open a file it is not allowed to access — for example, a system file, a file owned by another user, or a file that another program has locked.

PermissionError: [Errno 13] Permission denied: '/etc/passwd'

This is less common in student projects but can appear if you try to write to a read-only location or a protected system directory.

IsADirectoryError

This happens when you pass a folder path where Python expects a file path.

with open("my_folder", "r") as file:
    pass
Error
IsADirectoryError: [Errno 21] Is a directory: 'my_folder'

Writing to a File Fails Silently

One tricky error is not an exception at all. If you open a file in "r" mode and try to call .write(), you get an error. But sometimes students open in "r" mode and then wonder why their data was never saved — they forgot that "r" is read-only.

with open("output.txt", "r") as file:
    file.write("hello\n")    # io.UnsupportedOperation: not writable

Overwriting Instead of Appending

This is a logic error, not a Python exception. If you mean to add to a file but open it with "w" instead of "a", the old content is silently erased.

# Bug: this erases the file each time instead of adding to it
with open("log.txt", "w") as file:
    file.write("New entry\n")

Fix: use "a" mode when you want to preserve existing content.

Handling File Errors with try/except

You will learn exception handling fully in Chapter 23, but here is a preview of how to handle FileNotFoundError safely:

filename = "notes.txt"

try:
    with open(filename, "r") as file:
        content = file.read()
    print(content)
except FileNotFoundError:
    print(f"Error: the file '{filename}' was not found.")
    print("Please check the file name and location.")

This lets your program continue gracefully instead of crashing when a file is missing.

Looking Ahead

Chapter 23 covers try, except, else, and finally in full detail, including how to handle multiple kinds of file and input errors in one program.

Chapter Summary

Key Terms

file
A named collection of data stored on disk that persists after a program ends.
file system
The hierarchical structure of folders and files on a computer's storage.
path
A string that identifies the location of a file or folder in the file system.
absolute path
A path that starts from the root of the file system.
relative path
A path interpreted relative to the current working directory.
current working directory
The folder that Python treats as the base when resolving relative paths.
file object
The object returned by open() that represents the connection to a file.
mode
The string argument to open() that specifies how the file will be used ("r", "w", "a").
open()
The built-in function that opens a file and returns a file object.
.read()
File method that returns the entire file contents as one string.
.readline()
File method that reads one line and advances the file position.
.readlines()
File method that returns all lines as a list of strings.
.write()
File method that writes a string to the file.
.writelines()
File method that writes a list of strings to the file.
.close()
File method that closes the connection to the file.
with statement
A context manager that automatically closes a file when the block ends.
newline character
The character \n that marks the end of a line in a text file.
.strip()
String method that removes leading and trailing whitespace, including newlines.
append mode
File mode "a" that adds to the end of an existing file without erasing it.
FileNotFoundError
The exception raised when open() cannot find the file at the given path.
PracticeCode Reading

Read each program carefully. Answer the questions without running the code.

Exercise 21-R1
with open("greeting.txt", "w") as file:
    file.write("Hello\n")
    file.write("World\n")
  1. What mode is the file opened in?
  2. What will greeting.txt contain after this program runs?
  3. If greeting.txt already contained the text "Goodbye\n" before this program ran, what will it contain afterward?
Exercise 21-R2
with open("data.txt", "r") as file:
    for line in file:
        print(line.strip())

Assume data.txt contains:

apple
banana
cherry
  1. How many times does the loop body run?
  2. What does line contain on the second iteration, before .strip() is applied?
  3. What does the program print?
Exercise 21-R3
total = 0
count = 0

with open("scores.txt", "r") as file:
    for line in file:
        score = int(line.strip())
        total += score
        count += 1

if count > 0:
    print(f"Average: {total / count:.1f}")
else:
    print("No scores found.")

Assume scores.txt contains:

80
90
70
100
  1. Trace the values of total and count after each iteration.
  2. What does the program print?
  3. What would happen if scores.txt were empty?
Exercise 21-R4
with open("notes.txt", "a") as file:
    file.write("Meeting at 3pm\n")

with open("notes.txt", "a") as file:
    file.write("Call Rosa\n")
  1. What mode is used each time?
  2. If notes.txt was empty before, what does it contain after both with blocks run?
  3. If you ran this entire program a second time, what would notes.txt contain?
PracticeCode Tracing

Trace each program by tracking variables after each significant step.

Exercise 21-T1

Assume words.txt contains:

cat
dog
fish
result = []

with open("words.txt", "r") as file:
    for line in file:
        word = line.strip()
        result.append(word.upper())

print(result)

Trace the contents of result after each iteration. What does the program print?

Exercise 21-T2
lines_written = 0

items = ["pen", "notebook", "eraser"]

with open("supplies.txt", "w") as file:
    for item in items:
        file.write(item + "\n")
        lines_written += 1

print(f"Wrote {lines_written} lines.")

Trace lines_written after each iteration. What does the program print? What does supplies.txt contain?

Exercise 21-T3
with open("log.txt", "w") as file:
    file.write("Start\n")

with open("log.txt", "a") as file:
    file.write("Middle\n")

with open("log.txt", "a") as file:
    file.write("End\n")

with open("log.txt", "r") as file:
    content = file.read()

print(content)

Trace what log.txt contains after each with block. What does the program print at the end?

PracticePredict the Output

For each program, predict exactly what is printed to the screen. If the program causes an error, identify the error and explain why.

Exercise 21-P1

Assume hello.txt contains exactly: Hello, world! (no newline at the end)

with open("hello.txt", "r") as file:
    text = file.read()

print(len(text))
print(text.upper())
Exercise 21-P2
with open("numbers.txt", "w") as file:
    for n in range(1, 5):
        file.write(str(n))

with open("numbers.txt", "r") as file:
    print(file.read())
Exercise 21-P3

Assume ghost.txt does not exist.

with open("ghost.txt", "r") as file:
    print(file.read())
Exercise 21-P4
lines = ["one\n", "two\n", "three\n"]

with open("count.txt", "w") as file:
    file.writelines(lines)

with open("count.txt", "r") as file:
    data = file.readlines()

print(len(data))
print(data[1])
PracticeDebugging and Error Analysis

Each program below contains a bug or design problem. Identify the issue and write a corrected version.

Exercise 21-D1
file = open("output.txt", "w")
file.write("Hello\n")
file.write("World\n")

What is the problem? What is the risk if this code runs inside a larger program that might crash?

Exercise 21-D2
with open("journal.txt", "w") as file:
    file.write("Day 1: Arrived safely.\n")

with open("journal.txt", "w") as file:
    file.write("Day 2: Explored the city.\n")

with open("journal.txt", "r") as file:
    print(file.read())

The student expected to see both days printed. What went wrong? How do you fix it?

Exercise 21-D3
with open("prices.txt", "r") as file:
    for line in file:
        price = float(line)
        print(f"${price:.2f}")

Assume prices.txt contains:

3.99
12.50
7.00

The program crashes. What is the likely error? How do you fix it?

Exercise 21-D4
with open("report.txt", "w") as file:
    file.write("Name: Aisha")
    file.write("Score: 95")
    file.write("Grade: A")

The program runs without error, but the file does not look right. What is wrong? Write the corrected version.

Exercise 21-D5
filename = "data.txt"
with open(filename, "r") as file:
    lines = file.readlines()

with open(filename, "w") as file:
    for line in lines:
        file.write(line)

This program reads a file and writes it back. It appears to work, but there is a potential danger. What is it? Under what circumstances could this destroy data?

PracticeShort Programming Tasks

Write a complete Python program for each task. Test your program before submitting it.

  1. Word Saver (21-S1). Write a program that asks the user to enter five words, one at a time, and saves each word on its own line in a file called words.txt. After saving, open the file and print all the words back.
  2. Line Counter (21-S2). Write a function called count_lines(filename) that takes a file name as a parameter and returns the number of lines in the file. If the file does not exist, the function should return -1. Test your function with at least two different files.
  3. File Reverser (21-S3). Write a program that reads all the lines from a file, reverses their order, and writes them to a new file. For example, if original.txt contains first, second, third on separate lines, then reversed.txt should contain third, second, first.
  4. Append Log (21-S4). Write a function called log(message) that appends a line to a file called program.log. The line should contain the message followed by a newline. Call the function at least four times with different messages, then read and print the entire log file.
  5. File Statistics (21-S5). Write a program that reads a text file and prints the number of lines, the number of non-empty lines, the total number of words, and the total number of characters (not counting newlines). Test it with a file you create.
  6. Number File Processor (21-S6). Write a program that reads a file called numbers.txt where each line contains one integer. The program should compute and print the minimum, maximum, sum, and average of all the numbers. Handle the case where the file is empty.
Mini-ProjectNotes and Log File Program

Build a small console program that manages a personal notes file and a log file.

Core Features

When the program starts, it should append a timestamped entry to activity.log saying the program started, using the format [YYYY-MM-DD HH:MM:SS] Program started.

Menu shown to the user
=== Notes Program ===
1. View all notes
2. Add a note
3. Clear all notes
4. Quit
Option 1
View All Notes
Read and print every line from notes.txt. If the file does not exist, print a friendly message saying no notes exist yet.
Option 2
Add a Note
Ask the user for a note and append it (with a newline) to notes.txt. Also log the action to activity.log: [timestamp] Note added.
Option 3
Clear All Notes
Open notes.txt in "w" mode and write nothing, effectively emptying it. Ask for confirmation before clearing. Log the action.
Option 4
Quit
Log [timestamp] Program ended. and exit the program.

Requirements

  • Use with statements for every file operation.
  • Handle the case where notes.txt does not exist when the user tries to view notes.
  • Keep all file-related code inside well-named functions: log_event(message), view_notes(), add_note(text), clear_notes(), show_menu(), and main().
Challenge Extensions

Add the date and time to each note automatically when it is added. Add a "Search notes" option that prints only lines containing a given word. Format the log file entries so the program name is included: [timestamp] [notes_program] Note added. Let the user choose a custom notes file name at startup.

Sample run
=== Notes Program ===
1. View all notes
2. Add a note
3. Clear all notes
4. Quit
> 2
Enter your note: Buy milk and eggs.
Note saved.

=== Notes Program ===
1. View all notes
2. Add a note
3. Clear all notes
4. Quit
> 1
--- Your Notes ---
Buy milk and eggs.
------------------

=== Notes Program ===
...
> 4
Goodbye.
CumulativeMixed Review

These questions draw on earlier chapters. Keep your earlier skills sharp.

  1. (21-MR1) Write a function called words_in_list(filename) that reads a text file (one word per line) and returns a list of all the words, stripped of whitespace. If the file does not exist, return an empty list.
  2. (21-MR2) Write a program that generates a multiplication table for numbers 1 through 5 and saves it to times_table.txt. Each row should look like 1 x 1 = 1 through 5 x 5 = 25.
  3. (21-MR3) A file called inventory.txt has entries in this format, one per line: pencil,50. Write a program that reads the file and prints each item and its quantity, then prints the total number of items across all categories.
  4. (21-MR4) Write a function called save_list(items, filename) that takes a list of strings and writes each item on its own line to the given file. Write a second function called load_list(filename) that reads the file and returns a list of strings (stripped). Demonstrate that saving a list and then loading it gives back the same list.
Chapter 21 Checklist
Use this to confirm your understanding before moving to Chapter 22
Concepts
  • I can explain why programs use files and what persistence means.
  • I can describe the file system as a hierarchy of folders and identify the parts of a path.
  • I can distinguish absolute paths from relative paths and explain why relative paths suit beginner projects.
  • I can explain what each file mode ("r", "w", "a") does.
  • I can explain why the with statement is safer than manually opening and closing files.
  • I can explain the difference between .read(), .readline(), and .readlines().
  • I can explain why opening a file in "w" mode erases its existing contents.
  • I can explain why "a" mode is used for log files and accumulating records.
Applications
  • I can write a program that reads every line of a file using a for loop.
  • I can write a program that writes multiple lines to a file, including newline characters.
  • I can write a function that appends a timestamped entry to a log file.
  • I can diagnose a FileNotFoundError and explain its likely causes.
Study Habits
  • I trace file-processing code line by line rather than guessing the output.
  • I check whether I meant "w" or "a" before opening a file for writing.
  • I know the difference between a silent logic error and a Python exception.
Preview — Chapter 22
CSV Files and Table Data

Chapter 21 taught you to read and write plain text files — raw lines of characters. Many real programs work with structured table data: rows and columns, like a spreadsheet. The most common format for this kind of data is the CSV file (comma-separated values). Chapter 22 introduces CSV files. You will learn how rows and columns are stored, how to read CSV files with Python's csv module, how to write CSV files, how to filter rows, how to summarize columns, and how to clean table data. The skills in Chapter 22 connect directly to data analysis, spreadsheets, databases, and practical automation.

MGU Library Connection
  • Programming with Python — Appendix I: File and CSV Reference
  • Programming with Python — Appendix J: Standard Library Beginner Map
  • Companion resource: File Path Guide
  • Companion resource: Debugging Checklist
  • Next chapter: Chapter 22 — CSV Files and Table Data
  • Previous course: Computer Science Foundations — background on data and information