How to Read a Text File in Python: Methods, Options, and What to Know First

Reading a text file in Python is one of the most fundamental file operations you'll encounter — and Python gives you several ways to do it. The method that works best depends on the file size, what you plan to do with the data, and how much control you need over the reading process.

The Core Concept: Opening a File Before Reading It

Before Python can read a file, it needs to open it. The built-in open() function handles this. It takes the file path as its first argument and an optional mode parameter. For reading text files, the mode is 'r' (read), which is actually the default — so you can often omit it.

file = open('example.txt', 'r') 

However, the modern and strongly preferred approach uses a with statement, also called a context manager:

with open('example.txt', 'r') as file: content = file.read() 

The with block automatically closes the file when the block exits — even if an error occurs. Leaving files open is a common source of bugs and resource leaks, so this pattern matters in practice, not just in style guides.

Three Main Ways to Read the File Content

Once the file is open, you have three primary reading methods:

.read() — Load the Entire File at Once

with open('example.txt', 'r') as file: content = file.read() 

This pulls the entire file into a single string. It's straightforward and works well for small files. For large files — think hundreds of megabytes or more — loading everything into memory at once can slow your program down or cause memory issues.

.readline() — Read One Line at a Time

with open('example.txt', 'r') as file: first_line = file.readline() second_line = file.readline() 

Each call to .readline() advances an internal file pointer to the next line. This gives you fine-grained control but gets unwieldy if you're manually looping through a long file.

.readlines() — Load All Lines Into a List

with open('example.txt', 'r') as file: lines = file.readlines() 

This returns a list of strings, one per line, with newline characters () included. It's useful when you need indexed access to specific lines, but it still loads the whole file into memory.

Iterating Line by Line: The Memory-Efficient Approach 🗂️

For large files, the most practical pattern is iterating directly over the file object:

with open('example.txt', 'r') as file: for line in file: print(line.strip()) 

Python reads one line at a time without loading the entire file into memory. The .strip() call removes trailing newline characters and whitespace, which is almost always what you want when processing lines individually.

Handling Encoding: An Often-Overlooked Variable

Text files aren't just text — they're text encoded in a specific format. The most common is UTF-8, but files can also use UTF-16, ASCII, Latin-1, and others. If you open a file with the wrong encoding, you'll get garbled output or a UnicodeDecodeError.

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

Specifying encoding='utf-8' explicitly is a good habit. Python's default encoding depends on the operating system — it's often UTF-8 on macOS and Linux, but can differ on Windows — so not specifying it can cause code to behave differently on different machines.

What Happens With File Paths

The path you pass to open() can be relative or absolute.

Path TypeExampleWhat It Means
Relative'data/example.txt'Relative to the script's working directory
Absolute'/home/user/data/example.txt'Full path from the filesystem root
Windows absolute'C:\Users\user\data\example.txt'Windows-style, note the double backslash

On Windows, you can also use a raw string (r'C:Usersuserdataexample.txt') to avoid issues with backslashes being interpreted as escape characters.

Error Handling: What Can Go Wrong

A few errors come up frequently when reading text files:

  • FileNotFoundError — the path is wrong or the file doesn't exist
  • PermissionError — the file exists but the process doesn't have read access
  • UnicodeDecodeError — the encoding specified doesn't match the file's actual encoding

Wrapping your file operation in a try/except block handles these gracefully:

try: with open('example.txt', 'r', encoding='utf-8') as file: content = file.read() except FileNotFoundError: print("File not found.") except UnicodeDecodeError: print("Encoding mismatch — try a different encoding.") 

Factors That Shape Which Approach Makes Sense 🔍

The "right" method isn't universal — it shifts based on several variables:

  • File size: Small config files or logs? .read() is fine. Processing a multi-gigabyte dataset? Line-by-line iteration is far safer.
  • What you're doing with the data: If you need to search for a pattern, streaming line-by-line works. If you need to parse the whole structure at once, loading into memory makes more sense.
  • File encoding: Files generated on different systems or by different applications often use different encodings. Files from older Windows software frequently use Windows-1252 or Latin-1.
  • Python version: Python 3 handles text and bytes as separate types. If you're reading binary files disguised as text, or working with Python 2 legacy code, the behavior differs significantly. Python 3 is standard now, but the distinction matters if you're maintaining older scripts.
  • Where the file lives: Reading from a local disk is straightforward. Reading from a network path, a mounted cloud drive, or a virtual filesystem introduces latency and potential access errors that local file handling doesn't account for.

Stripped-Down vs. Full-Featured File Reading

For a simple script that reads a small config file, a three-line block with .read() is entirely appropriate. For a data pipeline processing log files in the gigabyte range, you'd want to think carefully about memory, buffering, encoding, and error recovery.

Python also has higher-level libraries — like pathlib for cleaner path handling, csv for structured text data, and pandas for tabular data — that build on these same fundamentals. Which layer of abstraction serves your needs depends on what the file contains and what your code ultimately needs to do with it.