Файлы и ввод-вывод в Python
Шпаргалка по работе с файлами в Python: open и режимы, with, чтение и запись, кодировки, seek, csv, json, pathlib и временные файлы.
Работа с файлами в Python строится вокруг функции open() и контекстного менеджера with. Эта шпаргалка собирает основные приёмы: режимы открытия, чтение и запись, кодировки, позиционирование, а также модули csv, json, pathlib и tempfile.
Функция open() и режимы
Функция open() возвращает файловый объект. Базовый синтаксис: open(path, mode, encoding=...). Режим определяет, читаем мы файл или пишем, текстовый он или бинарный.
f = open('data.txt', 'r', encoding='utf-8')
content = f.read()
f.close() # обязательно закрыть, если не используем with
print(content)
Перечень режимов открытия файла:
| Режим | Назначение | Если файла нет | Если файл есть |
|---|---|---|---|
| r | чтение (по умолчанию) | ошибка | читаем с начала |
| w | запись | создаётся | очищается |
| a | дозапись в конец | создаётся | пишем в конец |
| x | создание | создаётся | ошибка FileExistsError |
| r+ | чтение и запись | ошибка | не очищается |
| w+ | запись и чтение | создаётся | очищается |
| a+ | дозапись и чтение | создаётся | пишем в конец |
| b | бинарный (добавляется к режиму: rb, wb) | — | работаем с bytes |
| t | текстовый (по умолчанию) | — | работаем с str |
Модификаторы комбинируются: 'rb' — бинарное чтение, 'w+' — запись с возможностью чтения, 'a+b' — бинарная дозапись с чтением.
Оператор with (контекстный менеджер)
Оператор with автоматически закрывает файл, даже если внутри блока возникнет исключение. Это рекомендуемый способ работы с файлами — забыть про close() невозможно.
with open('data.txt', 'r', encoding='utf-8') as f:
content = f.read()
print(content)
# здесь файл уже закрыт автоматически
print(f.closed) # True
Можно открыть несколько файлов в одном with — например, чтобы скопировать содержимое:
with open('src.txt', 'r', encoding='utf-8') as src, \
open('dst.txt', 'w', encoding='utf-8') as dst:
dst.write(src.read())
Чтение файлов
Есть несколько способов прочитать содержимое. read() читает весь файл в строку, readline() — одну строку, readlines() — список всех строк.
with open('poem.txt', 'r', encoding='utf-8') as f:
whole = f.read() # 'Строка 1\nСтрока 2\n'
with open('poem.txt', 'r', encoding='utf-8') as f:
first = f.readline() # 'Строка 1\n'
second = f.readline() # 'Строка 2\n'
with open('poem.txt', 'r', encoding='utf-8') as f:
lines = f.readlines() # ['Строка 1\n', 'Строка 2\n']
Самый экономный по памяти способ — итерация по файлу построчно. Файл читается лениво, строка за строкой:
with open('big.log', 'r', encoding='utf-8') as f:
for line in f:
print(line.rstrip()) # rstrip() убирает '\n' в конце
Можно ограничить число прочитанных символов: f.read(100) вернёт не больше 100 символов.
Запись в файл
Метод write() записывает строку и возвращает число записанных символов. Перевод строки \n нужно добавлять вручную.
with open('out.txt', 'w', encoding='utf-8') as f:
n = f.write('Привет\n') # n == 7
f.write('Мир\n')
# Дозапись в конец без затирания
with open('out.txt', 'a', encoding='utf-8') as f:
f.write('Ещё строка\n')
Метод writelines() пишет список строк подряд — переводы строк он тоже не добавляет сам:
rows = ['яблоко\n', 'груша\n', 'слива\n']
with open('fruits.txt', 'w', encoding='utf-8') as f:
f.writelines(rows)
# Аналог через генератор и join:
with open('fruits.txt', 'w', encoding='utf-8') as f:
f.write('\n'.join(['яблоко', 'груша', 'слива']))
Кодировки
Всегда указывайте encoding явно. По умолчанию Python использует кодировку системы, что на Windows нередко приводит к кракозябрам. Для русского текста стандарт — utf-8.
with open('ru.txt', 'w', encoding='utf-8') as f:
f.write('Съешь же ещё этих мягких французских булок')
with open('ru.txt', 'r', encoding='utf-8') as f:
print(f.read()) # текст без искажений
Параметр errors управляет реакцией на «битые» байты: 'strict' (по умолчанию — исключение), 'ignore' (пропустить), 'replace' (заменить на знак вопроса).
with open('legacy.txt', 'r', encoding='utf-8', errors='replace') as f:
text = f.read() # битые символы заменятся на \ufffd
Бинарные файлы (картинки, архивы) читаются без кодировки — там работаем с bytes:
with open('logo.png', 'rb') as f:
data = f.read()
print(type(data)) # <class 'bytes'>
print(len(data)) # размер в байтах
Позиционирование: seek и tell
Метод tell() возвращает текущую позицию курсора в байтах, а seek(offset, whence) перемещает его. whence: 0 — от начала (по умолчанию), 1 — от текущей позиции, 2 — от конца.
with open('abc.txt', 'w', encoding='utf-8') as f:
f.write('ABCDEFGH')
with open('abc.txt', 'r', encoding='utf-8') as f:
print(f.tell()) # 0
print(f.read(3)) # 'ABC'
print(f.tell()) # 3
f.seek(0) # вернулись в начало
print(f.read(1)) # 'A'
f.seek(5) # на позицию 5
print(f.read()) # 'FGH'
Перемотка от конца (whence=2) в текстовом режиме разрешена только с нулевым смещением; для произвольного отступа от конца откройте файл в бинарном режиме.
with open('abc.txt', 'rb') as f:
f.seek(-2, 2) # 2 байта от конца
print(f.read()) # b'GH'
Работа с CSV
Модуль csv корректно обрабатывает запятые, кавычки и переводы строк внутри ячеек. При открытии файла указывайте newline='', чтобы избежать лишних пустых строк.
import csv
# Запись
with open('users.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerow(['имя', 'возраст'])
writer.writerow(['Аня', 25])
writer.writerow(['Борис', 30])
# Чтение
with open('users.csv', 'r', encoding='utf-8', newline='') as f:
reader = csv.reader(f)
for row in reader:
print(row) # ['имя', 'возраст'], ['Аня', '25'], ...
Удобнее работать со словарями через DictWriter и DictReader — тогда обращение идёт по именам колонок:
import csv
rows = [{'имя': 'Аня', 'возраст': 25}, {'имя': 'Борис', 'возраст': 30}]
with open('users.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.DictWriter(f, fieldnames=['имя', 'возраст'])
writer.writeheader()
writer.writerows(rows)
with open('users.csv', 'r', encoding='utf-8', newline='') as f:
for row in csv.DictReader(f):
print(row['имя'], row['возраст']) # Аня 25 ...
Разделитель меняется параметром delimiter: например, csv.reader(f, delimiter=';') для файлов с точкой с запятой.
Работа с JSON-файлами
Модуль json сериализует словари и списки Python в текст и обратно. dump() пишет в файл, load() читает; парные dumps() и loads() работают со строками.
import json
data = {'имя': 'Аня', 'хобби': ['чтение', 'бег'], 'возраст': 25}
# Запись в файл
with open('user.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
# Чтение из файла
with open('user.json', 'r', encoding='utf-8') as f:
loaded = json.load(f)
print(loaded['хобби']) # ['чтение', 'бег']
Параметр ensure_ascii=False сохраняет кириллицу как есть (а не как \uXXXX), а indent=2 делает файл читаемым с отступами. Без файла — через строку:
import json
s = json.dumps({'x': 1}, ensure_ascii=False) # '{"x": 1}'
obj = json.loads(s) # {'x': 1}
Модуль pathlib
Класс Path из pathlib — современный объектный способ работы с путями. Пути собираются оператором /, а чтение и запись делаются короткими методами без open().
from pathlib import Path
p = Path('data') / 'notes.txt'
# Запись и чтение текста одной строкой
p.parent.mkdir(parents=True, exist_ok=True) # создать папку 'data'
p.write_text('Привет, pathlib!', encoding='utf-8')
print(p.read_text(encoding='utf-8')) # Привет, pathlib!
# Бинарные данные
p.write_bytes(b'\x00\x01\x02')
print(p.read_bytes()) # b'\x00\x01\x02'
Полезные свойства и методы пути:
from pathlib import Path
p = Path('/home/user/report.txt')
print(p.name) # 'report.txt'
print(p.stem) # 'report'
print(p.suffix) # '.txt'
print(p.parent) # PosixPath('/home/user')
print(p.exists()) # True или False
# Перебор файлов в каталоге
for file in Path('.').glob('*.py'):
print(file)
Объект Path можно передавать прямо в open() — он совместим со старым API.
Временные файлы
Модуль tempfile создаёт файлы и каталоги, которые автоматически удаляются после использования. Это удобно для промежуточных данных, тестов и кэша.
import tempfile
# Временный файл: удаляется при выходе из with
with tempfile.NamedTemporaryFile(mode='w+', encoding='utf-8',
suffix='.txt', delete=True) as tmp:
tmp.write('временные данные')
tmp.seek(0)
print(tmp.read()) # 'временные данные'
print(tmp.name) # путь к файлу, напр. /tmp/abc123.txt
# здесь файл уже удалён
Целый временный каталог создаёт TemporaryDirectory — он со всем содержимым удаляется на выходе из блока:
import tempfile
from pathlib import Path
with tempfile.TemporaryDirectory() as d:
file = Path(d) / 'cache.txt'
file.write_text('кэш', encoding='utf-8')
print(file.exists()) # True
# каталог и файл удалены
Функция tempfile.mkstemp() вернёт дескриптор и путь без автоудаления — тогда чистка остаётся на вас.