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', чтобы дописывать без перезаписи всего файла. Эти небольшие решения предотвращают склеивание строк и экономят время на дальнейшем сопровождении.