Python Read Files
Learn practical strategies for reading text and binary files, controlling buffers, and processing large datasets efficiently.
Opening a File for Reading
Always open files with the context manager syntax to ensure timely cleanup:
with open("poem.txt", "r", encoding="utf-8") as file:
content = file.read()
print(content)If the file lives in another folder, build the path with pathlib.Path.
Read Entire File
file.read() returns the entire contents. For medium sized files this is convenient.
with open("story.txt", "r", encoding="utf-8") as file:
text = file.read()
print(len(text))Passing a number reads that many bytes/characters. Subsequent reads continue from the current file pointer.
with open("story.txt", "r", encoding="utf-8") as file:
header = file.read(100)
body = file.read() # RemainderRead Line by Line
Process large files incrementally by iterating over the file object or using readline().
with open("access.log", "r", encoding="utf-8") as file:
for line in file:
if "ERROR" in line:
alert(line.strip())The iteration protocol automatically reads buffered chunks, keeping memory usage low.
Readlines and Slices
file.readlines() materializes every line in a list. Limit the result with the optional hint argument to bound memory usage.
with open("todos.txt", "r", encoding="utf-8") as file:
first_block = file.readlines(2048)
print(first_block[:3])Binary File Reading
Use binary mode ('rb') for non-text data like images, compressed archives, or pickled objects. Omitting encoding is required.
with open("photo.jpg", "rb") as file:
chunk = file.read(1024)
while chunk:
process(chunk)
chunk = file.read(1024)Binary mode returns bytes objects; convert to other formats as needed.
Managing the File Pointer
The file pointer tracks where the next read starts. Use file.tell() to inspect the position and file.seek() to move it.
with open("report.txt", "r", encoding="utf-8") as file:
snippet = file.read(50)
print(file.tell()) # 50
file.seek(0)
print(file.readline())Seek offsets are byte oriented in binary mode and character oriented in text mode.
Graceful Error Handling
Wrap file operations to handle missing files, permission issues, or encoding problems.
from pathlib import Path
path = Path("logs/latest.log")
try:
with path.open("r", encoding="utf-8") as file:
print(file.readline())
except FileNotFoundError:
print(f"Missing file: {path}")
except UnicodeDecodeError as exc:
print(f"Encoding error: {exc}")Reading Structured Data
Most real-world files follow a format. Combine file reading with specialized modules:
json.load(file)for JSON documents.csv.reader(file)for comma-separated values.configparser.ConfigParser()for INI-style configs.pickle.load(file)(trusted input only).
import json
with open("settings.json", "r", encoding="utf-8") as file:
settings = json.load(file)
print(settings["theme"])Performance Tips
- Read in buffered chunks for large binary files.
- Profile hot loops with the
timemodule orcProfile. - Disable universal newline translation by opening with
newline=''when exact bytes matter. - Cache frequently accessed data if the file rarely changes.
Next Steps
Ready to persist updates? Continue to the writing chapter to append, overwrite, and create files safely.
Read the Whole File vs Line by Line
| Method | Returns | Best for |
|---|---|---|
f.read() | Entire file as one string | Small files |
f.readline() | One line | Reading incrementally |
f.readlines() | List of all lines | Small files, need a list |
for line in f | One line per loop | Large files (memory-safe) |
# memory-friendly: never loads the whole file at once
with open("big.log", encoding="utf-8") as f:
for line in f:
print(line.rstrip()) # rstrip() drops the trailing newlineIterating the file object directly is the best way to process large files — it streams one line at a time.
Try It Yourself
Exercise 1: Count the number of lines in a file.
Show solution
with open("demo.txt", encoding="utf-8") as f:
count = sum(1 for _ in f)
print(count)Exercise 2: Print only lines that contain the word "error".
Show solution
with open("app.log", encoding="utf-8") as f:
for line in f:
if "error" in line.lower():
print(line.rstrip())Key Takeaways
read()loads everything; iterate the file for large data.readlines()gives a list;for line in fstreams.- Strip newlines with
.rstrip()when printing.
📘 Real-World Deep Dive
Reading files correctly — line-at-a-time, chunked, or whole — and decoding them are baseline skills. Most production bugs surface here first.
Real-Life Scenario
Streaming a 300 MB log file: warm-up of the ingestion pipeline, parse two columns per line, and bound memory to ~8 KB.
Real-Life Example
import time
from pathlib import Path
def parse(line: str):
parts = line.split(maxsplit=1)
return parts[0], parts[1] if len(parts) > 1 else ""
def stream(path: Path):
with path.open("r", encoding="utf-8") as f:
for line in f:
yield line.rstrip("\n")
start = time.perf_counter()
total = 0
distinct = set()
for line in stream(Path("app.log")):
key, _ = parse(line)
distinct.add(key)
total += 1
print(f"read {total:,} lines, distinct keys: {len(distinct)} in {time.perf_counter()-start:.3f}s")Expected Output
read 1,824,201 lines, distinct keys: 142 in 1.184sCommon mistakes
- Reading an entire file into memory (
f.read()) is fine for small files and a disaster for 1 GB+ files. - Cross-platform newline handling: use
withand"r"always and don't pre-strip; letstr.splitlineshandle it. - A
strin Py3 may contain BOMS or stray ' ' — strip withline.rstrip("\r\n")for portability.
🚀 Performance & Best Practices
for line in fuses an internal buffer and is I/O-optimal — much faster thanf.read().splitlines().- For huge files, prefer memory-mapped access via
mmapif you need random access. - Combine reads:
f.read(8192)repeatedly, hand-parse, don't accumulate in Python lists.
🧪 Try It Yourself
- Replace the
setwith a streaming cardinality estimator (hyperloglog). - Add a typedef wrapper that returns Python iterators of structured log records.
- Run a
timeitcomparison againstf.read().splitlines().