Computer Programming 1 (Python) • Files & Data I/O

Reading Text Files — I can read a text file using with open(...) and process it line by line safely.

Learn to open, read, and process text files safely using Python's with statement and line-by-line iteration.

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

SECTION 1

Historical Context & Motivation

Programs have always needed a way to interact with data stored outside of memory. In the earliest days of computing, data lived on physical punch cards, and programmers had to deal with complicated hardware-level commands just to read a single row of holes. As operating systems matured, the concept of a file became the standard way to package and store information on disk. Every application you use today—from web browsers to video games—reads and writes files constantly.

Python's approach to file handling was shaped by decades of lessons learned from older languages like C, where forgetting to close a file could leak system resources and crash programs. Python introduced the with statement (also called a context manager) to make file I/O safer and more readable. This was a huge improvement because it guaranteed that files would be properly closed, even if an error occurred during processing.

1960s • Punch Cards & Tape

Early programs read data from physical punch cards and magnetic tape, requiring low-level hardware commands to access stored information.

1972 • C Language File I/O

The C programming language introduced fopen() and fclose() functions. Forgetting to call fclose() led to resource leaks—a common and frustrating bug.

1991 • Python 1.0 Introduces open()

Guido van Rossum created Python with a built-in open() function, making file operations simpler than in C or Java.

2005 • PEP 343 — The 'with' Statement

Python 2.5 introduced the with statement, enabling automatic resource management. Files opened with with open(...) are guaranteed to close, even if an error occurs.

2020s • Modern Python Best Practice

Today, with open(...) is the universally recommended pattern for file I/O in Python, used in data science, web development, and automation.

The central question that drives this lesson is straightforward: how can you reliably open a text file, read its content line by line, and guarantee that the file is properly closed when you are done? Understanding this pattern is essential because almost every real-world program needs to load data from external files at some point.

SECTION 2

Core Principles & Definitions

Before diving into code, you need to understand a few foundational ideas about how Python handles files. These principles will help you understand not just what the code does, but why the code is written a certain way.

1

File Object

When you call open(), Python creates a file object that acts as your connection to the data on disk. Think of it as a handle you hold while reading.
2

The with Statement

The with keyword creates a context manager that automatically closes the file when the indented block finishes, even if an error occurs.
3

Read Mode ('r')

Files can be opened in different modes. The default mode 'r' means read-only. You can look at the data, but you cannot change or delete it.
4

Line-by-Line Iteration

A file object is iterable—you can loop over it with a for loop, and each pass gives you one line of text. This is memory-efficient for large files.
5

The .strip() Method

Each line from a file includes a hidden newline character (\n) at the end. Calling .strip() removes this trailing whitespace cleanly.
✦

KEY TAKEAWAY

Think of with open(...) like borrowing a library book with an automatic return system. The with statement is the checkout desk—it hands you the book (file object) and guarantees the book is returned (file is closed) as soon as you walk out of the building (leave the indented block), even if you trip on your way out (encounter an error).

SECTION 3

Visual Explanation — How with open() Works

The diagram below illustrates the entire lifecycle of reading a text file in Python. Follow the flow from left to right to see how with open() opens a connection, how the for loop processes lines, and how the file is automatically closed at the end.

Lifecycle of with open("data.txt", "r") as f:STEP 1open() calledPython asks the OSto open the file→ file object createdSTEP 2with block enteredFile object assignedto variable fas f:STEP 3Process linesfor loop reads oneline at a timefor line in f:STEP 4Auto-closewith block ends→ f.close()called auto ✓Code Anatomywithopen("data.txt","r")asf:forlineinf:print(line.strip())← open file, assign to f← iterate one line at a time← process each line (remove \n)← indentation ends here → file auto-closedKeywords shown in purple · Built-in functions in cyan · Strings in green · Variables in gold
The top row shows the four-step lifecycle: open, enter the with block, process lines, and auto-close. The bottom section shows the syntax-highlighted code with annotations explaining each line.

Notice that the entire reading operation happens inside the indented block under the with statement. As soon as your code exits that indented block—whether it finishes normally or encounters an error—Python calls f.close() automatically. You never have to remember to close the file yourself. The for line in f loop reads one line at a time from the file, which means even a file with millions of lines will not crash your program by using too much memory.

SECTION 4

How It Works — The Mechanics of File Reading

The open() Function

The open() function is a Python built-in that takes at least one argument—the path to the file—and returns a file object. The second argument, the mode string, tells Python what you want to do with the file. The most common modes are listed in the table below.

ModeMeaningCreates File?Overwrites?
"r"Read (default)No — error if missingNo
"w"WriteYesYes — erases existing content
"a"AppendYesNo — adds to end
"r+"Read and WriteNo — error if missingPartial (depends on position)

Common file modes in Python

Reading Methods Compared

Python gives you three main ways to read content from a file object. Each approach has trade-offs related to memory usage and convenience. The method you choose depends on how large your file is and how you need to process the data.

MethodReturnsMemory UsageBest For
f.read()Entire file as one stringHigh — loads everythingSmall files (under 1 MB)
f.readlines()List of all linesHigh — loads everythingWhen you need all lines in a list
for line in f:One line per iterationLow — one line at a timeLarge files, line-by-line processing

Three ways to read file content

💡

Why line-by-line is best

Using for line in f: is the recommended approach for most situations. It reads only one line into memory at a time, so your program can handle files of any size—even multi-gigabyte log files—without running out of memory.

Encoding: How Text Becomes Bytes

Under the hood, files store raw bytes, not characters. Python converts bytes to text using an encoding. The default encoding in modern Python is UTF-8, which supports nearly every language and special character. If you ever get garbled output, you can specify the encoding explicitly: open("data.txt", "r", encoding="utf-8").

SECTION 5

Common Patterns for Processing Lines

Once you know how to read a file line by line, there are several common patterns you will use repeatedly. These patterns form the building blocks for almost every file-processing task in Python, from analyzing data logs to loading configuration settings.

Common File-Processing PatternsPATTERN 1 — Print All Lineswithopen("data.txt")asf:forlineinf:print(line.strip())Use case: Quick preview of filecontents, debugging data formatsBEGINNERPATTERN 2 — Collect into Listlines = []withopen("data.txt")asf:forlineinf:lines.append(line.strip())Use case: Store all lines for laterprocessing or random accessINTERMEDIATEPATTERN 3 — Search for a Keywordwithopen("log.txt")asf:forlineinf:if"ERROR"inline:print(line.strip())Use case: Searching log files,filtering data by conditionPRACTICALPATTERN 4 — Count Linescount = 0withopen("data.txt")asf:forlineinf:count += 1print(count)Use case: Quick file statistics,verifying data was loaded correctlyCOMMON
Four essential patterns for line-by-line file processing. Pattern 1 prints every line. Pattern 2 collects lines into a list for later use. Pattern 3 uses a conditional to filter specific lines. Pattern 4 counts the total number of lines in a file.

All four patterns share the same basic structure: a with open(...) as f: wrapper around a for line in f: loop. What changes is what you do inside the loop. Pattern 1 simply prints each line. Pattern 2 builds a list so you can access lines by index later. Pattern 3 adds an if statement to filter lines that match a condition. Pattern 4 accumulates a counter to determine how many lines the file contains.

⚠

Don't forget .strip()!

Each line read from a file ends with a newline character \n. If you print lines without calling .strip(), you will see extra blank lines in your output because print() adds its own newline on top of the one already in the string.

SECTION 6

Worked Example — Analyzing a Grades File

Suppose you have a file called grades.txt that contains one numeric grade per line. Your task is to read the file, compute the average grade, and print the result. Here is what the file looks like:

📝

grades.txt

92 85 78 95 88 76 91

Computing the Average Grade from a File

  1. 1

    Step 1 — Plan the Variables

    To compute an average, we need two pieces of information: the total sum of all grades and the count of how many grades there are. We initialize both to zero before opening the file.
    total = 0 and count = 0
  2. 2

    Step 2 — Open the File with 'with'

    We use with open("grades.txt", "r") as f: to safely open the file in read mode. The variable f now holds our file object.
    with open("grades.txt", "r") as f:
  3. 3

    Step 3 — Loop Through Each Line

    Inside the with block, we write for line in f: to iterate through the file one line at a time. Each line is a string like "92\n". We call line.strip() to remove the trailing newline, then convert it to an integer with int().
    grade = int(line.strip())
  4. 4

    Step 4 — Accumulate the Sum and Count

    For each line, we add the grade to our running total and increment the count by one. After the loop finishes, total will hold the sum of all grades (92 + 85 + 78 + 95 + 88 + 76 + 91 = 605) and count will be 7.
    total += grade and count += 1
  5. 5

    Step 5 — Compute and Print the Average

    After the with block ends (the file is now closed), we compute the average by dividing total by count. The result is 605 ÷ 7 ≈ 86.43.
    average = total / count → 86.43

Here is the complete program:

ℹ

Complete Code

total = 0 count = 0 with open("grades.txt", "r") as f: for line in f: grade = int(line.strip()) total += grade count += 1 average = total / count print(f"Average grade: {average:.2f}")

SECTION 7

with open() vs. Manual open/close — Strengths & Limitations

You can technically open files without the with statement by calling f = open("data.txt") and later f.close(). However, this older approach is error-prone and is not recommended. Let's compare both approaches side by side.

Featurewith open() ✅Manual open/close ⚠️
File guaranteed to closeYes — automatic on block exitOnly if you remember to call f.close()
Safe during errorsYes — closes even if an exception occursNo — if an error occurs before f.close(), file stays open
Lines of codeFewer — no separate close() call neededMore — requires explicit f.close()
ReadabilityClear indentation shows file scopeHarder to see where file is in use
Industry standardYes — PEP 8 recommendedConsidered outdated

Comparing safe vs. manual file handling

✦

KEY TAKEAWAY

Using with open() is like wearing a seatbelt while driving. You could technically drive without one, and you might be fine most of the time—but the one time something goes wrong (an error or crash), the seatbelt (automatic close) prevents a much bigger disaster (resource leaks, corrupted data). Always use with.

SECTION 8

Connection to Advanced File I/O

Reading plain text files line by line is your starting point, but the same foundational pattern extends to much more powerful file operations. As you advance in programming, you will encounter structured file formats and specialized libraries that build on these same principles.

This LessonWhat Comes Next
Reading .txt filesReading .csv files with the csv module
Line-by-line iterationParsing structured data (JSON, XML)
Read mode ("r")Write mode ("w") and append mode ("a")
Single file processingProcessing multiple files with os.listdir()
Manual string processingPandas DataFrames for large-scale data analysis

From basic text files to advanced data processing

The beauty of the with statement is that it is not limited to files. Python's context manager protocol works with database connections, network sockets, and even custom classes. Once you understand the pattern of "open a resource, use it, close it automatically," you have a mental model that will serve you across every area of software development.

ℹ

Error Handling Preview

What happens if the file does not exist? Python raises a FileNotFoundError. In future lessons, you will learn to handle this gracefully using try / except blocks. For now, just make sure the file you are trying to open actually exists in the same folder as your Python script.

SECTION 9

Practice Problems

Work through these five problems to reinforce your understanding. Each one builds on the concepts covered in this lesson. Try writing the code yourself before checking the answer.

Question 1: CONCEPTUAL

Explain in your own words why using with open(...) as f: is safer than using f = open(...) followed by f.close(). What specific problem does the with statement prevent?
Show answer
The with statement guarantees that the file will be closed when the indented block exits, even if an error (exception) occurs during processing. Without with, if an error happens between open() and close(), the program crashes and the file is never closed. This causes a resource leak—the operating system keeps the file locked, which can prevent other programs from accessing it and wastes system resources.

Question 2: BASIC

Write a short Python program that opens a file called names.txt and prints every line with the line number in front of it. For example, if the file contains "Alice" on line 1, it should print 1: Alice.
Show answer
Here is the solution: with open("names.txt", "r") as f: num = 1 for line in f: print(f"{num}: {line.strip()}") num += 1 We initialize a counter num to 1 before the loop. Inside the loop, we print the number followed by the stripped line, then increment the counter. Alternatively, you could use enumerate(f, start=1) to get both the number and the line in a single statement.

Question 3: INTERMEDIATE

A file called temperatures.txt contains one temperature reading per line (as a decimal number). Write a program that reads the file and prints the highest temperature found. Do not use f.readlines()—process the file line by line.
Show answer
Here is the solution: highest = None with open("temperatures.txt", "r") as f: for line in f: temp = float(line.strip()) if highest is None or temp > highest: highest = temp print(f"Highest temperature: {highest}") We initialize highest to None so the first temperature automatically becomes the highest. On each iteration, we convert the stripped line to a float and compare it to the current highest. This approach uses minimal memory since only one line is in memory at a time.

Question 4: APPLIED

You are building a simple word-count tool. Given a file called essay.txt, write a program that counts the total number of words in the file. Assume words are separated by spaces. Print the result as: "Total words: X".
Show answer
Here is the solution: word_count = 0 with open("essay.txt", "r") as f: for line in f: words = line.strip().split() word_count += len(words) print(f"Total words: {word_count}") For each line, we first strip the newline, then call .split() which breaks the line into a list of words (splitting on whitespace by default). We add the length of that list to our running count. The .split() method also handles multiple spaces and tabs correctly, treating any whitespace as a separator.

Question 5: CRITICAL THINKING

A file called scores.txt has lines in the format "Name,Score" (for example, "Alice,95"). Write a program that reads the file and prints only the names of students who scored 90 or above. What would happen if a line were blank or improperly formatted? How might you handle that?
Show answer
Here is the core solution: with open("scores.txt", "r") as f: for line in f: line = line.strip() if line == "": continue parts = line.split(",") if len(parts) == 2: name = parts[0] score = int(parts[1]) if score >= 90: print(name) We use .split(",") to separate the name from the score. To handle bad data, we check if the line is blank (using continue to skip it) and verify that the split produced exactly two parts. A more robust solution could wrap int(parts[1]) in a try/except block to catch lines where the score is not a valid number. This kind of defensive programming is essential when working with real-world files that may contain unexpected content.

SUMMARY

Lesson Summary

Summary

In this lesson, you learned to read text files safely in Python using the with open() pattern, which creates a context manager that automatically closes the file when the indented block ends. You saw that Python's open() function returns a file object, and that the default read mode ("r") lets you access the data without risk of accidentally changing it.

The most memory-efficient way to process a file is line-by-line iteration using for line in f:, which reads only one line at a time. You also learned to use .strip() to remove trailing newline characters, and you practiced four essential patterns: printing lines, collecting into a list, filtering by condition, and counting lines. These patterns are the building blocks for all future file-processing tasks in Python.

Varsity Tutors • Computer Programming 1 (Python) • Reading Text Files