1.File modes, binary vs. text files, and encoding
open(path, "r") reads text and decodes bytes to str using an encoding — always pass encoding="utf-8" explicitly. open(path, "rb") reads raw bytes with no decoding at all — no encoding applies.
Key features
- open(path, "r") — the default mode — returns str; open(path, "rb") returns bytes, with no decoding
- Text mode always needs an encoding to decode bytes into str — always pass encoding="utf-8" explicitly rather than relying on the platform default
- A file that is not actually text (an image, a zip, a compiled binary) must be opened "rb" — opening it "r" either raises UnicodeDecodeError or silently corrupts it
- A UnicodeDecodeError means the bytes on disk do not match the encoding you told Python to assume — check the real encoding, do not just retry a different one at random
- Path.read_text(encoding=...) / Path.read_bytes() are pathlib shortcuts for open().read() that also close the file automatically
Common mode strings
- Text mode ("r"/"w")
- Decodes bytes to str on read, encodes str to bytes on write
- Needs an encoding — always pass encoding="utf-8" explicitly
- For files that ARE genuinely text: source code, JSON, CSV, logs
- Binary mode ("rb"/"wb")
- No decoding at all — you get and give raw bytes
- No encoding parameter — bytes have no encoding
- For files that are NOT text: images, zips, compiled binaries
Command
with open(path, "r", encoding="utf-8") as f: # text -- returns str
text = f.read()
with open(path, "rb") as f: # binary -- returns bytes
data = f.read()
from pathlib import Path
Path(path).read_text(encoding="utf-8") # shortcut for the "r" case
Path(path).read_bytes() # shortcut for the "rb" caseThe mode string decides str ("r") vs. bytes ("rb") — text mode always needs an explicit encoding (encoding="utf-8"), binary mode never takes one. A file that is not actually text must open "rb".