Как работать с файлами в Python: режимы, методы, обработка исключений
Чтение и запись файлов в Python: базовые методы и варианты
Работа с файлами в Python строится вокруг функции open. Она возвращает файловый объект, через который выполняется чтение или запись. Наиболее надежный подход использует контекстный менеджер with, который автоматически закрывает файл даже при исключении.
Основной подход: with open и текстовые режимы
Цель: безопасное чтение и запись текстовых данных. Контекстный менеджер гарантирует закрытие ресурса. Режимы: 'r' чтение, 'w' запись с очисткой, 'a' добавление, 'x' создание, 'b' бинарный, 't' текстовый, '+' обновление.
with open('example.txt', 'w', encoding='utf-8') as f:
print('Первая строка', file=f)
print('Вторая строка', file=f)
with open('example.txt', 'r', encoding='utf-8') as f:
data = f.read()
print(data)чтение запись файлов python (чтение и запись файлов в python)
Первая строка Вторая строка
Пояснение: функция open принимает путь, режим и кодировку. Метод write возвращает число записанных символов. Метод read читает весь файл. Контекстный менеджер закрывает файл после выхода из блока.
Как открыть файл вручную, если нужна совместимость со старым кодом?
Вариант с try/finally применяется в коде, где контекстный менеджер недоступен или нужна ручная обработка закрытия. Такой подход требует явного вызова close.
f = open('manual.txt', 'w', encoding='utf-8')
try:
print('Данные', file=f)
finally:
f.close()
Пояснение: блок finally гарантирует закрытие даже при ошибке записи.
Как прочитать файл построчно без загрузки всего содержимого?
Итерация по файловому объекту читает строки по одной. Этот способ подходит для больших файлов и экономит память.
with open('big.txt', 'r', encoding='utf-8') as f:
for line in f:
print(line.rstrip())
Как записать несколько строк без множественных вызовов write?
Метод writelines принимает итерируемый объект со строками. Он не добавляет разделители автоматически.
lines = ['Альфа', 'Бета', 'Гамма']
with open('lines.txt', 'w', encoding='utf-8') as f:
for line in lines:
print(line, file=f)
Пояснение: каждый вызов print добавляет символ новой строки, если не указан параметр end.
Как добавить данные в конец существующего файла?
Режим 'a' открывает файл для добавления. Если файл отсутствует, он создается.
with open('log.txt', 'a', encoding='utf-8') as f:
print('Новая запись', file=f)
Как работать с бинарными файлами и изображениями?
Бинарный режим 'rb' и 'wb' возвращает и принимает bytes. Кодировка в этом режиме не применяется.
with open('image.png', 'rb') as src:
data = src.read()
with open('copy.png', 'wb') as dst:
dst.write(data)
Пояснение: для текстовых данных используются str, для бинарных bytes. Смешивание типов вызывает TypeError.
Как указать кодировку и обработать ошибки декодирования?
Параметр encoding задает кодировку. Параметр errors управляет реакцией на некорректные байты.
with open('text.txt', 'r', encoding='utf-8', errors='replace') as f:
content = f.read()
print(content)
Как использовать pathlib для чтения и записи?
Path.read_text и Path.write_text делают код короче и переносимым.
from pathlib import Path
p = Path('pathlib.txt')
p.write_text('Текст через pathlib', encoding='utf-8')
content = p.read_text(encoding='utf-8')
print(content)
Пояснение: pathlib автоматически управляет открытием и закрытием файла.
Частые ошибки при работе с файлами
FileNotFoundError: файл не найден. Решение: проверить путь, создать файл или обработать исключение. PermissionError: нет прав доступа. Решение: изменить права или выбрать другой путь. IsADirectoryError: путь указывает на папку. Решение: указать файл. TypeError: запись str в бинарный файл или bytes в текстовый. Решение: согласовать режим и тип данных. Потеря данных при 'w' вместо 'a'. Решение: выбирать режим по задаче.
Расширенные примеры чтения и записи файлов в Python
Примеры показывают разные сценарии: обработка больших файлов, подсчет строк, фильтрация, временные файлы, CSV, JSON, сжатие, произвольный доступ, буферизация, кодировки, атомарная запись.
Пример 1: чтение большими блоками
def read_in_chunks(path, size=4):
with open(path, 'r', encoding='utf-8') as f:
while True:
chunk = f.read(size)
if not chunk:
break
yield chunk
with open('chunks.txt', 'w', encoding='utf-8') as f:
f.write('abcdefghij')
print(list(read_in_chunks('chunks.txt', 3)))
['abc', 'def', 'ghi', 'j']
Пояснение: генератор выдает порции данных. Это полезно для потоковой обработки.
Пример 2: подсчет строк и слов в файле
with open('text.txt', 'w', encoding='utf-8') as f:
print('один два', file=f)
print('три четыре', file=f)
print('пять', file=f)
with open('text.txt', 'r', encoding='utf-8') as f:
lines = f.readlines()
line_count = len(lines)
word_count = sum(len(line.split()) for line in lines)
print(line_count, word_count)
3 5
Пояснение: readlines возвращает список строк. Метод split делит строку по пробельным символам.
Пример 3: фильтрация строк при записи нового файла
with open('source.txt', 'w', encoding='utf-8') as f:
print('важное', file=f)
print('мусор', file=f)
print('важное тоже', file=f)
with open('source.txt', 'r', encoding='utf-8') as src:
with open('filtered.txt', 'w', encoding='utf-8') as dst:
for line in src:
if 'мусор' not in line:
dst.write(line)
with open('filtered.txt', 'r', encoding='utf-8') as f:
print(f.read())
важное важное тоже
Пояснение: вложенные with открывают два файла. Чтение и запись идут построчно.
Пример 4: временный файл через tempfile
import tempfile
import os
fd, path = tempfile.mkstemp(text=True)
try:
with os.fdopen(fd, 'w', encoding='utf-8') as f:
print('Временные данные', file=f)
with open(path, 'r', encoding='utf-8') as f:
print(f.read())
finally:
os.remove(path)
Временные данные
Пояснение: mkstemp создает файл и дескриптор. fdopen превращает дескриптор в файловый объект. Удаление выполняется вручную.
Пример 5: запись CSV без внешних библиотек
rows = [
['имя', 'возраст'],
['Анна', '28'],
['Иван', '34'],
]
with open('people.csv', 'w', encoding='utf-8', newline='') as f:
for row in rows:
print(','.join(row), file=f)
with open('people.csv', 'r', encoding='utf-8', newline='') as f:
print(f.read())
имя,возраст Анна,28 Иван,34
Пояснение: newline='' предотвращает двойные переводы строк в CSV на некоторых платформах. Для сложных случаев есть модуль csv.
Пример 6: модуль csv для чтения и записи
import csv
with open('users.csv', 'w', encoding='utf-8', newline='') as f:
writer = csv.writer(f)
writer.writerow(['login', 'email'])
writer.writerow(['admin', 'admin@example.com'])
with open('users.csv', 'r', encoding='utf-8', newline='') as f:
reader = csv.reader(f)
for row in reader:
print(row)
['login', 'email'] ['admin', 'admin@example.com']
Пояснение: csv.writer и csv.reader корректно обрабатывают кавычки и разделители.
Пример 7: атомарная запись через временный файл и замену
import os
import tempfile
target = 'config.txt'
with tempfile.NamedTemporaryFile('w', encoding='utf-8', delete=False, dir='.') as tmp:
print('ключ=значение', file=tmp)
temp_name = tmp.name
os.replace(temp_name, target)
with open(target, 'r', encoding='utf-8') as f:
print(f.read())
ключ=значение
Пояснение: os.replace атомарно заменяет целевой файл. Такой прием снижает риск частично записанного файла.
Пример 8: произвольный доступ через seek и tell
with open('seek.txt', 'w+', encoding='utf-8') as f:
f.write('0123456789')
f.seek(3)
part = f.read(4)
pos = f.tell()
print(part, pos)
3456 7
Пояснение: seek перемещает позицию. tell возвращает текущую позицию. В текстовом режиме смещения зависят от кодировки, поэтому для точного доступа лучше бинарный режим.
Пример 9: бинарное копирование с буфером
buffer_size = 1024
with open('input.bin', 'wb') as f:
f.write(b'0123456789' * 200)
with open('input.bin', 'rb') as src, open('output.bin', 'wb') as dst:
while True:
chunk = src.read(buffer_size)
if not chunk:
break
dst.write(chunk)
print(open('output.bin', 'rb').read()[:10])
b'0123456789'
Пояснение: чтение блоками снижает расход памяти. Оба файла открыты в бинарном режиме.
Пример 10: обработка исключений при чтении
try:
with open('missing.txt', 'r', encoding='utf-8') as f:
data = f.read()
except FileNotFoundError:
print('Файл отсутствует')
except PermissionError:
print('Нет доступа')
except UnicodeDecodeError:
print('Проблема с кодировкой')
else:
print(data)
Файл отсутствует
Пояснение: разные исключения обрабатываются отдельно. Это дает понятные сообщения.
Пример 11: запись и чтение JSON
import json
data = {'name': 'Анна', 'age': 28}
with open('data.json', 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
with open('data.json', 'r', encoding='utf-8') as f:
loaded = json.load(f)
print(loaded['name'], loaded['age'])
Анна 28
Пояснение: ensure_ascii=False сохраняет русские символы. indent=2 делает файл читаемым.
Пример 12: сжатый файл gzip
import gzip
with gzip.open('text.gz', 'wt', encoding='utf-8') as f:
print('Сжатая строка', file=f)
with gzip.open('text.gz', 'rt', encoding='utf-8') as f:
print(f.read())
Сжатая строка
Пояснение: gzip.open поддерживает текстовые режимы 'wt' и 'rt'. Для бинарных данных используются 'wb' и 'rb'.
Пример 13: чтение файла с обратным порядком строк
with open('reverse.txt', 'w', encoding='utf-8') as f:
print('первая', file=f)
print('вторая', file=f)
print('третья', file=f)
with open('reverse.txt', 'r', encoding='utf-8') as f:
lines = f.readlines()
for line in reversed(lines):
print(line.rstrip())
третья вторая первая
Пояснение: readlines загружает все строки в память. Для больших файлов применяется отдельный алгоритм с seek.
Пример 14: проверка существования и размера файла
import os
path = 'check.txt'
with open(path, 'w', encoding='utf-8') as f:
f.write('проверка')
print(os.path.exists(path))
print(os.path.getsize(path))
True 6
Пояснение: os.path.exists проверяет наличие. os.path.getsize возвращает размер в байтах. Для русских букв в UTF-8 размер может отличаться от числа символов.