
Работа с файлами в Python
Файловый ввод-вывод — это основа долговременного хранения данных: программы читают и записывают информацию на диск, ведут логи, обмениваются результатами между этапами обработки. В Python инструменты для работы с файлами просты и выразительны: функция open(), контекстный менеджер with, режимы r/w/a, двоичный и текстовый режимы (b/t), позиционирование через seek()/tell(), а также удобный модуль pathlib для путей. Ниже — практичное руководство с примерами, ошибками и приёмами для больших файлов 🙂
Введение: зачем нужен файловый ввод-вывод
В памяти (RAM) данные живут до завершения процесса, а файлы обеспечивают постоянство: настройки, результаты расчётов, отчёты, кэш, журналирование. Грамотная работа с файлами в Python — это не только читать/писать, но и выбирать режим, корректно закрывать ресурс, контролировать кодировку и позицию указателя, работать с большими объёмами без лишнего расхода памяти.
Текстовые и бинарные файлы: различия и выбор
Текстовый режим (режим по умолчанию 't') читает/пишет строки и выполняет преобразование кодировок (обычно UTF-8), нормализует переводы строк. Бинарный режим ('b') работает с байтами как есть — полезно для изображений, архивов, сетевых дампов.
- Текст: encoding="utf-8", тип данных — str, автоматическая трансляция \n.
- Бинарный: без кодировок, тип данных — bytes/bytearray, чтение/запись блоками.
# текстовый режим
with open("notes.txt", "w", encoding="utf-8") as f:
f.write("Привет, мир!\\n")
# бинарный режим
with open("logo.png", "rb") as f:
chunk = f.read(4096)
Инструменты Python: open(), файловые объекты, pathlib
Базовая точка входа — функция open(file, mode, encoding, newline), возвращающая файловый объект с методами read(), write(), seek(), tell(), flush(). Для кроссплатформенных путей используйте pathlib.Path: его методы работают одинаково в Windows, Linux и macOS.
from pathlib import Path
data_dir = Path("data")
data_dir.mkdir(exist_ok=True)
path = data_dir / "report.txt"
with path.open("w", encoding="utf-8") as f:
f.write("OK\\n")
Открытие файла: режимы r/w/a/x, комбинированные режимы и пути
Режимы: 'r' — читать (ошибка, если файла нет), 'w' — создать/обрезать и писать, 'a' — дописывать в конец, 'x' — создать эксклюзивно (ошибка, если существует). Комбинации с '+' дают чтение+запись ('r+', 'w+', 'a+'). Добавляйте 'b' для бинарного и 't' для текстового режима (t — по умолчанию). В текстовом режиме задавайте encoding="utf-8".
- Windows-путь: используйте прямые слэши С:/path/file.txt или «сырые» строки r"C:\path\file.txt", либо pathlib.
- Linux/macOS: /home/user/file.txt, относительные пути — от текущего каталога.
# r / w / a / x и сочетания
open("in.txt", "r", encoding="utf-8") # чтение
open("out.txt", "w", encoding="utf-8") # запись (truncate)
open("log.txt", "a", encoding="utf-8") # добавить в конец
open("only_once.lock", "x") # создать, если нет
open("edit.txt", "r+", encoding="utf-8") # читать и писать
open("data.bin", "wb") # бинарная запись
Контекстный менеджер with: безопасное закрытие файла
with гарантирует закрытие ресурса даже при исключениях — это проще и надёжнее, чем вручную вызывать close(). Внутри with можно писать/читать сколько угодно; выход из блока закрывает файл автоматически.
# «with» = автозакрытие и меньше утечек
with open("notes.txt", "w", encoding="utf-8") as f:
f.write("Первая строка\\n")
f.write("Вторая строка\\n")
Чтение: read(), readline(), итерация по файлу, большие объёмы
Выбирайте стратегию под размер данных. read() без аргументов читает всё (для небольших файлов). Для крупных лучше использовать read(size) пакетами или итерироваться по строкам.
- read(size) — чтение блоками для экономии памяти.
- readline() — построчно, контролируя размер.
- for line in f: идиоматичный обход строк.
# безопасное чтение больших файлов — блоками
def read_chunks(path, size=1024*1024):
with open(path, "rb") as f:
while True:
chunk = f.read(size)
if not chunk: break
yield chunk
# построчное чтение
with open("poem.txt", "r", encoding="utf-8") as f:
for i, line in enumerate(f, 1):
print(i, line.rstrip())
Запись: write(), writelines(), добавление и буферизация
write() принимает str (в текстовом режиме) или bytes (в бинарном). writelines() пишет последовательность строк «как есть» — добавьте \n вручную. В режиме 'a'/'a+' запись идёт в конец файла; в 'w' — файл обрезается.
# запись строк и добавление в конец
with open("log.txt", "a", encoding="utf-8") as f:
f.write("start\\n")
f.writelines([f"item={i}\\n" for i in range(3)])
f.flush() # при необходимости форсировать запись
Позиционирование: seek() и tell(), работа в середине файла
tell() возвращает текущую позицию указателя, seek(offset, whence) перемещает её: whence=0 — от начала, 1 — от текущей позиции, 2 — от конца. В текстовом режиме позиция измеряется в абстрактных единицах, в бинарном — в байтах; для точного позиционирования внутри контента используйте бинарный режим.
# вставка «поверх» (read-modify-write)
with open("data.txt", "r+", encoding="utf-8") as f:
head = f.read(10) # читаем кусок
rest = f.read() # остальное
f.seek(0)
f.write(head + "[INS]" + rest)
Редактирование существующего файла: шаблоны обновления
Файлы — потоковые сущности, произвольная «вставка посередине» не бесплатна. Классический шаблон: прочитать, изменить в памяти, перезаписать. Для очень больших файлов применяют временные файлы и потоковую переработку.
# вставка строки после N-й позиции
from pathlib import Path
p = Path("todo.txt")
lines = p.read_text(encoding="utf-8").splitlines()
lines.insert(1, "- [x] добавить примеры")
p.write_text("\\n".join(lines) + "\\n", encoding="utf-8")
Пути и файловая система: абсолютные/относительные, создание папок, проверка наличия
pathlib упрощает жизнь: склейка путей оператором /, кроссплатформенность, удобные методы. Пользуйтесь exists(), is_file(), mkdir(), rename(), unlink().
from pathlib import Path
base = Path("var") / "data"
base.mkdir(parents=True, exist_ok=True)
target = base / "metrics.csv"
if not target.exists():
target.write_text("ts,value\\n", encoding="utf-8")
Лучшие практики и частые ошибки
- Всегда используйте with — закрытие ресурса гарантировано.
- Указывайте encoding="utf-8" для текста: меньше сюрпризов между ОС.
- Стримьте большие файлы: read(size) и построчный обход вместо read() «всё сразу».
- Режим выбирайте осознанно: 'w' обрезает файл, 'a' дописывает, 'x' защитит от перезаписи.
- В бинарном режиме позиционирование точнее; в текстовом — осторожнее с seek() в середине многобайтовых символов.
- Пути формируйте pathlib — меньше экранирования и «сломанных» слэшей.
Мини-практикум: 6 задач на чтение/запись/позиционирование
1) Копирование файла блоками (без переполнения памяти)
def copy_file(src, dst, buf=1024*1024):
with open(src, "rb") as f_in, open(dst, "wb") as f_out:
while chunk := f_in.read(buf):
f_out.write(chunk)
2) Построчная фильтрация (только строки с «ERROR»)
def grep_errors(src, dst):
with open(src, "r", encoding="utf-8") as fin, open(dst, "w", encoding="utf-8") as fout:
for line in fin:
if "ERROR" in line: fout.write(line)
3) Безопасное логирование (append + автосоздание каталога)
from pathlib import Path
def write_log(dirpath, msg):
Path(dirpath).mkdir(parents=True, exist_ok=True)
with open(Path(dirpath)/"app.log", "a", encoding="utf-8") as f:
f.write(msg + "\\n")
4) Вставка заголовка в начало файла
def insert_header(path, header):
with open(path, "r+", encoding="utf-8") as f:
body = f.read()
f.seek(0); f.write(header + "\\n" + body)
5) Подсчёт размера файла и числа строк
from pathlib import Path
def stats(path):
p = Path(path)
size = p.stat().st_size
n = sum(1 for _ in p.open("r", encoding="utf-8"))
return size, n
6) Бинарная запись чисел (bytes)
def write_bytes(path, nums):
with open(path, "wb") as f:
for n in nums: f.write(n.to_bytes(4, "little", signed=True))
FAQ
Короткие ответы на частые вопросы — раскройте нужный спойлер 👇
r — чтение (ошибка, если нет файла), w — запись с обрезкой, a — дописывать, x — создать «если нет». С +: чтение+запись. Добавляйте b для бинарного и t (по умолчанию) для текстового режима.
Потому что \ — символ экранирования. Используйте «сырые» строки (r"C:\path\file.txt"), прямые слэши ("C:/path/file.txt") или pathlib.
В текстовом режиме — да, надёжнее. Рекомендуйте UTF-8, чтобы избежать сюрпризов между системами и редакторами.
Для небольших файлов read() быстрее и проще. Для больших — итерация по строкам или read(size) блоками: так вы экономите память.
Напрямую — нет: файлы потоковые. Используйте шаблон «прочитать → изменить в памяти → перезаписать» или временный файл и потоковую переработку.
pathlib даёт объектно-ориентированный, кроссплатформенный API, удобную склейку путей оператором / и прямой доступ к файловым операциям (open, read_text, write_text).
Заключение
Вы познакомились с ключевыми приёмами работы с файлами в Python: выбор текстового/бинарного режима, осознанные r/w/a/x и комбинации с +, безопасное открытие через with, чтение блоками и построчно, запись строк и байтов, позиционирование seek()/tell(), редактирование существующих файлов и управление путями через pathlib. Придерживайтесь лучших практик, тестируйте на маленьких сэмплах перед обработкой гигабайт и автоматизируйте рутинные сценарии — ваш файловый код станет чище, быстрее и надёжнее.