Работа с файлами в Python: полное руководство
Работа с файлами в Python: открытие в разных режимах, чтение и запись данных, pathlib, обработка ошибок и управление позицией в файле.
Работа с файлами позволяет программам постоянно хранить данные и получать их позже. В этой главе рассматриваются все распространённые файловые операции в Python: открытие файлов в различных режимах, чтение и запись данных, навигация внутри файла, задание кодировки символов и обработка ошибок, возникающих в реальном коде.
Функция open()
Каждая файловая операция в Python начинается с open(). Она возвращает объект файла, предоставляющий методы для чтения, записи и позиционирования внутри файла.
file_object = open(file, mode="r", encoding=None)file— путь к файлу (string или объектpathlib.Path).mode— режим открытия файла (см. таблицу ниже).encoding— кодировка текста, например"utf-8". Всегда указывайте её для текстовых файлов, чтобы код работал одинаково на любой операционной системе.
Режимы открытия файлов
| Режим | Значение | Создаёт файл? | Очищает существующий файл? |
|---|---|---|---|
"r" | Чтение (по умолчанию) | Нет | Нет |
"w" | Запись | Да | Да |
"a" | Добавление | Да | Нет |
"x" | Эксклюзивное создание | Ошибка, если файл существует | — |
"r+" | Чтение и запись | Нет | Нет |
"b" | Двоичный режим (в сочетании с другими, например "rb") | — | — |
"t" | Текстовый режим (по умолчанию, в сочетании с другими, например "rt") | — | — |
Всегда используйте with для открытия файлов
Оператор with (менеджер контекста) гарантирует закрытие файла при выходе из блока — даже если возникло исключение. Это предотвращает утечки ресурсов и обеспечивает сброс буферизованных записей на диск.
Безопасное открытие файла с помощью with
with open("notes.txt", "r", encoding="utf-8") as f:
contents = f.read()
# File is automatically closed hereВызов open() без with и забытый file.close() — распространённая причина повреждения данных и ошибок «слишком много открытых файлов» в длительно работающих программах.
Чтение файлов
Python предоставляет несколько способов считывания содержимого файла.
Чтение всего файла с помощью read()
read() возвращает полное содержимое файла в виде единой строки.
Чтение всего файла
with open("notes.txt", "r", encoding="utf-8") as f:
content = f.read()
print(content)Для больших файлов это загружает всё содержимое в память сразу, что приемлемо для маленьких файлов, но неэффективно для многогигабайтных журналов.
Чтение фиксированного числа символов
Передайте целое число в read(n), чтобы прочитать не более n символов.
Чтение первых 20 символов
with open("notes.txt", "r", encoding="utf-8") as f:
chunk = f.read(20)
print(repr(chunk))Чтение строки за строкой с помощью readline()
readline() возвращает следующую строку вместе с завершающим \n, или пустую string в конце файла.
Построчное чтение файла с помощью readline()
with open("notes.txt", "r", encoding="utf-8") as f:
line = f.readline()
while line:
print(line, end="") # line already contains '\n'
line = f.readline()Итерация по строкам (наиболее идиоматичный способ)
Прямая итерация по объекту файла — наиболее эффективный по памяти подход для построчного чтения.
Итерация по строкам
with open("notes.txt", "r", encoding="utf-8") as f:
for line in f:
print(line, end="")Чтение всех строк в список с помощью readlines()
readlines() возвращает список, где каждый элемент — одна строка (включая символ новой строки).
Чтение всех строк в список
with open("notes.txt", "r", encoding="utf-8") as f:
lines = f.readlines()
print(lines[0]) # first line
print(len(lines)) # total number of linesИспользуйте readlines(), когда нужен произвольный доступ к конкретным строкам по индексу. Для последовательной обработки предпочтительнее паттерн for line in f.
Запись файлов
Режим записи ("w")
Режим записи создаёт файл, если он не существует, и очищает его (обнуляет содержимое), если он существует.
Запись текста в файл
with open("output.txt", "w", encoding="utf-8") as f:
f.write("Line one\n")
f.write("Line two\n")Запись нескольких строк с помощью writelines()
writelines() принимает итерируемый объект из строк. Символы новой строки не добавляются автоматически.
Запись списка строк
lines = ["apple\n", "banana\n", "cherry\n"]
with open("fruits.txt", "w", encoding="utf-8") as f:
f.writelines(lines)Режим добавления ("a")
Режим добавления перемещает позицию записи в конец файла перед каждой записью, поэтому существующее содержимое никогда не перезаписывается.
Добавление записи в журнал
import datetime
entry = f"{datetime.date.today()} — task complete\n"
with open("log.txt", "a", encoding="utf-8") as f:
f.write(entry)При каждом запуске этого скрипта в log.txt добавляется новая строка без изменения предыдущих записей.
Режим эксклюзивного создания ("x")
Используйте "x", когда нужно создать новый файл с гарантией того, что существующий не будет перезаписан. Python вызывает FileExistsError, если файл уже существует.
Создание файла только в случае его отсутствия
try:
with open("config.txt", "x", encoding="utf-8") as f:
f.write("[settings]\n")
except FileExistsError:
print("config.txt already exists — not overwriting.")Позиция в файле: seek() и tell()
Объекты файлов поддерживают внутренний указатель позиции, который перемещается по мере чтения или записи. Этот указатель можно проверять и изменять.
tell()— возвращает текущую позицию в байтах.seek(offset, whence=0)— перемещает указатель.whence=0(по умолчанию) — от начала,1— от текущей позиции,2— от конца.
Возврат в начало с помощью seek(0)
with open("notes.txt", "r", encoding="utf-8") as f:
first_pass = f.read()
f.seek(0) # go back to the start
second_pass = f.read()
print(first_pass == second_pass) # Trueseek() особенно полезен в режиме "r+" (чтение-запись), когда нужно прочитать раздел, а затем перезаписать его в рамках того же открытого файла.
Работа с двоичными файлами
Откройте файл в двоичном режиме, добавив "b" к строке режима ("rb", "wb", "ab"). Двоичный режим возвращает необработанные байты вместо строк, что необходимо для изображений, аудио, сжатых архивов и других нетекстовых данных.
Копирование файла в двоичном режиме
with open("photo.jpg", "rb") as src:
data = src.read()
with open("photo_backup.jpg", "wb") as dst:
dst.write(data)Не указывайте encoding при использовании двоичного режима — Python вызовет ValueError, если вы это сделаете.
Обработка ошибок
Файловые операции могут завершаться неудачей предсказуемым образом. Оборачивание их в блоки try/except делает скрипты надёжными.
Обработка распространённых ошибок файлов
try:
with open("data.txt", "r", encoding="utf-8") as f:
content = f.read()
except FileNotFoundError:
print("Error: the file does not exist.")
except PermissionError:
print("Error: you do not have permission to read this file.")
except OSError as e:
print(f"OS error: {e}")Распространённые исключения, с которыми вы столкнётесь:
| Исключение | Когда возникает |
|---|---|
FileNotFoundError | Чтение файла, которого не существует |
FileExistsError | Создание файла в режиме "x", когда он уже существует |
PermissionError | Отсутствие прав на чтение/запись |
IsADirectoryError | Попытка открыть директорию как файл |
UnicodeDecodeError | Байты файла не соответствуют указанной кодировке |
Современная альтернатива: pathlib
В Python 3.4 появился pathlib.Path — объектно-ориентированный подход к путям файловой системы. Объекты Path seamlessly работают с open() и предоставляют собственные удобные методы read_text() / write_text().
Чтение файла с помощью pathlib
from pathlib import Path
content = Path("notes.txt").read_text(encoding="utf-8")
print(content)Запись файла с помощью pathlib
from pathlib import Path
Path("output.txt").write_text("Hello, world!\n", encoding="utf-8")read_text() и write_text() открывают и закрывают файл за вас, делая разовые операции чтения и записи очень лаконичными. Используйте open() с блоком with, когда нужен более тонкий контроль — например, чтение файла по частям или использование seek().
Переименование и перемещение файлов
Для переименования или перемещения файла используйте os.rename() при переименовании в пределах одной файловой системы или shutil.move(), когда нужно переместить между файловыми системами.
Переименование файла с помощью os.rename()
import os
os.rename("old_name.txt", "new_name.txt")Перемещение файла с помощью shutil.move()
import shutil
shutil.move("report.txt", "archive/report.txt")shutil.move() работает даже когда источник и назначение находятся на разных дисках; os.rename() в этом случае вызывает OSError.
Проверка существования файла
Перед открытием файла для чтения может потребоваться убедиться, что он существует. Используйте os.path.exists() или эквивалент из pathlib.
Проверка существования с помощью os.path
import os
if os.path.exists("data.txt"):
print("File found.")
else:
print("File not found.")Проверка существования с помощью pathlib
from pathlib import Path
p = Path("data.txt")
if p.exists():
print("File found.")Обратите внимание, что os.path.exists() и Path.exists() могут возвращать устаревшие результаты в многопоточных или многопроцессных программах. В таких случаях предпочтительнее просто вызвать open() и перехватить FileNotFoundError.
Сводка ключевых функций
| Операция | Подход через os / shutil | Подход через pathlib |
|---|---|---|
| Открыть и прочитать | open(path, "r") | Path(path).read_text() |
| Открыть и записать | open(path, "w") | Path(path).write_text() |
| Проверить существование | os.path.exists(path) | Path(path).exists() |
| Переименовать | os.rename(src, dst) | Path(src).rename(dst) |
| Переместить | shutil.move(src, dst) | Path(src).rename(dst) (та же ФС) |
| Удалить | os.remove(path) | Path(path).unlink() |
Связанные главы
- Чтение файлов в Python — подробный разбор каждого метода чтения
- Запись и создание файлов в Python — запись, создание и рекомендации
- Удаление файлов в Python — безопасное удаление файлов и директорий
- Try Except в Python — обработка исключений в Python
- Оператор with в Python — как работают менеджеры контекста