Справочник по Python · модуль 8 · исключения и файлы
Глава 8.6
Запись в файл:
write()
Метод записывает строку в текущую позицию и не делает ничего сверх этого: не добавляет перевод строки, не преобразует числа, не спрашивает подтверждения. Два следствия — самые частые сюрпризы темы: всё слипается в одну строку, а режим "w" стирает файл при открытии.
- Категория
- Файлы
- Уровень
- Базовый
- Связанные темы
- Строки, режимы открытия, кодировки
- Зачем это знать
- Сохранять данные в файл и не потерять существующие
§1Последовательная запись
Последовательный доступ из главы 8.4 означает, что данные пишутся подряд: каждая новая порция ложится в текущую позицию, а позиция сдвигается вперёд. Перескочить к произвольному месту нельзя — этим занимается seek() из главы 8.11.
Для записи файл открывают в подходящем режиме, и выбор режима здесь важнее, чем при чтении: ошибка стоит потерянных данных.
§2Режимы для записи
| Режим | Что делает | Существующее содержимое |
|---|---|---|
"w" | открывает для записи | удаляется |
"a" | открывает для дозаписи | сохраняется, запись идёт в конец |
"wb" | двоичная запись | удаляется |
"w+" | запись и чтение | удаляется |
Во всех четырёх случаях файл будет создан, если его нет. Разница только в судьбе существующего содержимого.
- содержимое уцелело
- содержимое стёрто или файл не открыт
Решающая строка — первая. open(path, "w") стирает файл в момент открытия, ещё до первого write(): даже если программа упадёт следующей строкой, старых данных уже не будет. Это самая дорогая ошибка во всей теме файлов, и защита от неё — режим "x" из главы 8.13.
§3Метод write()
write(string) записывает строку в текущую позицию файла.
with open("demo.txt", "w", encoding="utf-8") as file: file.write("Привет") print(repr(open("demo.txt", encoding="utf-8").read()))
'Привет'
Никаких добавлений: ни пробела, ни перевода строки, ни кавычек. Что передали — то и оказалось в файле.
§4Что возвращает write()
Метод возвращает количество записанных символов — в отличие от методов изменения коллекций из модуля 3, которые возвращали None.
with open("demo.txt", "w", encoding="utf-8") as file: count = file.write("Привет") print(count)
6
Обратите внимание: символов, а не байтов. В UTF-8 эти шесть букв займут на диске двенадцать байтов — разница из главы 5.4. В двоичном режиме метод вернёт именно число байтов.
§5Перевод строки не добавляется
Самая частая неожиданность: два вызова подряд не создают двух строк.
with open("demo.txt", "w", encoding="utf-8") as file: file.write("первая") file.write("вторая") print(repr(open("demo.txt", encoding="utf-8").read()))
'перваявторая'
with open("demo.txt", "w", encoding="utf-8") as file: file.write("первая\n") file.write("вторая\n") print(repr(open("demo.txt", encoding="utf-8").read()))
'первая\nвторая\n'
Это отличает write() от print(), который перевод строки добавляет сам — параметр end из главы 1.4. Здесь такого умолчания нет.
§6Режим w стирает файл
Второе важное свойство, и оно разрушительное: содержимое удаляется в момент открытия, ещё до первого write().
до запуска: строка 1
строка 2
open(path, "w") ← файл стал пустым уже здесь
file.write("новое")
после: новое
with open("demo.txt", "w", encoding="utf-8") as file: file.write("новое") print(repr(open("demo.txt", encoding="utf-8").read()))
'новое'
Программа, которая ведёт журнал в режиме "w", при каждом запуске теряет весь предыдущий журнал. Ошибка не проявляется на первом запуске — она проявляется на втором, когда терять уже есть что.
§7Режим a дописывает
Когда существующее содержимое нужно сохранить, берут режим "a" — от append.
with open("demo.txt", "a", encoding="utf-8") as file: file.write(" ещё") print(repr(open("demo.txt", encoding="utf-8").read()))
'новое ещё'
| Задача | Режим |
|---|---|
| создать отчёт заново | "w" |
| вести журнал событий | "a" |
| сохранить настройки | "w" |
| копить измерения | "a" |
§8Запись нескольких строк
Чтобы записать список строк, write() вызывают в цикле, добавляя перевод строки.
lines = ["строка 1", "строка 2", "строка 3"] with open("demo.txt", "w", encoding="utf-8") as file: for line in lines: file.write(line + "\n") print(repr(open("demo.txt", encoding="utf-8").read()))
'строка 1\nстрока 2\nстрока 3\n'
Для этой задачи есть и специальный метод writelines() — ему посвящена глава 8.7. Забегая вперёд: перевод строки он тоже не добавляет.
§9Только строки
Аргумент должен быть строкой. Число метод не примет.
with open("demo.txt", "w", encoding="utf-8") as file: file.write(42)
TypeError: write() argument must be str, not int
Это снова отличие от print(), который приводит к строке что угодно. Здесь преобразование нужно делать самому.
§10Запись чисел
value = 42 with open("demo.txt", "w", encoding="utf-8") as file: file.write(str(value) + "\n") file.write(f"значение: {value}\n") file.write("итого: {}\n".format(value)) print(open("demo.txt", encoding="utf-8").read())
42 значение: 42 итого: 42
Помните вывод главы 8.4: в текстовом файле лежат символы, а не числа. При чтении обратное преобразование делают через int() или float().
§11Двоичный режим
В режиме "wb" метод принимает не строку, а байты — тип bytes из главы 5.4.
with open("demo.bin", "wb") as file: count = file.write("Привет".encode("utf-8")) print(count) print(open("demo.bin", "rb").read())
12 b'\xd0\x9f\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82'
Здесь видна разница из §4: символов было шесть, байтов записано двенадцать. Попытка передать обычную строку в двоичный файл даёт TypeError: a bytes-like object is required, not 'str'. Аргумент encoding в двоичном режиме не указывают — кодирование выполняется вручную.
§12Зачем нужен двоичный формат
| Причина | В чём выигрыш |
|---|---|
| компактность | число занимает меньше места, чем его текстовая запись |
| скорость | не нужно кодировать и разбирать текст |
| точность | дробные числа сохраняются без округления при выводе |
| нетекстовые данные | изображения, звук, архивы текстом не выразить |
Плата — нечитаемость: такой файл не открыть Блокнотом и не поправить руками. Поэтому настройки и журналы обычно хранят текстом, а данные большого объёма — в двоичном виде.
§13Режим w+
Плюс к режиму означает «и чтение, и запись через один объект».
with open("demo.txt", "w+", encoding="utf-8") as file: file.write("abc") file.seek(0) print(file.read())
abc
Вызов seek(0) обязателен: после записи позиция стоит в конце файла, и чтение вернуло бы пустую строку. Работа с позицией разбирается в главе 8.11. Режим "w+", как и "w", стирает файл при открытии — для чтения существующего с возможностью правки берут "r+".
§14Частые ошибки
| Что написано | Что произойдёт | Как правильно |
|---|---|---|
"w" вместо "a" для журнала | при каждом запуске файл обнуляется | режим "a" |
два write() подряд без \n | текст слипнется в одну строку | добавлять "\n" самому |
file.write(42) | TypeError: нужна строка | file.write(str(42)) |
file.write("текст") в режиме "wb" | TypeError: нужны байты | "текст".encode("utf-8") |
считать возврат write() байтами | в текстовом режиме это символы | для байтов открыть в "wb" |
читать после записи без seek(0) | вернётся пустая строка | перевести позицию в начало |
открыть "w", чтобы «посмотреть» файл | файл будет очищен | режим "r" |
писать без encoding | на другой машине файл прочитается иначе | encoding="utf-8" |
§15Проверьте себя
Когда режим "w" удаляет содержимое файла?
В момент открытия — ещё до первого вызова write(). Даже если запись не состоится, файл уже пуст.
Добавляет ли write() перевод строки?
Нет. В отличие от print(), символ \n нужно дописывать самому.
Что возвращает write()?
Количество записанных символов в текстовом режиме и количество байтов — в двоичном.
Почему file.write(42) — ошибка?
Метод принимает только строку и ничего не преобразует. Нужен str(42) или f-строка.
Чем "a" отличается от "w"?
"a" сохраняет существующее содержимое и пишет в конец, "w" очищает файл при открытии.
Зачем seek(0) в режиме "w+"?
После записи позиция стоит в конце файла, и чтение вернуло бы пустую строку. seek(0) возвращает её в начало.
§16Шпаргалка
file.write("текст\n")open(path, "w", encoding="utf-8")open(path, "a", encoding="utf-8")file.write(f"{value}\n")for line in lines: file.write(line + "\n")file.write(text.encode("utf-8"))Итог главы
write() записывает строку в текущую позицию и возвращает число записанных символов. Он не добавляет перевод строки и не преобразует числа — обе привычки достались от print() и здесь не работают. Режим определяет судьбу существующего содержимого: "w" стирает файл уже при открытии, "a" дописывает в конец. В двоичном режиме метод принимает bytes, и строку приходится кодировать вручную.