The file.read([size]) method reads data from an open file stream up to size characters (in text mode) or bytes (in binary mode), returning the contents as a str or bytes object.
Modern File Idiom: Always open files using the with open(...) as f: context manager. This guarantees automatic resource cleanup and file descriptor closure even if runtime exceptions or unexpected interruptions occur.
In Python, the built-in open() function returns a file stream object that provides the read() method:
file_object.read(size=-1)
| Parameter / Property | Type | Default | Behavior & Description |
|---|---|---|---|
size |
int |
-1 |
Number of characters (in text mode) or bytes (in binary mode) to read. If negative, omitted, or None, the entire remaining contents of the file are read until EOF (End-of-File). |
| Return Value (Text) | str |
— | String decoded from file contents according to the specified encoding. Returns empty string "" at EOF. |
| Return Value (Binary) | bytes |
— | Raw bytes read from the stream. Returns empty byte string b"" at EOF. |
| Mode | Description | Pointer Start | Behavior if File Missing |
|---|---|---|---|
'r' |
Read-only (Text mode, default) | Beginning of file (offset 0) | Raises FileNotFoundError |
'rb' |
Read-only (Binary mode) | Beginning of file (offset 0) | Raises FileNotFoundError |
'r+' |
Read and Write (Text mode) | Beginning of file (offset 0) | Raises FileNotFoundError |
'w' |
Write-only (Truncates file) | Beginning of file (offset 0) | Creates new file |
'a' |
Append (Writes at end) | End of file | Creates new file |
Modern Python engineering requires managing file resources using the with statement. The context manager protocol guarantees that the file descriptor is closed immediately upon exiting the block, preventing resource leaks and locked file handles:
# Recommended: Context manager automatically closes file handle
with open("data.txt", "r", encoding="utf-8") as fob:
content = fob.read()
print("File length in characters:", len(content))
print(content)
# Verified: fob.closed is now True
print("Is file stream closed?", fob.closed) # True
When you provide a positive integer to read(size), Python reads at most that number of units from the current stream position, advancing the internal file cursor:
with open("sample.txt", "r", encoding="utf-8") as fob:
header = fob.read(10) # Read first 10 characters
body = fob.read(20) # Read next 20 characters
print("Header slice:", header)
print("Body slice: ", body)
print("Current cursor offset:", fob.tell()) # 30
Reading an entire multi-gigabyte file with an unconstrained f.read() exhausts system RAM and triggers MemoryError. Instead, process data in fixed-size buffers (e.g., 64 KB chunks):
def count_file_characters(filepath: str, chunk_size: int = 65536) -> int:
total_characters = 0
with open(filepath, "r", encoding="utf-8") as stream:
while chunk := stream.read(chunk_size):
total_characters += len(chunk)
return total_characters
total = count_file_characters("server.log")
print(f"Processed {total:,} characters with minimal memory footprint.")
When working with images, compressed archives, databases, or compiled binaries, always specify binary mode ('rb'). Binary mode avoids automatic newline translations and reads exact raw bytes:
def check_png_header(image_path: str) -> bool:
# The standard PNG magic signature is 8 bytes
png_magic = b"PNG
"
with open(image_path, "rb") as fob:
header = fob.read(8)
return header == png_magic
print("Is valid PNG:", check_png_header("logo.png"))
Python provides four distinct mechanisms for reading file contents. Choose based on file size and line boundaries:
| Technique | Return Type | Memory Usage | Best Suited For |
|---|---|---|---|
f.read() |
str or bytes |
Loads entire file | Small to medium configuration files, complete JSON payloads. |
f.read(chunk_size) |
str or bytes |
Constant (O(1) buffer) | Streaming large media files, cryptographic hashing, binary parsing. |
f.readline() |
str (single line) |
Memory of single line | Interactive terminal input, inspecting file header lines. |
for line in f: |
Iterator (yielding str) |
Minimal (Buffered iterator) | Best practice for line-by-line processing of large logs and CSVs. |
f.readlines() |
list[str] |
Loads all lines in memory | Small scripts needing random indexed access to all lines. |
Default operating system encodings vary across platforms (Windows often defaults to cp1252, while Linux and macOS use utf-8). Always specify encoding="utf-8" explicitly to avoid cross-platform decoding bugs:
# Explicit UTF-8 decoding with robust error recovery
with open("international.txt", "r", encoding="utf-8", errors="replace") as fob:
text_data = fob.read()
print("Decoded cleanly with replacement characters for corrupted bytes.")
After calling read(), the stream cursor sits at the end of the file. A subsequent read() immediately returns "". To read again, reset the cursor to the beginning using f.seek(0).
with open("notes.txt", "r", encoding="utf-8") as f:
first_read = f.read()
second_read = f.read() # Empty string "" because cursor is at EOF
f.seek(0) # Rewind cursor back to beginning
rewound_read = f.read() # Successfully reads content again
Attempting to open a non-existent file in read mode immediately raises FileNotFoundError. Wrap file openings in try-except FileNotFoundError or verify existence with os.path.exists().
Execute this complete test harness demonstrating file creation, full reads, partial reads, and buffered chunk processing:
import tempfile
import os
class FileReaderSuite:
@staticmethod
def read_full_text(path: str) -> str:
with open(path, "r", encoding="utf-8") as stream:
return stream.read()
@staticmethod
def read_in_chunks(path: str, chunk_size: int = 16) -> list[str]:
chunks = []
with open(path, "r", encoding="utf-8") as stream:
while chunk := stream.read(chunk_size):
chunks.append(chunk)
return chunks
# Verification test harness
test_content = "Line 1: Plus2Net Python
Line 2: Stream Handling
Line 3: File I/O Suite
"
with tempfile.NamedTemporaryFile("w+", delete=False, encoding="utf-8") as tmp:
tmp.write(test_content)
tmp_path = tmp.name
try:
suite = FileReaderSuite()
# Assert full read equivalence
assert suite.read_full_text(tmp_path) == test_content
# Assert chunked read reassembly
reassembled = "".join(suite.read_in_chunks(tmp_path, 8))
assert reassembled == test_content
# Assert partial character read
with open(tmp_path, "r", encoding="utf-8") as f:
assert f.read(6) == "Line 1"
print("All file.read() unit assertions passed successfully!")
finally:
if os.path.exists(tmp_path):
os.remove(tmp_path)
Open in Google Colab View on GitHub
Run all file reading exercises interactively, test context managers, benchmark chunked reading against full memory reads, and inspect binary magic headers in Google Colab.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.