Maintenance

Site is under maintenance — quizzes are still available.

Go to quizzes

Read and Write Text Files

Learn to manage files: read and write text files in Python. This hands-on tutorial covers opening, reading, writing, and closing text files, with troubleshooting and practical exercises.

Focus: manage files: read and write text files

Sponsored

Imagine writing a Python script that analyzes log files, generates a report, or saves user preferences, only to discover after an hour that the program crashed because the file you thought you wrote to disk is empty. This is the pain of implicit file management. Without a solid grasp on reading and writing text files, your data is ephemeral—it exists only while your script runs. Mastering file I/O is the gateway between your Python program and persistent storage, enabling you to save results, load configurations, and process external data with confidence.

The problem this lesson solves

Even simple scripts need to talk to the filesystem. You need to save a user's high score, read a list of cities from a CSV, or write a log of errors. Without proper file handling, you either lose data when the program ends or, worse, corrupt existing files by accident. The core problem is that Python has multiple ways to interact with files (reading, writing, appending), and beginners often miss crucial steps like closing files or handling encoding errors. This lesson removes that guesswork by giving you a repeatable, safe pattern for every text file operation.

Core concept / mental model

Think of a text file as a long piece of paper (the document) inside a folder. To work with it, you must: 1. Open the folder (open() function) — you get a pointer (file handle) marking your place. 2. Read or write on the paper (.read(), .write(), etc.) — the pointer moves as you work. 3. Close the folder (.close() or context manager) — formally ends the operation and makes sure every change is saved to disk.

A file path tells Python where the folder lives: either a full path like /home/user/data.txt (absolute) or a path relative to the script like 'data.txt' or '../logs/errors.txt'. In Python, the with statement (context manager) automates step 3, which is the recommended best practice because it ensures the file is closed even if an error occurs.

Pro tip: Always use a context manager (with open(...) as f:) instead of plain open()/.close(). It's less error-prone and makes your code more readable.

How it works step by step

Step 1: Choose the file mode

The open() function's second argument is the mode string:

Mode What it does File pointer start at Creates file if missing? Overwrites content?
'r' Read (default) Beginning No No
'w' Write (destructive) Beginning Yes Yes
'a' Append End Yes No
'x' Create exclusively Beginning Yes (but raises FileExistsError if exists) No
'r+' Read and write Beginning No No (manual seek needed)

Step 2: Open the file

Use open(filepath, mode) and a context manager:

with open('myfile.txt', 'r') as f:
    content = f.read()
# file automatically closed here

Step 3: Perform the operation

For reading, you have several methods: - .read() — reads entire content as a single string. - .readline() — reads one line (including the newline character). - .readlines() — returns a list of all lines (each with \n).

For writing: - .write(string) — writes the string to the file. - .writelines(list_of_strings) — writes each string in the list (no newlines added automatically).

Step 4: Handle encoding (always for text files)

Text files have an encoding (usually UTF-8). Always specify it to avoid UnicodeDecodeError:

with open('myfile.txt', 'r', encoding='utf-8') as f:
    content = f.read()

Hands-on walkthrough

Example 1: Write data and then read it back

# Write some lines
lines = ["Line 1\n", "Line 2\n", "Line 3\n"]
with open('sample.txt', 'w', encoding='utf-8') as f:
    f.writelines(lines)

# Read it back
with open('sample.txt', 'r', encoding='utf-8') as f:
    contents = f.read()

print(contents)

Expected output:

Line 1
Line 2
Line 3

Pro tip: .writelines() does not add newlines. You must include \n in each string you write.

Example 2: Append to an existing file

with open('sample.txt', 'a', encoding='utf-8') as f:
    f.write("Appended line\n")

# Verify
with open('sample.txt', 'r', encoding='utf-8') as f:
    print(f.read())

Expected output:

Line 1
Line 2
Line 3
Appended line

Example 3: Read a file line by line (memory-efficient for large files)

with open('sample.txt', 'r', encoding='utf-8') as f:
    for line in f:
        print(f"Read: {line.strip()}")

Expected output:

Read: Line 1
Read: Line 2
Read: Line 3
Read: Appended line

Compare options / when to choose what

The choice of mode depends on your goal:

Scenario Recommended mode Why
Reading a config file 'r' Safe, won't modify the data
Writing a new report 'w' Clear old content first
Adding to a log file 'a' Preserves past entries
Creating a file only if it doesn't exist 'x' Prevents accidental overwrite
Updating a specific part of a file 'r+' Manual .seek() needed

When to read all at once vs line by line: - Use .read() for small files (< 50 MB) or when you need the whole content at once (e.g., parsing a JSON file). - Use iteration (for line in f:) for large files (e.g., processing million-line logs) to avoid memory explosion.

Troubleshooting & edge cases

Common Mistake 1: Forgetting to close the file without a context manager

f = open('data.txt', 'w')
f.write('hello')
# No close() — may not be written to disk!

Fix: Always use with open(...) as f:.

Common Mistake 2: FileNotFoundError when reading a non-existent file

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

Fix: Use 'w' or 'a' mode to create missing files, or check existence first:

import os
if os.path.exists('missing.txt'):
    with open('missing.txt', 'r') as f:
        content = f.read()
else:
    print("File not found")

Common Mistake 3: UnicodeDecodeError / UnicodeEncodeError

# If file is not UTF-8
with open('data.bin', 'r', encoding='utf-8') as f:
    content = f.read()
# UnicodeDecodeError

Fix: Specify the correct encoding (e.g., encoding='latin-1' for legacy files). When in doubt, read the file in binary mode 'rb' and then decode manually.

Common Mistake 4: Newline confusion with writelines

lines = ["line1", "line2"]
with open('output.txt', 'w') as f:
    f.writelines(lines)
# Result: "line1line2" — no newlines!

Fix: Always include \n in each string or use f.write('\n'.join(lines)).

What you learned & what's next

You now know how to: - Explain the difference between 'r', 'w', 'a', 'x', and 'r+' modes. - Open and close files safely using the with statement. - Read text files entirely or line by line. - Write and append text to files. - Troubleshoot common errors like FileNotFoundError and UnicodeDecodeError.

This foundation is critical because in the next lesson you'll learn to work with structured data like CSV and JSON files, where you'll combine file I/O with Python's parsing libraries. You'll never again lose data because of an unclosed file handle.

Practice recap

Create a Python script that asks the user to enter 3 lines of text, writes them to a file named user_notes.txt (one line each, with \n), and then reads the file back and prints each line with a line number. This exercise reinforces safe file writing, line-by-line reading, and proper use of encoding='utf-8'.

Common mistakes

  • Forgetting to use a context manager (with) — the file may not be written to disk if an exception occurs or if you forget to call .close().
  • Using 'w' mode when you meant to append ('a') — this erases all existing content in the file without warning.
  • Ignoring encodings — reading a UTF-8 file without specifying encoding='utf-8' can cause UnicodeDecodeError on non-ASCII characters.
  • Assuming .writelines() adds newlines — it does not; you must include \n in each string or join with newlines manually.

Variations

  1. Use pathlib.Path.read_text() and .write_text() for a more modern, object-oriented API (Python 3.4+).
  2. Use shutil.copy() or fileinput for batch processing or in-place modification of files.
  3. Handle binary files with 'rb' and 'wb' modes for images, audio, or any non-text data.

Real-world use cases

  • Log file analyzers: Read server access logs line by line (hundreds of MB) to count unique IPs or detect patterns.
  • Configuration backup tool: Write a script that reads a YAML config file, validates entries, and writes a backup copy with a timestamp.
  • Data pipeline step: Write a CSV extractor that reads a text file of raw sensor data, cleans lines, and writes a clean CSV for downstream tools.

Key takeaways

  • Always use with open(...) as f: for automatic file closing.
  • Choose the correct mode ('r', 'w', 'a', 'x', 'r+') based on whether you need to read, write, append, or all three.
  • Specify encoding='utf-8' when working with text files to avoid encoding errors.
  • For large files, iterate line by line (for line in f:) instead of loading the entire file into memory with .read().
  • .writelines() does not add newlines — you must explicitly include them in each string.
  • Handle FileNotFoundError by checking os.path.exists() or by using 'w'/'a' modes that create missing files.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.