ШПАРГАЛКА

Файлы и ввод-вывод в 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() вернёт дескриптор и путь без автоудаления — тогда чистка остаётся на вас.

Поддержать проект