Alfa Brain

Переносы строк CRLF и LF, Каретка и клавиша Shift

Алексей ВечкановАлексей Вечканов   

Если разработчик работает за одной разновидностью операционной системы, то он вряд ли столкнется с проблемой описанной ниже. Но как только ему приходится разрабатывать на разных операционных системах (Windows, Linux, MacOs) - вот тут могут появиться вопросы. Вопросы по поводу переносов строк в разных операционных системах.

Изображение статьи

В этой статье я отвечу на вопросы: Что такое переносы строк? Какими последовательностями символов они обозначаются, и наверное самый главный вопрос, почему с ними такая путаница?

Если совсем кратко, то при использовании любого текстового редактора, набирая в нем текст, вы делаете переносы строк. Чаще всего, эти переносы строк никак не отображаются в браузере. Для обычного пользователя, это просто перенос строки и все. Но ведь для машины любой текст это просто набор нулей и единиц. Как же тогда редактору понять где нужно сделать переносы строк, отображая очередной текст в текстовом редакторе? Правильно, нужно использовать специальную последовательность нулей и единиц, которые подскажут редактору, где нужно делать переносы. То есть нажимая клавишу Enter, вы не просто делаете перенос строки, а вставляете в ваш текст специальный символ переноса строки. Обычно в редакторах такие символы не отображаются, но их конечно же можно сделать видимыми (Чуть позже я покажу, как можно включить отображение скрытых символов в разных редакторах).

Отображаемые переносы строк в редакторе VS Code
Отображаемые переносы строк в редакторе VS Code

И вот в чем путаница. Исторически сложилось так, что для переноса строчек, в разных операционных системах использовались разные последовательности символов. То есть если вы написали файл в одно операционной системе, и в ней у вас отлично работают переносы строк, то может быть так, что на другой OS все склеится в одну большую строчку, так как в этой операционной системе переносы строка задаются иначе.

Честно говоря, эту проблему сложно заметить, так как различные OS научились определять форматы переносов строк и адаптируют свои программы таким образом, чтобы переносы строк отображались правильно. Но это не исключает того фактора, что внутри самого текстового файла хранятся разные последовательности символов для переноса строк. Если вы разработчик, рано или поздно вы с этим столкнетесь.

Итак, давайте разбираться.

Новая строка

Новая строка (часто называется (line ending), концом строки (end of line или EOL), следующей строкой (next line или NEL) или разрывом строки (next line)) — это специальный управляющий символ или последовательность управляющих символов в спецификациях кодировки символов, таких как ASCII, EBCDIC, Unicode и других. Много названий, но суть одна. Этот символ или последовательность символов, используется для обозначения конца строки текста и начала новой.

Таких специальных последовательностей символов наберется с десяток. На разных старых компьютерах могут использоваться совсем специфические символы для переноса строк. Но, нас это уже мало интересует. Самые распространенные из низ это последовательности LF и CRLF.

Выбор переноса строк в редакторе VS Code
Выбор переноса строк в редакторе VS Code

LF (0A в HEX) - расшифровывается как Line Feed или Подача Линии (нет, не кормящая линия)
CR (0D в HEX)  - расшифровывается как Caret Return или возврат каретки.

Операционные системы Linux, MacOS для обозначения переноса строк используют последовательность LF.
OS Windows использует две последовательности одна за другой - CR LF.

Почему так? Будем разбираться дальше. Но перед тем как мы продолжим, обязательно посмотрите видео ниже.

Что такое Каретка, LF и CR

Отлично, теперь мы знаем откуда все пошло. Наш мигающий символ места ввода называется Каретка, а когда мы нажимаем Enter, создаем перенос строки, то есть возвращаем каретку на новую строчку.

Итого:
LF (line feed) - это виртуальная подача бумаги на одну линию.
CR (caret return) - это виртуальный сдвиг каретки, что бы мы могли начать печатать с новой строки.

Ок, но для чего все это? С печатной машинкой все понятно. Не сдвинешь каретку, не сможешь больше печатать, не подашь бумагу, будешь печатать поверх старого. То есть нужно 2 действия, но зачем это все в редакторе. Ведь в цифровом документе невозможно напечатать текст поверх предыдущего. Зачем этот сдвиг или подача бумаги? Все очень хитро.

Телетайпы

Перед тем как я затрону историю появления последовательностей переноса строк, хочу познакомить вас с телетайпами, иначе дальше можно не уловить смысл.

Телетайп (англ. teletype, TTY) — электромеханическая печатная машина, используемая для передачи между двумя абонентами текстовых сообщений по простейшему электрическому каналу (обычно по паре проводов).

Телетайп времён Второй мировой войны
Телетайп времён Второй мировой войны

Посмотрите видео ниже, чтобы лучше представлять как они работают.

Обратите внимание, что данные уже передаются в цифровом виде, но вот подача бумаги и перевод каретки все еще физические действия которые занимают довольно много времен.

История переноса строк

Следующая история из статьи на вики.

В середине 1800-х годов, задолго до появления телетайпов, операторы азбуки Морзе и телеграфисты изобрели и использовали символы азбуки Морзе для кодирования и форматирования текста с пробелами в текстовых сообщениях. В частности, в азбуке Морзе для обозначения новой строки.

Позже, в эпоху современных телетайпов, были разработаны стандартизированные управляющие коды набора символов, помогающие форматировать текст с пробелами. Кодировка ASCII была разработана одновременно Международной организацией по стандартизации (ISO) и Американской ассоциацией стандартов (ASA), последняя была организацией предшественницей Американского национального института стандартов (ANSI). В период с 1963 по 1968 год проекты стандартов ISO поддерживали использование только CR+LF или LF в качестве новой строки, в то время как проекты ASA поддерживали только CR+LF.

То есть уже в те годы, один стандарт предписывал использовать сразу две последовательности символов для переноса строки (CR+LF), а другой позволял указывать только одну (LF).

Последовательность CR + LF использовалась во многих ранних компьютерных системах, в которых использовались телетайпы - например для Teletype Model 33 ASR. Разделение новой строки на две последовательности скрывало тот факт, что печатающая головка не могла вовремя вернуться из крайнего правого положения в начало следующей строки для печати следующего символа. Любой символ, напечатанный после CR, часто печатался в виде пятна в середине страницы, в то время как печатающая головка все еще перемещала каретку обратно в первое положение. Вот что об этом писал Qualline, Steve (Автор книги по текстовому редактору VIM)  «Решение состояло в том, чтобы сделать новую строку двумя символами: CR, чтобы переместить каретку в первый столбец, и LF, чтобы переместить бумагу вверх». На самом же деле, и этого было мало. Часто приходилось отправлять лишние символы — посторонние CR или NUL, — которые игнорируются телетайпом, но дают печатающей головке время для перемещения к левому полю. Многим ранним видеодисплеям также требовалось несколько символов для прокрутки дисплея.

То есть по факту, двойные последовательности для переноса строк это всего лишь "костыль" который нужен был для того, чтобы все печаталось корректно. На телетайпе, можно было выполнить физическую подачу бумаги и перенос каретки одной последовательностью управляющих символов (LF), но это приводило к тому, что печать следующей строчки начиналась еще до того, как каретка доедет до начала строки. Эту проблему решили, не исправлением телетайпов, а просто программным путем, делая задержки для телетайпа.

Приложения/редакторы должны были напрямую общаться с машиной Teletype и следовать ее соглашениям, поскольку концепция драйверов устройств, скрывающих такие детали оборудования от приложения, еще не была хорошо разработана. Поэтому текст обычно составлялся для удовлетворения потребностей телетайпов. Это соглашение использовалось в большинстве миникомпьютерных систем от DEC. CP/M (Control Program for Microcomputers — операционная система для массового рынка, созданная в 1974 году для процессоров Intel 8080/85) также использовала его для печати на тех же терминалах, что и мини-компьютеры. Оттуда MS-DOS (1981 г.) приняла CR + LF для совместимости между операционными системами, и это соглашение было унаследовано более поздней операционной системой Microsoft Windows.

Операционная система Multics начала разрабатываться в 1964 году и использовала только LF в качестве новой строки. Multics использовала драйвер устройства для преобразования этого символа в любую последовательность, необходимую принтеру (включая дополнительные символы заполнения), а один байт был более удобен для программирования. То, что кажется более очевидным выбором было — не использовать CR, поскольку CR предоставлял полезную функцию наложения одной строки на другую для создания эффектов полужирного шрифта, подчеркивания и зачеркивания. Возможно, что более важно, использование только LF в качестве разделителя строки уже было включено в проекты возможного стандарта ISO/IEC 646. Unix последовала практике Multics, а более поздние Unix-подобные системы последовали за Unix. Это создавало конфликты между Windows и Unix-подобными операционными системами, из-за чего файлы, созданные в одной операционной системе, не могли быть правильно отформатированы или интерпретированы другой операционной системой.

Понятия возврата каретки (CR) и перевода строки (LF) тесно связаны между собой и могут рассматриваться как по отдельности, так и вместе. В физических носителях пишущих машинок и принтеров две оси движения, «вниз» и «поперек», необходимы для создания новой строки на странице. Хотя конструкция машины (пишущей машинки или принтера) должна учитывать их по отдельности, абстрактная логика программного обеспечения может объединить их вместе как одно событие. Вот почему новая строка в кодировке символов может быть определена как CR и LF, объединенные в одну (обычно называемую CR+LF или CRLF).

Заключение

Мы разобрались в истории и теперь знаем откуда такая путаница с переносами строк. По сути - все из-за бага с телетайпами и костыля в виде символа перевода каретки который помог решить эту проблему. Вывод который я могу сделать из этой истории: Пишите код сразу правильно, иначе костыль сделанный на время останется с вами навсегда. В следующей статье я опиши способы автоматической замены и приведения символов переносов строк к одному варианту. Буду рад если оставите комментарий.

На десерт

Ну, и на десерт, видео от автора про пишущую машинку, о том как работает клавиша shift на печатных машинках.

Ссылки на материалы:
CRLF vs. LF: Normalizing Line Endings in Git
Newline



Поделиться: