Python File I/O Modes Cheat Sheet
1 · The lesson
readThis page collects the everyday ways to read and write files in Python: open() and its modes, the pathlib module for file names and folders, and the csv and json modules. Every example runs in your browser, so each file is created before it is read. For the full tutorial, see File I/O and File I/O basics.
Two rules first. Always open text files with encoding="utf-8" (an encoding is the rule that turns letters into bytes). And use with open(...): it closes the file even if your code fails.
1. Read and write in one line
A Path object is the name of a file or folder. It is only a name until you read or write it. For small files, read_text() and write_text() do the whole job in one line.
from pathlib import Path p = Path("a.txt") print(p.write_text("a\nb\n", encoding="utf-8")) # 4 print(repr(p.read_text(encoding="utf-8"))) # 'a\nb\n' text = p.read_text(encoding="utf-8") print(text.splitlines()) # ['a', 'b']
write_text() creates the file, or replaces it if it exists, and returns how many characters it wrote. splitlines() gives a list of lines without the \n at the end of each one.
Binary data (images, zip files) is bytes, not text, so it takes no encoding:
print(Path("b.bin").write_bytes(b"\x00\xff")) # 2 print(Path("b.bin").read_bytes()) # b'\x00\xff'
setup added so this can run · re-runs 1 earlier block on this page first
# 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 Path(*_a, **_kw): print('-> Path() called') return _AutoMock('Path()') # Earlier blocks on this page that this one builds on, re-run quietly first. try: from pathlib import Path except Exception: pass _pm_earlier = [ 'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8")) # 4\nprint(repr(p.read_text(encoding="utf-8"))) # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines()) # [\'a\', \'b\']', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
2. open() and with
open() gives you a file object. The with block closes the file when the block ends, even after an error.
with open("a.txt", encoding="utf-8") as f: text = f.read() # the whole file as one string print(repr(text)) with open("a.txt", encoding="utf-8") as f: for line in f: # one line at a time, so huge files are fine print(line.rstrip("\n"))
'a\nb\n'
a
b setup added so this can run · defines a, b
# 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,) a = _AutoMock('a') b = _AutoMock('b')
Other ways to read from an open file:
| Call | What it gives you |
|---|---|
f.read() | The rest of the file as one string |
f.read(1024) | The next 1024 characters (bytes in binary mode) |
f.readline() | The next line, with its \n |
f.readlines() | A list of all remaining lines, each with its \n |
To add to a file, open it with mode "a". write() adds no newline for you, but print(..., file=f) does.
with open("log.txt", "a", encoding="utf-8") as f: f.write("start\n") f.writelines(["Ada logged in\n", "Grace logged in\n"]) # no newlines added print("ready", file=f) print(Path("log.txt").read_text(encoding="utf-8").splitlines()) # ['start', 'Ada logged in', 'Grace logged in', 'ready']
setup added so this can run · re-runs 1 earlier block on this page first
# 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 Path(*_a, **_kw): print('-> Path() called') return _AutoMock('Path()') # Earlier blocks on this page that this one builds on, re-run quietly first. try: from pathlib import Path except Exception: pass _pm_earlier = [ 'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8")) # 4\nprint(repr(p.read_text(encoding="utf-8"))) # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines()) # [\'a\', \'b\']', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
The full list of options for open() (a summary, not code to run):
open(file,
mode="r",
buffering=-1,
encoding=None, # pass "utf-8"; the default depends on the computer
errors="strict", # "replace" puts a marker in place of bad bytes instead of failing
newline=None, # use "" for csv files
closefd=True)3. Modes
The mode is the second argument to open(). It says whether you read or write, and what happens to a file that already exists.
| Mode | Read | Write | Empties the file first | Creates the file | Starts at |
|---|---|---|---|---|---|
"r" (default) | yes | no | no | no, it must exist | start |
"w" | no | yes | yes | yes | start |
"x" | no | yes | no | yes, FileExistsError if it exists | start |
"a" | no | yes | no | yes | end |
"r+" | yes | yes | no | no | start |
"w+" | yes | yes | yes | yes | start |
"a+" | yes | yes | no | yes | end |
Add b for binary mode ("rb", "wb", "ab", "r+b"). In binary mode you read and write bytes, and you do not pass encoding=.
4. Useful helpers
from pathlib import Path # A file next to the script, wherever you run the script from data_file = Path(__file__).parent / "data.txt"
import os from tempfile import TemporaryDirectory # A scratch folder that is deleted when the block ends with TemporaryDirectory() as tmp: print(Path(tmp).exists()) # True # Older code joins paths with os.path.join; Path / does the same job print(os.path.join("data", "a.txt") == str(Path("data") / "a.txt")) # True
setup added so this can run · re-runs 1 earlier block on this page first
# 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 Path(*_a, **_kw): print('-> Path() called') return _AutoMock('Path()') # Earlier blocks on this page that this one builds on, re-run quietly first. try: from pathlib import Path except Exception: pass _pm_earlier = [ 'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8")) # 4\nprint(repr(p.read_text(encoding="utf-8"))) # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines()) # [\'a\', \'b\']', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
5. Path parts
A Path splits a file name into useful parts. The / operator joins parts with the right separator for the operating system (\ on Windows, / elsewhere). as_posix() shows a path with / so the output is the same everywhere.
p = Path("data/sales/report.csv") print(p.name) # report.csv print(p.stem) # report print(p.suffix) # .csv print(p.parent.as_posix()) # data/sales print(p.parts) # ('data', 'sales', 'report.csv') print(p.with_suffix(".json").name) # report.json print((Path("data") / "a.txt").as_posix()) # data/a.txt print(Path("a.tar.gz").suffixes) # ['.tar', '.gz']
setup added so this can run · re-runs 1 earlier block on this page first
# 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 Path(*_a, **_kw): print('-> Path() called') return _AutoMock('Path()') # Earlier blocks on this page that this one builds on, re-run quietly first. try: from pathlib import Path except Exception: pass _pm_earlier = [ 'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8")) # 4\nprint(repr(p.read_text(encoding="utf-8"))) # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines()) # [\'a\', \'b\']', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
stem is the name without its suffix, and parent is the folder the file is in. Path.cwd() is the folder you are working in, Path.home() is your home folder, and p.resolve() gives the full absolute path.
6. Check, list and tidy
import shutil d = Path("data") (d / "sub").mkdir(parents=True, exist_ok=True) # parents=True also makes missing parent folders for n in ["a.csv", "b.csv", "sub/x.md"]: (d / n).write_text("x", encoding="utf-8") print(d.is_dir()) # True print((d / "a.csv").is_file()) # True print((d / "zz.csv").exists()) # False print((d / "a.csv").stat().st_size) # 1 print(sorted(q.name for q in d.glob("*.csv"))) # ['a.csv', 'b.csv'] print(sorted(q.name for q in d.iterdir())) # ['a.csv', 'b.csv', 'sub'] print([q.as_posix() for q in d.rglob("*.md")]) # ['data/sub/x.md']
setup added so this can run · re-runs 1 earlier block on this page first
# 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 Path(*_a, **_kw): print('-> Path() called') return _AutoMock('Path()') # Earlier blocks on this page that this one builds on, re-run quietly first. try: from pathlib import Path except Exception: pass _pm_earlier = [ 'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8")) # 4\nprint(repr(p.read_text(encoding="utf-8"))) # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines()) # [\'a\', \'b\']', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
stat().st_size is the size in bytes. glob() finds names that match a pattern in one folder. rglob() also searches every subfolder. The order of the results is not fixed, so sort them when the order matters. iterdir() lists the files and folders inside.
To rename, copy and delete:
Path("out").mkdir(exist_ok=True) # exist_ok=True: no error if the folder already exists (d / "b.csv").rename(d / "c.csv") # rename or move shutil.copy(d / "a.csv", "out") # copy a file into the folder "out" (d / "c.csv").unlink(missing_ok=True) # delete a file shutil.rmtree("out") # delete a folder and everything in it print(sorted(q.name for q in d.iterdir())) # ['a.csv', 'sub']
setup added so this can run · re-runs 2 earlier blocks on this page first
# 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,) d = _AutoMock('d') shutil = _AutoMock('shutil') def Path(*_a, **_kw): print('-> Path() called') return _AutoMock('Path()') # Earlier blocks on this page that this one builds on, re-run quietly first. try: from pathlib import Path import shutil except Exception: pass _pm_earlier = [ 'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8")) # 4\nprint(repr(p.read_text(encoding="utf-8"))) # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines()) # [\'a\', \'b\']', 'import shutil\n\nd = Path("data")\n(d / "sub").mkdir(parents=True, exist_ok=True) # parents=True also makes missing parent folders\nfor n in ["a.csv", "b.csv", "sub/x.md"]:\n (d / n).write_text("x", encoding="utf-8")\n\nprint(d.is_dir()) # True\nprint((d / "a.csv").is_file()) # True\nprint((d / "zz.csv").exists()) # False\nprint((d / "a.csv").stat().st_size) # 1\nprint(sorted(q.name for q in d.glob("*.csv"))) # [\'a.csv\', \'b.csv\']\nprint(sorted(q.name for q in d.iterdir())) # [\'a.csv\', \'b.csv\', \'sub\']\nprint([q.as_posix() for q in d.rglob("*.md")]) # [\'data/sub/x.md\']', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
7. CSV
A CSV file is a table saved as text, one row per line. Open CSV files with newline="": the csv module handles line endings itself, and without newline="" you get extra blank rows on Windows.
import csv rows = [["name", "age"], ["Ada", 36]] with open("people.csv", "w", newline="", encoding="utf-8") as f: csv.writer(f).writerows(rows) with open("people.csv", newline="", encoding="utf-8") as f: back = list(csv.DictReader(f)) print(back) # [{'name': 'Ada', 'age': '36'}]
DictReader uses the header row as the keys. Every value comes back as a string, so 36 is now '36'. csv.reader(f) gives plain lists instead, with the header row included.
8. JSON
JSON is a text format for nested data: dicts, lists, strings, numbers, True/False and None. dumps() and loads() work with strings. dump() and load() work with open files.
import json cfg = {"name": "Ada", "tags": ["x"]} p = Path("cfg.json") p.write_text(json.dumps(cfg, indent=2), encoding="utf-8") # indent=2 puts each item on its own line print(json.loads(p.read_text(encoding="utf-8"))) # {'name': 'Ada', 'tags': ['x']} with open(p, encoding="utf-8") as f: cfg = json.load(f) print(cfg["name"]) # Ada print(json.dumps({"a": None, "b": True})) # {"a": null, "b": true} print(json.dumps("é")) # "\u00e9" print(json.dumps("é", ensure_ascii=False)) # "é"
setup added so this can run · re-runs 1 earlier block on this page first
# 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 Path(*_a, **_kw): print('-> Path() called') return _AutoMock('Path()') # Earlier blocks on this page that this one builds on, re-run quietly first. try: from pathlib import Path except Exception: pass _pm_earlier = [ 'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8")) # 4\nprint(repr(p.read_text(encoding="utf-8"))) # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines()) # [\'a\', \'b\']', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
ensure_ascii=False keeps accented letters as they are, so the file stays readable.
9. Big binary files
Read a big file in pieces so it never has to fit in memory at once. f.tell() gives the current position in bytes and f.seek() moves to a position.
with open("b.bin", "rb") as f: while chunk := f.read(65536): # 64 KB at a time print(len(chunk)) # 2 print(f.tell()) # 2 f.seek(0) # back to the start print(f.read(1)) # b'\x00'
setup added so this can run · defines chunk
# 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,) chunk = _AutoMock('chunk')
f.seek(-1, 2) means "1 byte before the end" (the second argument is 0 for the start, 1 for the current position, 2 for the end). In text mode, only f.seek(0) and positions you got from f.tell() are safe.
10. Errors you will meet
for action in [lambda: open("nope.txt"), lambda: open("a.txt", "x"), lambda: Path("data").mkdir(), lambda: Path("data").rmdir()]: try: action() except OSError as e: print(type(e).__name__)
setup added so this can run · re-runs 1 earlier block on this page first
# 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 Path(*_a, **_kw): print('-> Path() called') return _AutoMock('Path()') # Earlier blocks on this page that this one builds on, re-run quietly first. try: from pathlib import Path except Exception: pass _pm_earlier = [ 'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8")) # 4\nprint(repr(p.read_text(encoding="utf-8"))) # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines()) # [\'a\', \'b\']', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
FileNotFoundError FileExistsError FileExistsError OSError
| Error | Usual cause | Fix |
|---|---|---|
FileNotFoundError | Wrong name, or Python is running in another folder | Check the name and Path.cwd(). For writing, create the folder with mkdir(parents=True) |
FileExistsError | Mode "x", or mkdir() on a folder that exists | Use "w", or pass exist_ok=True |
OSError from rmdir() | The folder is not empty | shutil.rmtree() deletes a folder and its contents |
PermissionError | The file is open in another program, or you have no right to write there | Close the other program, or write to your own folder |
UnicodeDecodeError | The file was read without the right encoding | Pass encoding="utf-8" (or the file's real encoding) |
Common mistakes
No encoding. open("data.txt") uses the computer's default encoding, which on Windows is often not UTF-8. Accented letters then come out wrong or raise UnicodeDecodeError. Fix: open("data.txt", encoding="utf-8").
Mode "w" when you meant to add. open("notes.txt", "w") empties the file the moment it opens. Fix: use "a" to add to the end.
Backslashes in Windows paths. In "C:\new\table.csv" the \n becomes a newline and \t becomes a tab. Fix: write r"C:\new\table.csv" or use forward slashes, "C:/new/table.csv".
Reading after the with block. The file is closed when the block ends, so f.read() after it raises ValueError: I/O operation on closed file. Fix: read inside the block and keep the text in a variable.
Reading twice. The second f.read() returns "" because you are already at the end of the file:
with open("a.txt", encoding="utf-8") as f: print(repr(f.read())) # 'a\nb\n' print(repr(f.read())) # '' f.seek(0) # fix: go back to the start print(repr(f.read())) # 'a\nb\n'
Relative paths. open("data.txt") looks in the folder where you ran Python, not the folder that holds the script. Fix: Path(__file__).parent / "data.txt".
Comparing a line with its newline. A line read from a file ends in "\n", so line == "yes" is never true:
Path("answer.txt").write_text("yes\n", encoding="utf-8") with open("answer.txt", encoding="utf-8") as f: line = f.readline() print(line == "yes") # False print(line.strip() == "yes") # True
setup added so this can run · re-runs 1 earlier block on this page first
# 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 Path(*_a, **_kw): print('-> Path() called') return _AutoMock('Path()') # Earlier blocks on this page that this one builds on, re-run quietly first. try: from pathlib import Path except Exception: pass _pm_earlier = [ 'from pathlib import Path\n\np = Path("a.txt")\nprint(p.write_text("a\\nb\\n", encoding="utf-8")) # 4\nprint(repr(p.read_text(encoding="utf-8"))) # \'a\\nb\\n\'\n\ntext = p.read_text(encoding="utf-8")\nprint(text.splitlines()) # [\'a\', \'b\']', ] def _pm_run_earlier(_srcs, _g): import sys as _s, io as _io, warnings as _w for _src in _srcs: _o, _e = _s.stdout, _s.stderr _s.stdout = _s.stderr = _io.StringIO() try: with _w.catch_warnings(): _w.simplefilter('ignore') exec(compile(_src, '<earlier block>', 'exec'), _g) except (Exception, SystemExit): pass finally: _s.stdout, _s.stderr = _o, _e if 'matplotlib.pyplot' in _s.modules: _s.modules['matplotlib.pyplot'].close('all') # their plots belong to their own blocks _pm_run_earlier(_pm_earlier, globals()) del _pm_earlier, _pm_run_earlier
See also
- File I/O (tutorial)
- File I/O basics
- Datetime cheat sheet for timestamps in log files
- Exceptions cheat sheet
Practice this
on practicepython.inShort exercises that run in your browser and tell you what your code did, as well as whether a test passed.
Found this useful? Share it with a friend who’s learning Python.