Урок курса

Запись в текстовый файл

Python для новичков

В прошлом уроке ты открывал файлы в режиме 'r' и читал содержимое — конструкция with open(...) as f уже знакома. Сейчас меняется только режим: вместо чтения будем писать. И вот здесь сразу возникает выбор, который определяет, что произойдёт с файлом в момент открытия.

Режимы записи 'w' и 'a': перезапись и дозапись

Когда открываешь файл для записи, Python смотрит на второй аргумент функции open() — строку режима. Для записи используются два основных варианта: 'w' и 'a'. Они похожи внешне, но ведут себя принципиально по-разному.

Режим 'w' — перезапись. Как только Python выполняет open('log.txt', 'w'), файл очищается немедленно — ещё до того, как ты вызовешь хоть один метод записи. Если файл существовал и в нём были данные, они безвозвратно удаляются. Если файл не существовал — он создаётся пустым. После этого ты пишешь в чистый файл с нуля.

Режим 'a' — дозапись (append). При открытии файл не трогается: существующее содержимое остаётся нетронутым, а курсор записи встаёт в самый конец. Всё, что ты запишешь, добавится после последней строки. Если файл не существовал — он тоже создаётся, как и в режиме 'w'.

Разница критична на практике. Представь, что log.txt уже содержит записи за прошлый сеанс:

# Режим 'w' — ОСТОРОЖНО: все предыдущие записи будут уничтожены
with open('log.txt', 'w', encoding='utf-8') as f:
    f.write('Новый сеанс\n')
# log.txt теперь содержит только одну строку — предыдущие данные стёрты

# Режим 'a' — предыдущие записи сохраняются
with open('log.txt', 'a', encoding='utf-8') as f:
    f.write('Новый сеанс\n')
# log.txt содержит всё, что было раньше, плюс эта строка в конце

Главная ловушка режима 'w' в том, что файл стирается в момент открытия, а не в момент записи. Это значит: даже если программа упадёт с ошибкой сразу после open(), не записав ничего, старый файл уже потерян. Поэтому 'w' используют осознанно — когда нужно создать файл заново или перезаписать его целиком. Для накопления данных между запусками правильный выбор — 'a'.

Методы write() и writelines(): запись строк в файл

Когда файл открыт в режиме 'w' или 'a', для записи данных в него используются два метода файлового объекта: write() и writelines().

Метод write() принимает одну строку и записывает её в файл. Ничего лишнего он не делает — в частности, не добавляет перенос строки в конце. Это принципиальное отличие от print(), к которому ты, скорее всего, привык: print('текст') сам ставит \n после вывода, а write('текст') — нет.

Если не добавить \n явно, все вызовы write() запишут данные в одну непрерывную строку:

with open('log.txt', 'w', encoding='utf-8') as f:
    f.write('Первое событие')
    f.write('Второе событие')

Файл после этого будет содержать:

Первое событиеВторое событие

Чтобы каждая запись оказалась на отдельной строке, \n нужно вставлять самому:

with open('log.txt', 'w', encoding='utf-8') as f:
    f.write('Первое событие\n')
    f.write('Второе событие\n')

Теперь файл выглядит так, как ожидается:

Первое событие
Второе событие

Метод writelines() принимает не одну строку, а итерируемый объект — обычно список строк — и записывает каждый элемент подряд. По поведению это эквивалентно вызову write() для каждого элемента в цикле: никаких разделителей и переносов между элементами не добавляется.

события = [
    'Пользователь вошёл\n',
    'Открыт файл config.txt\n',
    'Пользователь вышел\n',
]

with open('log.txt', 'w', encoding='utf-8') as f:
    f.writelines(события)

Это удобно, когда у тебя уже есть готовый список строк и не хочется писать цикл вручную. Но обрати внимание: \n в конце каждой строки списка — снова твоя ответственность. Если написать 'Пользователь вошёл' без \n, строки в файле снова сольются.

Простое правило: и write(), и writelines() пишут ровно то, что ты передаёшь. Хочешь перенос — передай \n. Хочешь, чтобы строки были на отдельных строках — позаботься об этом сам.

Дозапись в действии: режим 'a' и накопление данных

Режим 'a' уже введён, и понятно, что он добавляет данные в конец. Но возникает закономерный вопрос: как Python знает, где именно находится этот конец, если файл закрывался и открывался заново?

Рассмотрим простой случай — два независимых открытия:

# Первый запуск программы
with open('log.txt', 'a', encoding='utf-8') as f:
    f.write('10:00 — программа запущена\n')

# ... время прошло, файл закрыт ...

# Второй запуск программы
with open('log.txt', 'a', encoding='utf-8') as f:
    f.write('10:15 — пользователь открыл меню\n')

Ответ — на уровне операционной системы. При каждом открытии в режиме 'a' ОС сама находит текущий конец файла и ставит курсор записи туда. Python не читает файл, не считает байты и не запоминает смещение между запусками — это делает ОС при каждом обращении заново.

Из этого следует важное практическое свойство: режим 'a' работает корректно независимо от того, сколько раз файл открывался и закрывался, из одного места кода или из разных. Каждое открытие — это независимый запрос к ОС найти конец файла. Именно поэтому 'a' надёжен для любого сценария накопления данных — от простого цикла до нескольких скриптов, пишущих в один файл по очереди.

Программа сохранения лога действий в файл

Теперь соберём всё изученное в одну рабочую программу. Задача простая: пользователь вводит команды в цикле, а программа записывает каждое действие в файл log.txt.

Вот полный вариант:

print('Программа запущена. Введите действие или "стоп" для завершения.')

while True:
    действие = input('> ')
    строка = f'Действие: {действие}\n'

with open('log.txt', 'a', encoding='utf-8') as f:
        f.write(строка)

if действие.lower() == 'стоп':
        break

print('Лог сохранён в log.txt')

Что здесь происходит по шагам:

  1. Программа запрашивает ввод в бесконечном цикле.

  2. Из введённого текста формируется строка лога — 'Действие: открыл меню\n' — с явным \n в конце.

  3. Файл открывается в режиме 'a': каждый вызов добавляет строку в конец, не трогая предыдущие записи.

  4. Конструкция with гарантирует, что файл закрывается сразу после записи.

  5. Когда пользователь вводит 'стоп', цикл завершается.

После нескольких вводов log.txt будет выглядеть так:

Действие: открыл настройки
Действие: изменил имя пользователя
Действие: сохранил изменения
Действие: стоп

Для учебного логгера файл открывается в режиме 'a' внутри цикла: конструкция with закрывает его после каждой записи, а старые строки сохраняются. Это простой вариант для начинающего; сравнивать стратегии буферизации в этом уроке не требуется.

Если нужно сбросить лог перед новым сеансом, достаточно один раз открыть файл в режиме 'w' в самом начале программы:

# Очищаем лог перед стартом нового сеанса
with open('log.txt', 'w', encoding='utf-8') as f:
    f.write('=== Новый сеанс ===\n')

После этого все последующие записи через 'a' будут добавляться к этому чистому началу. Это типичная схема: 'w' один раз при старте для инициализации, 'a' во всём остальном коде для накопления.

Попробуйте решить

Программист открыл существующий файл notes.txt с данными в режиме 'w' и сразу закрыл файл, ничего не записав:

with open('notes.txt', 'w', encoding='utf-8') as f:
 pass

Что произойдёт с содержимым файла?

Продолжить с проверкой и прогрессом

Откройте интерактивный раннер с заданиями урока.

Перейти к интерактивному уроку