раздел 02 · подстраница 1
Как программа читает и пишет файл
Чтение и запись файла - базовый навык, на котором стоят и CSV, и JSON, и логи. Разберём по шагам и отдельно остановимся на том, что чаще всего ломается у новичков: на пути к файлу.
Где лежит файл: абсолютный и относительный путь
Когда вы пишете open("notes.txt"), программа ищет файл в текущей рабочей директории - то есть в той папке, откуда запущена программа. Это не то же самое, что папка, где лежит файл с кодом.
open("notes.txt") # относительный путь: ищет в папке запуска
open("data/notes.txt") # в подпапке data
open("/Users/me/notes.txt") # абсолютный путь: всегда одно и то же место
Надёжный приём - строить путь относительно файла с кодом, а не относительно папки запуска:
from pathlib import Path
# папка, где лежит этот скрипт
base = Path(__file__).parent
notes_path = base / "notes.txt" # рядом со скриптом, где бы его ни запустили
with open(notes_path, "r", encoding="utf-8") as f:
print(f.read())
Четыре операции, которые покрывают всё
Прочитать целиком:
with open("notes.txt", "r", encoding="utf-8") as f:
text = f.read()
Прочитать построчно (удобно для списков):
with open("notes.txt", "r", encoding="utf-8") as f:
lines = [line.strip() for line in f] # strip убирает перенос строки
Перезаписать целиком:
with open("notes.txt", "w", encoding="utf-8") as f:
f.write("новое содержимое\n")
Дописать в конец:
with open("notes.txt", "a", encoding="utf-8") as f:
f.write("ещё одна строка\n")
Почему with
Конструкция with open(...) as f: гарантирует, что файл закроется сам, даже если внутри случится ошибка. Без неё файл может остаться открытым, а изменения - не записаться на диск. Всегда используйте with - это стандарт.
Изменить одну запись в файле
В текстовом файле нельзя «отредактировать строку посередине» на месте. Шаблон такой: прочитали всё в память, изменили, записали обратно.
# заменить задачу "купить молоко" на "купить кефир"
with open("notes.txt", "r", encoding="utf-8") as f:
lines = f.readlines()
lines = [l.replace("купить молоко", "купить кефир") for l in lines]
with open("notes.txt", "w", encoding="utf-8") as f:
f.writelines(lines)
Именно эта неуклюжесть - «прочитай всё, поменяй, запиши всё обратно» - один из сигналов, что задача переросла файл и просится в базу данных, где можно изменить одну запись напрямую.
Тот же принцип в JavaScript
На сервере (Node.js) всё работает похоже:
const fs = require("fs");
fs.writeFileSync("notes.txt", "купить молоко\n"); // перезаписать
fs.appendFileSync("notes.txt", "позвонить маме\n"); // дописать
const text = fs.readFileSync("notes.txt", "utf-8"); // прочитать
console.log(text);
В браузере прямого доступа к файлам на диске нет - это сделано из соображений безопасности. Там для постоянного хранения используют localStorage или отправку данных на сервер.
Антипаттерны
- Путать папку запуска и папку скрипта. Источник 90% ошибок «файл не найден». Стройте путь от
__file__. - Читать огромный файл целиком в память. Для больших файлов читайте построчно, иначе программа упадёт по памяти.
- Не обрабатывать отсутствие файла. Первый запуск - файла ещё нет. Оборачивайте чтение в
try / except FileNotFoundErrorили проверяйте существование заранее.