Перенос строки: символы новой строки, ОС и тег
в HTML

Перенос строки: символы новой строки, ОС и тег <br> в HTML Полезное

Перенос строки — это управляющий символ или их последовательность, которые отмечают конец одной строки текста и начало следующей. В файле это не «пустота» и не нажатие Enter само по себе, а конкретные байты: чаще всего это \n (LF), реже \r (CR) или их пара \r\n (CRLF). Ниже разберем, чем эти символы отличаются, почему в Windows, Linux и macOS они разные, как перенести строку в HTML тегом <br> и какие ошибки возникают при работе с текстовыми данными.

Что такое символ новой строки

Когда вы нажимаете Enter в редакторе, программа вставляет в текст невидимый управляющий символ. Именно он и есть перенос строки. В большинстве языков программирования такие символы записывают через обратный слэш — это называется escape-последовательность:

  • \n — LF (Line Feed, «перевод строки», код 10, или 0x0A). Изначально команда сдвинуть бумагу в печатной машинке на строку вниз.
  • \r — CR (Carriage Return, «возврат каретки», код 13, или 0x0D). Изначально команда вернуть каретку к левому краю.

Разделение на два символа осталось с эпохи телетайпов, где начать новую строку означало и опустить валик (LF), и вернуть каретку в начало (CR). Компьютеры унаследовали эти коды, но распорядились ими по-разному — отсюда и путаница между ОС.

Различия между Windows, Unix и старым Mac

Разные операционные системы исторически выбрали разные символы для обозначения конца строки. Это главный источник проблем при переносе файлов между системами.

Обозначение Escape Код (dec / hex) Где используется по умолчанию
LF \n 10 / 0x0A Unix, Linux, macOS (с 2001 года)
CR \r 13 / 0x0D Классический Mac OS (до версии 9, до 2001 года)
CRLF \r\n 13 10 / 0x0D 0x0A Windows, MS-DOS; сетевые протоколы HTTP, SMTP

Ключевые моменты, которые стоит запомнить:

  • Windows использует пару \r\n. Именно поэтому текстовый файл из Windows, открытый простым инструментом в Linux, иногда показывает в конце строк лишний символ ^M — это и есть «висящий» \r.
  • Классический Mac OS (System 9 и раньше) применял одиночный \r; сегодня это история, но такие переносы встречаются в старых данных.
  • В сетевых протоколах (HTTP, SMTP, FTP) разделителем строк по стандарту служит CRLF, независимо от ОС сервера.

Перенос строки в коде: примеры

Разберем поведение символов на коде. Примеры на Python 3 (актуальная ветка на 2026 год — Python 3.13), их можно запустить как есть.

Печать строки с переносом внутри:

text = "первая строка\nвторая строка"
print(text)
# первая строка
# вторая строка

print(repr(text))
# 'первая строка\nвторая строка'

Функция print интерпретирует \n как реальный перенос, а repr показывает строку «как она записана в коде», с видимой escape-последовательностью. Проверим коды и длину символов:

print(ord("\n"), ord("\r"))   # 10 13
print(len("\n"), len("\r\n")) # 1 2

То есть \n — это один символ, а \r\n — два символа, идущих подряд. Это важно при подсчете длины и при обрезке строк.

Ошибка: разбиваем текст только по \n

Частая ошибка — резать текст из Windows-источника только по символу \n, забыв про \r.

Неверно:

data = "строка1\r\nстрока2"
print(data.split("\n"))

Фактический вывод:

['строка1\r', 'строка2']

У первого элемента остался «хвост» \r, который потом ломает сравнения и вывод. Исправление — метод splitlines, который понимает все виды переносов сразу:

data = "строка1\r\nстрока2"
print(data.splitlines())
# ['строка1', 'строка2']

# splitlines корректно обрабатывает и смешанные переносы:
print("a\r\nb\rc\nd".splitlines())
# ['a', 'b', 'c', 'd']

Ошибка: лишние пустые строки при записи файла

Второй классический случай — лишние пустые строки при записи CSV или текста в Windows.

Неверно:

import csv
with open("out.csv", "w") as f:      # newline не указан
    csv.writer(f).writerow(["a", "b"])

Фактический результат в Windows: модуль csv уже пишет \r\n, а текстовый режим файла добавляет к каждому \n еще один \r. Получается \r\r\n, и между строками появляются пустые строки. Исправление — открывать файл с newline="":

import csv
with open("out.csv", "w", newline="") as f:
    csv.writer(f).writerow(["a", "b"])
# лишние пустые строки исчезают

Полезно помнить про «универсальные переносы строк»: при чтении файла в текстовом режиме Python сам приводит \r\n и \r к \n. Поэтому при построчном чтении разница между ОС обычно не мешает — проблемы начинаются при записи и в бинарном режиме.

Перенос строки в HTML: тег br и не только

В HTML перенос работает иначе, чем в текстовом файле. Браузер по умолчанию схлопывает любые пробелы и переносы строк из исходного кода в один пробел. Поэтому просто нажать Enter в разметке недостаточно.

Неверно (перенос в исходнике ни на что не влияет):

<p>первая строка
вторая строка</p>

Фактический результат на странице:

первая строка вторая строка

Чтобы получить настоящий перенос, используют одиночный тег <br>. Это пустой (void) элемент: у него нет закрывающего тега и содержимого.

<p>первая строка<br>вторая строка</p>

Результат на странице:

первая строка
вторая строка

В HTML5 достаточно писать <br>; форма <br/> тоже допустима и нужна в XML-совместимой разметке (XHTML), но обязательной уже не является. Тег <br> уместен для смысловых разрывов внутри одного блока — адрес, строки стихотворения, строки в форме. Для отдельных абзацев используют <p>, а не два <br> подряд.

Если же нужно сохранить переносы и пробелы ровно так, как в исходнике (например, вывести фрагмент кода), их не обязательно расставлять тегами — достаточно тега <pre> или CSS-свойства white-space:

.code { white-space: pre-wrap; }

Значение pre сохраняет и переносы, и пробелы; pre-wrap делает то же, но еще и переносит длинные строки по ширине блока; pre-line сохраняет переносы, но схлопывает лишние пробелы. Перенос же длинных слов — отдельная задача свойств overflow-wrap и word-break.

Экранирование и кодировки

Символ переноса не печатается, поэтому в коде его записывают экранированием — через \n и \r. Обратный слэш служебный: "\\n" даст на экране текст \n, а не перенос.

С точки зрения кодировки перенос — это обычный символ таблицы:

  • В ASCII и UTF-8 LF занимает один байт 0x0A, CR — один байт 0x0D.
  • В UTF-16 те же символы занимают по два байта: 0x000A и 0x000D.

Проверить это можно так:

print("\n".encode("utf-8"))    # b'\n'
print("\n".encode("utf-16-le"))# b'\n\x00'

Поэтому при работе с бинарными данными важно знать кодировку файла: искать байт 0x0A «вслепую» в UTF-16 или в неизвестной кодировке — ненадежно.

Выводы

  • Перенос строки — это конкретный управляющий символ в данных, а не просто нажатие Enter: чаще всего \n (LF, код 10), реже \r (CR, код 13) или пара \r\n (CRLF).
  • Операционные системы расходятся: Unix, Linux и современная macOS используют \n, Windows — \r\n, классический Mac OS до 2001 года — \r. Отсюда «висящие» \r и лишние пустые строки при переносе файлов.
  • В коде надежнее разбивать текст методом splitlines, а не split("\n"), и записывать файлы с учетом newline, чтобы не удвоить \r.
  • В HTML переносы из исходника схлопываются в пробел: для видимого разрыва внутри блока нужен тег <br>, а для сохранения исходного форматирования — <pre> или CSS white-space.
  • В кодировке LF и CR — обычные символы: один байт в ASCII и UTF-8, два байта в UTF-16. При работе с бинарными данными это нужно учитывать.

Где применяется / связь с практикой

С переносами строк сталкивается любой, кто обрабатывает текст: парсит логи и CSV, читает и пишет файлы, генерирует HTML, обменивается данными по сети. Умение отличать LF от CRLF экономит часы отладки, когда «одинаковый» файл ведет себя по-разному на разных машинах.

Отрабатывать это удобнее всего на практике — на реальных файлах и коде. Базовые навыки работы со строками, файлами и кодировками дают на курсе Python Basic: там разбирают чтение и запись файлов, обработку текстовых данных и типичные ошибки вроде тех, что описаны выше. Если хочется сначала присмотреться к формату обучения, посмотрите ближайшие бесплатные вебинары — там можно вживую задать вопросы преподавателям.

Смежные темы: UTF-16: что это такое и чем она отличается от UTF-8, Двоичный код: что это такое, как работает и где применяется, XML: что это такое, как выглядит XML-файл и как с ним работать.

FAQ

Почему на Windows после переноса файла из Linux появляются лишние пустые строки или символ ^M?
Linux размечает строки одиночным \n, а многие Windows-программы ждут \r\n и, наоборот, при обратном переносе добавляют лишний \r, который отображается как ^M. Лечится приведением переносов к нужному виду — инструментами dos2unix/unix2dos или настройкой редактора.

Как узнать, какие переносы строк в конкретном файле — LF или CRLF?
Большинство редакторов (VS Code, Notepad++ и другие) показывают тип переносов в статусной строке и переключают его одним кликом. В терминале это видно через file имя_файла или просмотром байтов через xxd: 0d 0a означает CRLF, одиночный 0a — LF.

Сколько тегов <br> подряд можно ставить, чтобы сделать отступ между абзацами?
Так делать не стоит: <br> предназначен для разрыва внутри одного блока, а не для управления отступами. Для расстояния между абзацами используют отдельные теги <p> и CSS-свойство margin.

OTUS Журнал
Скидка 5% 14-20 сентября на курсы (popup)