Запись и создание файлов в Python
Все способы записи файлов в Python: write(), writelines(), режим append, бинарная запись, pathlib и безопасные паттерны с блоком with.
Запись файлов — одна из самых фундаментальных операций ввода-вывода в Python. Сохраняете ли вы результаты работы программы, конфигурацию, экспортируете данные в CSV или записываете события в лог — вам нужен надёжный способ создавать и обновлять файлы. В этой главе рассматриваются все подходы, которые предоставляет Python: write(), writelines(), режим append, бинарная запись, обработка символов новой строки, кодировка символов, современный API pathlib и паттерны безопасной записи без потери данных.
Открытие файла для записи
Любая операция записи начинается со встроенной функции open(). Второй аргумент — режим — определяет, что происходит при открытии файла:
| Режим | Значение | Файл существует | Файл отсутствует |
|---|---|---|---|
"w" | Запись (текст) | Усекает (стирает) файл | Создаёт новый файл |
"a" | Добавление (текст) | Перемещает указатель в конец | Создаёт новый файл |
"x" | Исключительное создание | Вызывает FileExistsError | Создаёт новый файл |
"wb" | Запись (бинарный) | Усекает файл | Создаёт новый файл |
"ab" | Добавление (бинарный) | Перемещает указатель в конец | Создаёт новый файл |
"r+" | Чтение + запись | Открывает на месте | Вызывает FileNotFoundError |
Самое важное, что нужно помнить о режиме "w": он молча стирает весь файл перед записью. Если вы хотите только добавить содержимое к существующему файлу, используйте режим "a" (append).
Всегда указывайте параметр encoding при записи текстовых файлов, чтобы ваш код вёл себя одинаково на Windows, macOS и Linux:
file = open("output.txt", "w", encoding="utf-8")Всегда используйте блок with
Вызов open() без блока with означает, что вы должны самостоятельно вызвать file.close(). Забыв закрыть файл, вы рискуете тем, что буферизованные данные никогда не будут записаны на диск, получите ошибки «слишком много открытых файлов» в долгоработающих скриптах и повреждение файлов на некоторых операционных системах.
Оператор with (менеджер контекста) решает все эти проблемы. Python автоматически закрывает файл при выходе из блока — даже если внутри блока возникло исключение.
with open("output.txt", "w", encoding="utf-8") as f:
f.write("Hello, World!\n")
# File is closed and flushed here — guaranteedВсе примеры в этой главе используют оператор with. Избегайте ручного паттерна open() / close().
Запись текста с помощью write()
file.write(string) записывает переданную строку в файл и возвращает количество записанных символов. Символ новой строки не добавляется автоматически — вы должны включить \n самостоятельно.
Запись одной строки в новый файл
with open("greeting.txt", "w", encoding="utf-8") as f:
chars_written = f.write("Hello, World!\n")
print(chars_written) # 14Запись нескольких строк многократными вызовами write()
with open("poem.txt", "w", encoding="utf-8") as f:
f.write("Roses are red,\n")
f.write("Violets are blue,\n")
f.write("Python is great,\n")
f.write("And so are you.\n")Каждый вызов write() добавляет данные в файл начиная с текущей позиции. Файл записывается заново (всё предыдущее содержимое удаляется), поскольку использован режим "w".
Запись нескольких строк с помощью writelines()
file.writelines(iterable) принимает любой итерируемый объект строк — список, генератор или кортеж — и записывает каждый элемент последовательно. Как и write(), он не добавляет символы новой строки между элементами.
Запись списка строк
lines = [
"First line\n",
"Second line\n",
"Third line\n",
]
with open("lines.txt", "w", encoding="utf-8") as f:
f.writelines(lines)Если исходные данные не содержат \n, добавьте его перед записью:
data = ["Alice", "Bob", "Charlie"]
with open("names.txt", "w", encoding="utf-8") as f:
f.writelines(name + "\n" for name in data)Выражение-генератор name + "\n" for name in data эффективно по памяти: Python создаёт каждую строку по требованию, не строя весь список в памяти заранее.
write() против writelines() — когда что использовать
write() | writelines() | |
|---|---|---|
| Входные данные | Одна строка | Любой итерируемый объект строк |
| Символы новой строки | Вы управляете каждым \n | Вы управляете каждым \n |
| Лучше всего для | Постепенного формирования вывода | Записи готовой последовательности за один раз |
Создание файла, который не должен существовать
Используйте режим "x" (исключительное создание), если хотите, чтобы Python создал новый файл и завершился с ошибкой, если файл уже существует. Это предотвращает случайную перезапись важных данных.
try:
with open("config.txt", "x", encoding="utf-8") as f:
f.write("host=localhost\n")
f.write("port=8080\n")
except FileExistsError:
print("config.txt already exists — not overwriting.")Этот паттерн полезен при генерации уникальных выходных файлов (логов, экспортов, снимков состояния), когда коллизия означает, что что-то пошло не так.
Добавление данных в существующий файл
Открытие файла в режиме "a" перемещает указатель записи в конец файла. Новое содержимое добавляется после существующего; ничего не стирается.
Добавление записи в лог в существующий файл
import datetime
with open("app.log", "a", encoding="utf-8") as f:
timestamp = datetime.datetime.now().isoformat()
f.write(f"[{timestamp}] Server started\n")Если app.log ещё не существует, Python создаёт его. Если он уже существует, новая строка добавляется в конец. При многократном запуске скрипта лог постепенно растёт.
Запись против добавления — выбор правильного режима
- Используйте
"w", когда хотите заменить содержимое файла полностью (сгенерировать свежий отчёт, сохранить новую конфигурацию). - Используйте
"a", когда хотите добавить к существующему содержимому (ведение лога, накопление результатов за несколько запусков).
Символы новой строки и окончания строк
Текстовый режим Python ("w", "a", "r") транслирует универсальный символ новой строки \n в платформенный разделитель строк при записи:
- Windows:
\n→\r\n(CRLF) - macOS / Linux:
\nостаётся\n(LF)
Обычно это правильное поведение — файлы, записанные на Windows, корректно открываются в Блокноте.
Если нужно принудительно задать конкретное окончание строки независимо от платформы — например, при генерации файлов, которые должны читаться конкретной системой, — передайте параметр newline:
# Force Unix-style LF on all platforms (e.g. for Linux-target files)
with open("unix_file.txt", "w", encoding="utf-8", newline="\n") as f:
f.write("line one\n")
f.write("line two\n")
# Preserve line endings exactly as given (no translation at all)
with open("raw.txt", "w", encoding="utf-8", newline="") as f:
f.write("line one\r\n")
f.write("line two\n")Кодировка символов
Всегда указывайте encoding= при записи текстовых файлов. Использование платформенной кодировки по умолчанию рискует создать файлы, которые нельзя прочитать на других системах.
Рекомендуемые кодировки для распространённых сценариев:
| Кодировка | Когда использовать |
|---|---|
"utf-8" | Общего назначения; работает для всех языков; по умолчанию для большинства Python-проектов |
"utf-8-sig" | UTF-8 с BOM — полезна для файлов, которые будут открываться в Excel на Windows |
"latin-1" | Устаревшие западноевропейские файлы |
"cp1252" | Windows ANSI текст |
Запись файла в кодировке UTF-8
with open("international.txt", "w", encoding="utf-8") as f:
f.write("English: Hello\n")
f.write("Japanese: こんにちは\n")
f.write("Arabic: مرحبا\n")Запись бинарных файлов
Откройте файл в режиме "wb" (запись бинарных данных), чтобы записывать необработанные байты вместо строк. Бинарный режим необходим для изображений, аудио, сжатых архивов, исполняемых файлов и любых нетекстовых данных. Не указывайте encoding в бинарном режиме.
Запись байтов в бинарный файл
data = bytes([0x89, 0x50, 0x4E, 0x47]) # PNG magic bytes
with open("header.bin", "wb") as f:
f.write(data)
print(f.write(b"\r\n\x1a\n")) # 4Копирование бинарного файла
with open("photo.jpg", "rb") as src:
content = src.read()
with open("photo_backup.jpg", "wb") as dst:
dst.write(content)Для больших бинарных файлов читайте и записывайте по частям, чтобы не загружать весь файл в память:
CHUNK = 65536 # 64 KB
with open("large.bin", "rb") as src, open("large_copy.bin", "wb") as dst:
while True:
chunk = src.read(CHUNK)
if not chunk:
break
dst.write(chunk)Обработка ошибок при записи
Скрипт производственного качества всегда предусматривает возможные ошибки записи файла.
Обработка распространённых ошибок записи
try:
with open("/etc/protected.txt", "w", encoding="utf-8") as f:
f.write("data\n")
except PermissionError:
print("Error: you do not have write permission for this file.")
except FileNotFoundError:
print("Error: one or more directories in the path do not exist.")
except IsADirectoryError:
print("Error: the path points to a directory, not a file.")
except OSError as e:
print(f"OS error: {e}")Распространённые исключения, с которыми вы столкнётесь:
| Исключение | Когда возникает |
|---|---|
PermissionError | У процесса нет прав на запись |
FileNotFoundError | Промежуточный каталог в пути не существует |
FileExistsError | Режим "x" и файл уже существует |
IsADirectoryError | Путь указывает на каталог |
OSError | Диск заполнен, ошибка сетевой файловой системы и другие проблемы уровня ОС |
Смотрите Python Try Except — полное руководство по обработке исключений.
Безопасная запись файлов (паттерн атомарной записи)
Обычный open("file.txt", "w") небезопасен для критически важных данных: если ваш скрипт аварийно завершится или будет прерван в процессе записи, файл останется в частично записанном, повреждённом состоянии. Стандартное решение — атомарная запись: сначала запишите во временный файл, затем переименуйте его поверх целевого.
import os
import tempfile
def write_file_safely(path, content, encoding="utf-8"):
"""Write content to path atomically using a temp file + rename."""
dir_name = os.path.dirname(os.path.abspath(path)) or "."
# Write to a temp file in the same directory (same filesystem = atomic rename)
fd, tmp_path = tempfile.mkstemp(dir=dir_name)
try:
with os.fdopen(fd, "w", encoding=encoding) as f:
f.write(content)
os.replace(tmp_path, path) # atomic on POSIX; best-effort on Windows
except Exception:
os.unlink(tmp_path) # clean up if something went wrong
raise
write_file_safely("important.txt", "critical data\n")os.replace() (Python 3.3+) атомарно заменяет файл назначения на POSIX-системах: читатели видят либо старый файл, либо новый, но никогда — частичную запись.
Запись файлов с помощью pathlib
pathlib.Path (введён в Python 3.4) предоставляет лаконичный объектно-ориентированный API. Для простых единовременных записей Path.write_text() и Path.write_bytes() читаются лучше, чем open().
Path.write_text()
from pathlib import Path
Path("output.txt").write_text("Hello from pathlib!\n", encoding="utf-8")write_text() открывает файл в режиме "w", записывает строку и закрывает файл — всё за один вызов. Он всегда перезаписывает файл. Эквивалента для добавления не существует; для добавления используйте open() с режимом "a".
Path.write_bytes()
from pathlib import Path
Path("data.bin").write_bytes(b"\x00\x01\x02\x03")Построение путей с помощью pathlib
pathlib также упрощает безопасное построение путей без конкатенации строк:
from pathlib import Path
output_dir = Path("results")
output_dir.mkdir(exist_ok=True) # create the directory if needed
report_path = output_dir / "report.txt"
report_path.write_text("Run complete.\n", encoding="utf-8")
print(report_path) # results/report.txt
print(report_path.exists()) # TrueОператор / на объектах Path соединяет сегменты пути — нет необходимости в os.path.join().
Практический пример: запись CSV-отчёта
Следующий полный пример записывает список записей в CSV-файл, используя только встроенные инструменты (без модуля csv), демонстрируя несколько концепций из этой главы вместе.
from pathlib import Path
import datetime
def write_csv_report(path, headers, rows):
"""Write a simple CSV file with a header row."""
with open(path, "w", encoding="utf-8", newline="") as f:
f.write(",".join(headers) + "\n")
for row in rows:
f.write(",".join(str(v) for v in row) + "\n")
records = [
("Alice", 30, "Engineering"),
("Bob", 25, "Marketing"),
("Charlie", 35, "Finance"),
]
output = Path("staff_report.txt")
write_csv_report(output, ["Name", "Age", "Department"], records)
print(output.read_text(encoding="utf-8"))Ожидаемый вывод:
Name,Age,Department
Alice,30,Engineering
Bob,25,Marketing
Charlie,35,FinanceОбратите внимание: newline="" передаётся в open(), чтобы Python не применял двойную трансляцию окончаний строк внутри CSV-строк — это соответствует рекомендации в документации модуля csv Python.
Для всего более сложного (кавычки, диалекты, граничные случаи Unicode) используйте встроенный модуль Python CSV.
Краткий справочник
| Цель | Паттерн кода |
|---|---|
| Создать или перезаписать файл | open("f.txt", "w", encoding="utf-8") |
| Добавить данные в файл | open("f.txt", "a", encoding="utf-8") |
| Создать только если нового | open("f.txt", "x", encoding="utf-8") |
| Записать бинарные данные | open("f.bin", "wb") |
| Записать одну строку | f.write("text\n") |
| Записать список строк | f.writelines(lines) |
| Единовременная запись текста | Path("f.txt").write_text("...", encoding="utf-8") |
| Единовременная запись байтов | Path("f.bin").write_bytes(b"...") |
| Безопасная / атомарная запись | Запись во временный файл, затем os.replace() |
Связанные главы
- Python File Handling — режимы открытия, параметры
open()и операторwith - Python Read Files —
read(),readline(),readlines(), итерация по строкам иseek() - Python Delete Files — безопасное удаление файлов и каталогов
- Python Try Except — обработка исключений в Python
- Python CSV — чтение и запись CSV-файлов с модулем
csv - Python JSON — сериализация данных в JSON-файлы