2025, Dec 25 00:03

Склеивание строк при записи в файл в Python: причины и решение

Почему в Python при записи строки склеиваются: роль \n, readlines() и writelines(); как нормализовать окончания и когда использовать режим 'a' вместо перезаписи.

Когда пишешь простой менеджер задач на Python, легко упустить из виду, как строки попадают на диск. Небольшой рефакторинг — переход с match-case на if-else — привёл к проблеме с выводом: новые задачи стали «прилипать» к концу предыдущей строки вместо того, чтобы появляться на новой. В основе ошибки — обработка символов перевода строки при чтении и записи.

Демонстрация проблемы

Фрагмент ниже показывает поведение, при котором добавленные задачи склеиваются, потому что записываются без завершающего перевода строки:

while True:
    cmd = input("Type add, show, edit, complete or exit: ")
    cmd = cmd.strip()

    if 'add' in cmd or 'new' in cmd:
        entry = cmd[4:]

        with open('tasks.txt', 'r') as fh:
            lines = fh.readlines()

        lines.append(entry)

        with open('tasks.txt', 'w') as fh:
            fh.writelines(lines)

    elif 'show' in cmd:
        with open('tasks.txt', 'r') as fh:
            lines = fh.readlines()

        for idx, val in enumerate(lines):
            val = val.strip('\n')
            row = f"{idx+1}-{val}"
            print(row)

Что пошло не так

Скорее всего, проблема возникла после добавления cmd = cmd.strip(), которое удаляет символ новой строки \n в конце ввода. Функция writelines() не добавляет переводы строки сама по себе: она записывает строки ровно в том виде, в каком они есть в списке. При этом строки, полученные через readlines(), уже содержат завершающий \n. Когда к ним добавляется новая задача без этого символа и весь список записывается через writelines(), последняя существующая строка и новая задача оказываются на одной строке.

Как исправить

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

with open('tasks.txt', 'w') as fh:
    for line in lines:
        line = line.strip('\n')
        fh.write(line + '\n')

Если файл может ещё не существовать, защитите начальное чтение, чтобы избежать исключения, и в этом случае начинайте с пустого списка:

try:
    with open('tasks.txt', 'r') as fh:
        lines = fh.readlines()
except FileNotFoundError:
    lines = []

Когда нужно просто добавить задачу в существующий файл, рассмотрите open() в режиме 'a' — так не придётся читать все строки и перезаписывать файл при каждом добавлении.

Почему это важно

Обработка текста в Python обманчиво проста, но работа с переводами строк — частый источник тонких ошибок. Непонимание того, как взаимодействуют readlines(), writelines() и окончание строк, приводит к испорченным данным, запутанному выводу и лишним операциям ввода‑вывода. Обеспечивая единообразные окончания строк и выбирая подходящий режим файла, вы сохраняете данные аккуратными, а операции — эффективными.

Выводы

Запомните три практичных правила. Всегда нормализуйте окончания строк перед записью, особенно когда смешиваете ввод пользователя и данные из readlines(). Применяйте writelines() только если элементы списка уже содержат нужный \n, либо используйте write() в цикле, который это гарантирует. Когда требуется лишь добавить записи, выбирайте режим 'a', чтобы дописывать без перезаписи всего файла. Эти небольшие решения предотвращают склеивание строк и экономят время на дальнейшем сопровождении.