reference
5 min read
·
lesson 12 of 16 in Reference
Python File I/O Modes Cheat Sheet
1 · The lesson
readQuick reference for open() modes, text vs binary, and pathlib shortcuts. For the full tutorial see File I/O.
1. Mode Characters
| Char | Meaning |
|---|---|
r | Read (default). File must exist. |
w | Write. Truncates existing file or creates new. |
x | Exclusive create. Fails if file exists. |
a | Append. Writes go to end. Creates if missing. |
+ | Update (read + write). Combine with r/w/a. |
b | Binary mode. Read/write bytes. |
t | Text mode (default). Read/write str. |
2. Combined Modes
| Mode | Read | Write | Truncate | Create | Position | Type |
|---|---|---|---|---|---|---|
r | yes | no | no | no | start | text |
w | no | yes | yes | yes | start | text |
x | no | yes | - | exclusive | start | text |
a | no | yes | no | yes | end | text |
r+ | yes | yes | no | no | start | text |
w+ | yes | yes | yes | yes | start | text |
a+ | yes | yes | no | yes | end | text |
rb | yes | no | no | no | start | bytes |
wb | no | yes | yes | yes | start | bytes |
ab | no | yes | no | yes | end | bytes |
r+b | yes | yes | no | no | start | bytes |
3. open() Signature
python
open(file, mode="r", buffering=-1, encoding=None, # use "utf-8" — see gotcha below errors="strict", # or "replace", "ignore", "backslashreplace" newline=None, # use "" for csv closefd=True)
setup added so this can run · defines file
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) file = _AutoMock('file')
Always use with:
python
with open("notes.txt", "r", encoding="utf-8") as f: text = f.read() # file is closed here, even if read() raised
4. Reading
python
with open("data.txt", encoding="utf-8") as f: f.read() # whole file -> str f.read(1024) # next 1024 chars f.readline() # one line incl '\n' f.readlines() # list[str] of all lines (incl '\n') for line in f: # streaming, no big list process(line.rstrip("\n"))
setup added so this can run · defines process
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) def process(*_a, **_kw): print('-> process() called') return _AutoMock('process()')
5. Writing
python
with open("out.txt", "w", encoding="utf-8") as f: f.write("first line\n") f.writelines(["a\n", "b\n", "c\n"]) # no automatic newlines added
write returns the number of characters written. print(..., file=f) adds newline:
python
with open("out.txt", "w", encoding="utf-8") as f: print("hello", file=f)
6. Seeking
python
with open("data.bin", "rb") as f: head = f.read(4) pos = f.tell() # current byte offset f.seek(0) # back to start f.seek(-8, 2) # 8 bytes before end (whence: 0=start, 1=cur, 2=end)
Note: in text mode, only f.seek(0) and seeks to values returned by f.tell() are guaranteed.
7. pathlib Shortcuts
python
from pathlib import Path p = Path("data") / "notes.txt" p.read_text(encoding="utf-8") # full text p.write_text("hello\n", encoding="utf-8") p.read_bytes() # full bytes p.write_bytes(b"\x00\x01") p.exists(), p.is_file(), p.is_dir() p.stat().st_size p.suffix, p.stem, p.parent, p.name p.mkdir(parents=True, exist_ok=True)
8. Encoding Gotcha (Windows)
The platform default on Windows is not UTF-8. Always pass encoding="utf-8" or you'll get UnicodeDecodeError on any non-ASCII byte:
python
# BAD — uses cp1252 on Windows open("x.txt").read() # GOOD open("x.txt", encoding="utf-8").read()
Set PYTHONUTF8=1 (or use Python 3.15+ where it's default) to make it always UTF-8.
9. CSV Newline Gotcha
When reading or writing CSV files, pass newline="" so the csv module handles line endings:
python
import csv with open("rows.csv", "w", newline="", encoding="utf-8") as f: csv.writer(f).writerow(["a", "b", "c"])
Common Patterns
python
# Read whole file text = Path("x.txt").read_text(encoding="utf-8") # Read lines, stripped, no trailing blank lines = Path("x.txt").read_text(encoding="utf-8").splitlines() # Iterate huge file line by line with open("big.log", encoding="utf-8") as f: for line in f: ... # Read JSON import json data = json.loads(Path("config.json").read_text(encoding="utf-8")) # Write JSON (pretty) Path("out.json").write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8") # Append a log line with open("app.log", "a", encoding="utf-8") as f: f.write(f"{datetime.now().isoformat()} INFO ready\n") # Read binary in chunks with open("blob.bin", "rb") as f: while chunk := f.read(8192): process(chunk) # Atomic-ish write (write to tmp, rename) tmp = p.with_suffix(p.suffix + ".tmp") tmp.write_text(new_text, encoding="utf-8") tmp.replace(p)
setup added so this can run · defines new_text, p, Path, process, chunk, datetime
# Lightweight mock for objects whose attributes/methods aren't critical class _AutoMock: def __init__(self, name='mock'): self._name = name def __getattr__(self, k): return _AutoMock(self._name + '.' + k) def __call__(self, *a, **kw): print('-> ' + self._name + '() called') return _AutoMock(self._name + '()') def __repr__(self): return '<mock ' + self._name + '>' def __str__(self): return '<mock ' + self._name + '>' def __bool__(self): return True def __iter__(self): return iter([]) def __len__(self): return 0 def __getitem__(self, k): return _AutoMock(self._name + '[...]') def __setitem__(self, k, v): pass def __enter__(self): return self def __exit__(self, *a): return False async def __aenter__(self): return self async def __aexit__(self, *a): return False def __add__(self, o): return self def __radd__(self, o): return self def __sub__(self, o): return self def __mul__(self, o): return self def __rmul__(self, o): return self def __truediv__(self, o): return self def __eq__(self, o): return isinstance(o, _AutoMock) def __hash__(self): return hash(self._name) def __lt__(self, o): return True def __le__(self, o): return True def __gt__(self, o): return False def __ge__(self, o): return False def __mro_entries__(self, bases): return (object,) new_text = _AutoMock('new_text') p = _AutoMock('p') def Path(*_a, **_kw): print('-> Path() called') return _AutoMock('Path()') def process(*_a, **_kw): print('-> process() called') return _AutoMock('process()') chunk = _AutoMock('chunk') datetime = _AutoMock('datetime')
Common Errors
| Error / Symptom | Cause | Fix |
|---|---|---|
FileNotFoundError: [Errno 2] ... | File doesn't exist in mode r/r+/a on dir that doesn't exist | Check Path.exists(); create parent with mkdir(parents=True) |
PermissionError: [Errno 13] | Locked, read-only, or another process holds it | Close other handles; check ACLs; don't write to Program Files |
FileExistsError | Mode x and file already exists | Choose w or check first |
UnicodeDecodeError | Read non-ASCII file without encoding="utf-8" | Pass encoding="utf-8" (or the real encoding) |
| Garbled CSV with extra blank rows | Missed newline="" | Open with newline="" when using csv module |
| File silently truncated | Used w instead of r+ or a | Pick the right mode (see table) |
ValueError: I/O operation on closed file | Used f after with block ended | Read inside the block, or pass content out |
| ResourceWarning: unclosed file | Forgot with / close() | Use with open(...) as f: |
See Also
- File I/O (tutorial)
- File I/O basics
- Datetime cheat for log timestamps
- Exceptions cheat
Practice this
on practicepython.inShort exercises that run in your browser and tell you what your code actually did, not just whether a test passed.