Зачем пустая строка в конце файла
Некоторые инструменты стиля кода рекомендуют это, и я помню, как некоторые инструменты командной строки unix предупреждали об отсутствии пустой строки.
В чем причина наличия лишней пустой строки?
Некоторые инструменты не работают, если файл не заканчивается новой строкой. Это отличается от пустой строки в конце (это будет 2 символа новой строки).
cat файл в оболочке, и вы поймете, почему. Если в вашем файле приглашение моей оболочки появляется в любом другом месте, кроме того, в котором оно должно быть (в начале строки), я, вероятно, буду вас ненавидеть. ;)
Я наткнулся на этот старый вопрос и просто не могу поверить, что каждый ответ пытается оправдать недостатки и недостатки других инструментов и систем, говоря, что современные кодировщики должны добавлять символ, который не имеет ценности в самом коде. Поговорим о 5 обезьянах в клетке! :-D
Многие старые инструменты работают неправильно, если последняя строка данных в текстовом файле не заканчивается символом новой строки или комбинацией возврата каретки / новой строки. Они игнорируют эту строку, так как вместо этого она заканчивается ^ Z (eof).
Спасибо за ответ! Есть ли примеры популярных инструментов, которые могут демонстрировать такое поведение?
@NickM Почти все инструменты командной строки POSIX / Unix, которые принимают текстовый ввод или читают текстовый файл, предполагают, что \n в конце файла заканчивается строка ( ). Несколько текстовых редакторов, таких как Vim, и несколько компиляторов (особенно C ++ и Python) будут выдавать предупреждения. (В случае C ++ стандарт явно требует этого.)
Если вы попытаетесь объединить два текстовых файла вместе, вы будете намного счастливее, если первый будет заканчиваться символом новой строки.
Но когда вы когда-нибудь объединяли файлы и у вас не было возможности добавлять новые строки между ними во время объединения?
Помимо того, что это более удобная позиция курсора при переходе к концу файла в текстовом редакторе.
Наличие новой строки в конце файла обеспечивает простую проверку того, что файл не был усечен.
Ничто не мешает файлу иметь символы новой строки где-то посередине, и файл может быть легко усечен прямо здесь.
@Rudey, правда, но это хорошая бесплатная эвристика. Случайное усечение вряд ли приведет к появлению новой строки в конце.
Можно также привести аргумент для более чистых различий, если вы добавите в файл, следуя тем же соображениям, что и Почему в списке разрешены конечные запятые?
Следующее копируется (и немного урезается) из связанного ресурса:
включает только однострочное изменение в diff:
Это превосходит более запутанный многострочный diff, когда конечная запятая была опущена:
Появляется пустая строка в конце файла, чтобы стандартное чтение из входного потока знало, когда прекратить чтение, обычно возвращает EOF, чтобы указать, что вы достигли конца. Большинство языков могут обрабатывать маркер EOF. Именно по этой причине с давних времен в DOS маркер EOF был клавишей F6 или Ctrl-Z, для систем * nix это был Ctrl-D.
Большинство, если не все, фактически будут читать прямо до маркера EOF, так что функция чтения из ввода библиотеки времени выполнения будет знать, когда прекратить чтение дальше. Когда вы открываете поток для режима добавления, он стирает маркер EOF и записывает после него, пока явно не будет вызвано закрытие, в которое он вставит маркер EOF в этой точке.
Старые инструменты ожидали появления пустой строки с маркером EOF. В настоящее время инструменты могут обрабатывать пустую строку и игнорировать ее.
^ D не был «маркером EOF». Нажатие ^ D заставляло оболочку закрывать сторону записи канала, из которого выполняла чтение группа процессов переднего плана, так что чтение из этого канала возвращало EOF. Нет "маркера EOF".
@William Pursell Вы ошибочно объединили * NIX и Windows. Устаревшая Windows / DOS абсолютно использовала маркер EOF (26, 0x1a), обычно встроенный в конец большинства файлов, в качестве резерва для совместимости с древним CP / M (кто, черт возьми, использовал CP / M после 1983 года?). Другое "развлечение": \r\n вместо \n вызовов DOS используется смесь ASCIIZ и ASCII $. Хуже того, позже Windows обычно вставляет метку порядка байтов Unicode (BOM) в начало большинства текстовых файлов. Прекрасная «неповторимость».
Также, когда вы изменяете файл и добавляете код в конец файла - diff (по крайней мере, git diff в стандартной конфигурации) покажет, что вы изменили последнюю строку, а единственное, что вы на самом деле сделали - добавили символ новой строки. Так отчеты cvs становятся менее удобными.
Некоторые языки определяют свой входной файл в терминах строк ввода, где каждая строка ввода представляет собой серию символов, оканчивающихся символом возврата каретки. Если их грамматика определена таким образом, то последняя действительная строка файла также должна завершаться возвратом каретки.
Это из-за определения того, что такое текстовый файл. Когда вы создаете новый текстовый файл в любой среде unix, содержимым этого файла является символ новой строки '\ n'
Без этого файл на самом деле не идентифицируется как текстовый файл. Теперь, когда мы добавляем код в этот текстовый файл, речь идет о том, чтобы не удалять эту начальную новую строку, которая определяет сам текстовый файл .
Управляющий символ ASCII, обычно называемый «новой строкой» (U + 000A LINE FEED, \n в C) , не начинает новую строку текстового файла (в стиле Unix). Он заканчивает текущую строку текстового файла. Если последним символом текстового файла является U + 000A, не будет пустой строки «между» U + 000A и маркером EOF файловой системы (как бы то ни было, это реализовано). И наоборот, если последний символ (непустого) текстового файла не равен U + 000A, последняя строка файла не закончена - она считается «неполной».
Вероятно, это будет яснее на некоторых примерах:
Этот файл содержит две полные строки текста. Он не содержит третьей пустой строки.
Этот файл содержит третью пустую строку.
И этот файл содержит только одну полную строку плюс вторую неполную строку.
Иногда неполная последняя строка - это то, что вам нужно, например, наличие новой строки между финальной ?> частью PHP-скрипта и EOF может привести к появлению дополнительных пробелов в обработанном HTML-коде в плохом месте (я бы сослался на конкретные примеры но мне не удалось найти его сегодня утром). Следовательно, хорошие текстовые редакторы будут четко различать все три из вышеперечисленных случаев в своем пользовательском интерфейсе.
Однако старые инструменты обработки текста часто неправильно обрабатывают неполные заключительные строки. Например, некоторые реализации wc не будут считать неполную последнюю строку строкой, а некоторые реализации vi будут молча добавлять новую строку в файл, который не заканчивается на ней, хотите вы этого или нет. Следовательно, вы должны использовать неполные заключительные строки только тогда, когда они вам нужны.
(Примечание: насколько мне известно, все, что я только что сказал, верно и для текстовых файлов в стиле DOS, где двухбайтовая управляющая последовательность U + 000D U + 000A используется для завершения строки, а не просто U + 000A. )
в моем текущем проекте мы всегда вставляем пустую новую строку в конце исходных файлов java. Мы также применяем это с помощью CheckStyle (с уровнем ошибки).
Я долго искал эту тему, но, к сожалению, не могу найти убедительной причины для этого. Кажется, что другие разработчики довольно равнодушны к этому, потому что они просто проверили один флажок в Eclipse formatter, и это делается автоматически. Но я все еще не знаю, зачем это нужно и почему это возможно. быть важными.) Поэтому мой вопрос:
Почему нужны пустые строки в конце исходных файлов Java? Является ли это текущей потребностью или реликтом прошлого и нежелательным в текущих кодовых базах?
Я думаю, что они пытаются обеспечить каждый файл заканчивается конечным символом новой строки. Это отличается от окончания пустой строкой а.к. a. пустая новая линия.
Edit :как @ Easy Angel кратко пояснил в комментариях: trailing newline = " \n "и пустая строка = "\n\n"
ваш лидер либо требует, чтобы каждый файл заканчивался символом новой строки, но его неправильно интерпретируют как мандатирование что каждый файл заканчивается пустой строкой (т. е. пустой строкой, которая заканчивается новой строкой), или иначе
Они пытаются гарантировать, что каждый файл заканчивается символом новой строки, фактически санкционируя каждый конец файла пустой строкой (a.к. a. пустая строка, заканчивающаяся новой строкой), тем самым гарантируя, что файлы заканчиваются хотя бы одной новой строкой (и, возможно, избыточной дополнительной новой строкой - overkill?).
Если редактор фактически не показывает символы новой строки, его нет всегда понятно в некоторых редакторах, что файл:
- НЕ ЗАКАНЧИВАЕТСЯ новая строка вообще,
- заканчивается С одной конечной новой строкой или
- заканчивается С пустой новой строкой, т. е. 2 конечные новые строки
я думаю, что большинство современных редакторов исходного кода вставить пустую строку. Однако при использовании более старых редакторов более общего характера, я всегда пытался бы обеспечить файлы исходного кода (и текстовые файлы в целом) всегда заканчивались конечной новой строкой (которая иногда выходила как пустая строка/пустая новая строка в зависимости от редактора, который я использовал), потому что:
при использовании cat чтобы отобразить файл в командной строке, если файл не имеет конечной новой строки, следующий вывод (например, приглашение оболочки или визуальный разделитель, который скрипт может выводить между файлами) будет отображаться сразу после последнего символа без новой строки, а не начинается с новой строки. В общем, конечная новая строка сделала файлы более удобными для пользователя и скрипта.
Я считаю, что некоторые редакторы (я не помню никаких особенностей) автоматически вставляют конечную новую строку, если в текстовом файле ее нет. Это будет выглядеть так, как будто файл был изменен. Это будет запутанным, если у вас есть куча файлов, открытых в разных окнах, а затем перейдите, чтобы закрыть все из них-редактор предложит вам сохранить, но вы не уверены, сделали ли вы "реальные изменения", чтобы файл или его просто автоматически вставленная новая строка.
некоторые инструменты, такие как diff и некоторые компиляторы будут жаловаться о пропаже пустую строку. Это больше шума, с которым могут иметь дело пользователи и инструменты.
о редакторах, добавляющих новые строки и не способных увидеть, есть ли новая строка против пустой новой строки в конце файла, я только что протестировал Vim, Eclipse и Emacs (в моей системе Windows с Cygwin): я открыл новый файл, набрал " h "" e "" l "" l "" o " и сохранил, не нажимая [ENTER]. Я изучил каждый файл с od -c -t x1 .
- Vim сделал добавить пустую строку.
- Emacs сделал добавить пустую строку.
- затмение не добавить пустую строку.
- Vim не позвольте мне курсор вниз к пустой строке под "hello".
- Emacs сделал позвольте мне курсор вниз к пустой строке под "привет".
- затмение не позвольте мне курсор вниз к пустой строке под "привет".
интерпретировать как вам нравится.
моя личная практика заключается в том, чтобы попытаться обеспечить окончание текстовых файлов с конечной новой строкой. Я просто чувствую, что есть крайней мере сюрприз для людей и инструментов с это дело. В этом отношении я бы не рассматривал исходные файлы, отличные от текстовых файлов.
который, начиная с этого редактирования, показывает хиты, которые говорят о предупреждениях о недостающей конечной новой строке, поступающей от компиляторов C, svn (из-за diff), diff и т. д. Я чувствую, что есть общее ожидание, что текстовые файлы (включая исходные файлы) заканчиваются конечной новой строкой и наименее удивительными (и менее шумными), когда они имеют тенденцию быть там.
наконец-то этой это интересно:
Очистка файлов пустую строку
Текстовые файлы должны иметь все свои строки, завершенные символами новой строки (т. е. \n). Это указано POSIX, который говорит, что текстовый файлфайл, содержащий символы, организованные в ноль или более строк.
Линия, в свою очередь, определяется как
* Последовательность нулей или больше символов плюс завершающий символ.
, все, что сказано, это только моя личная практика. Я рад поделиться своим мнением с любым, кто спросит, но я не навязываю это никому. Я не считаю, что это что-то стоит, предписывающих, как я говорю здесь:
хотя я один, чье Все для последовательности, я также против микроуправления каждым битом стиля. Имея огромный список кодирования условности, особенно когда некоторые из них кажутся произвольными, являются частью того, что мешает людям следовать им. Я думаю, что руководящие принципы кодирования должны быть оптимизированы для наиболее ценных методов, которые улучшают возможности. Насколько улучшается читаемость, ремонтопригодность, производительность и т. д., санкционируя эту практику?
вот хорошая причина для дополнительного разрыва строки в конце:
Если у вас есть файл без разрыва строки в конце, в следующий раз, когда файл будет отредактирован, чтобы добавить другую строку, большинство инструментов слияния подумают, что существующая строка изменилась (я на 90% уверен, что SVN также делает).
в приведенном ниже примере строка, содержащая "последняя строка перед редактированием", не имеет разрыва строки. Если мы попытаемся добавить новую строку "последняя строка после редактирования", как мы видим, обе строки 5 и 6 помечены как изменено, но фактическое содержимое строки 5 в обеих версиях одинаковое.
Если все следуют вашему предложению руководства проекта, то это будет результатом (только строка 6 отличается от исходного файла). Это также позволяет избежать недоразумений во время слияния.
хотя это может не выглядеть большим делом, скажем, один разработчик (A) на самом деле хотел изменить содержимое последней строки, а другой разработчик (B) добавил новый линия. Если вы не используете разрыв строки перед EOF, у вас есть конфликт слияния, потому что разработчик B был вынужден также отредактировать предыдущую последнюю строку, чтобы добавить разрыв строки. И. кому нравятся конфликты CVS/SVN?
ответ бесстыдно украден у Ральфа Рикенбаха:
многие старые инструменты плохо себя ведут, если последний строка данных в текстовом файле отсутствует заканчивается новой линией или перевозкой комбинация return/new line. Они игнорируйте эту строку, когда она завершается вместо этого с ^Z (eof).
поэтому я думаю, что это в основном призрак прошлого. К сожалению, такие призраки могут укусить вас за хвост если не изгнать их должным образом. (Является ли ваш сервер сборки старым и использует более старые сценарии оболочки для сводок и таких вещей).
попробуйте вырезать / вставить весь файл. Что-то ошибка в checkstyle или eclipse:)
Иногда ваш компилятор не анализирует его правильно:
Error: Reached end of file while parsing
помимо уже упомянутых уважительных причин наличия символа новой строки (возможные проблемы со старыми инструментами и diff), вот еще один способ взглянуть на это:
Почему специальный случай последняя строка не добавить символ новой строки, когда все остальные строки в файле есть?
Это просто стиль кодирования. Ничего не болит и не помогает. Я бы не позволил вам беспокоить вас, похоже, что ваши команды предпочитают включать и пустую строку. На самом деле нет хорошего аргумента против этого, кроме того, почему кто-то заботится о том, чтобы добавить его в checkstyle?
Я никогда не слышал о таком требовании.
фактически, я только что подтвердил, что программа Java будет работать без каких-либо ошибок компилятора/времени выполнения или предупреждений, когда в конце файла нет пустой строки.
Это, как сказали некоторые комментаторы, должно быть проблемой стиля кодирования. К сожалению, я не могу предположить, почему может быть важно, чтобы в конце файла на Java была пустая строка. На самом деле, это кажется мне совершенно бессмысленным
Я предполагаю, что все здесь знакомы с пословицей, что все текстовые файлы должны заканчиваться новой строкой. Я знал об этом" правиле " много лет, но всегда задавался вопросом - почему?
3.206 строку Последовательность нулевых или более символов, отличных от символов , плюс завершающий символ .
поэтому строки, не заканчивающиеся символом новой строки, не считаются фактическими строками. Вот почему некоторые программы имеют проблемы с обработкой последней строки файла, если она не завершена.
есть по крайней мере один жесткий преимущество этого руководства при работе с эмулятором терминала: все инструменты Unix ожидают этого соглашения и работают с ним. Например, при объединении файлов с cat , файл, завершенный новой строкой, будет иметь другой эффект, чем без:
и, как показано в предыдущем примере, при отображении файла в командной строке (например, через more ), файл с новой строкой завершается правильным отображением. Неправильно завершенный файл может быть искажен (вторая строка.)
для согласованности очень полезно следовать этому правилу-в противном случае это потребует дополнительной работы при работе с инструментами Unix по умолчанию.
теперь о не совместимый с POSIX системы (в настоящее время это в основном Windows), вопрос спорный: файлы обычно не заканчиваются новой строкой, и (неофициальное) определение строки может, например, быть "текст, который отделить по новым строкам" (обратите внимание на ударение). Это полностью действительный. Однако для структурированных данных (например, программного кода) это делает синтаксический анализ минимально более сложным: обычно это означает, что Парсеры должны быть переписаны. Если синтаксический анализатор изначально был написан с учетом определения POSIX, то было бы проще изменить поток токенов, а не синтаксический анализатор - другими словами, добавить токен "искусственной новой строки" в конец ввода.
каждая строка должна заканчиваться символом новой строки, включая последнюю. Некоторые программы имеют проблемы с обработкой последней строки файла, если она не завершена.
GCC предупреждает об этом не потому, что это не могу обработать файл, но потому что это до в рамках стандарта.
этот ответ является попыткой технического ответа, а не мнения.
если мы хотим быть пуристами POSIX, мы определяем строку как:
последовательность нулевых или более символов, отличных от символов , плюс завершающий символ .
неполная строка как:
последовательность одного или нескольких символов, отличных от в конце файла.
текстовый файл в виде:
файл, содержащий символы, организованные в ноль или более строк. Строки не содержат символов NUL и не могут превышать байт по длине, включая символ . Хотя в POSIX.1-2008 не различая текстовые файлы и двоичные файлы (см. Стандарт ISO C), многие утилиты производят предсказуемый или значимый вывод только при работе с текстовыми файлами. Стандартные утилиты, которые имеют такие ограничения, всегда указывают "текстовые файлы" в своих разделах STDIN или входных файлов.
непрерывный последовательность байтов, завершенных первым нулевым байтом и включающих его.
из этого мы можем вывести, что единственный раз мы будем потенциально возникнут вопросы, если мы имеем дело с понятием строка файла или файла как текстовый файл (том, что текстовый файл is организация из нуля или более строк, и строка, которую мы знаем, должна заканчиваться ).
пример: wc -l filename .
С wc 's руководство мы читаем:
строка определяется как строка символов, разделенных символом .
каковы последствия для JavaScript, HTML и CSS-файлов, тогда они являются текст файлов?
в браузерах, современных IDEs и другие интерфейсные приложения нет проблем с пропуском EOL в EOF. Приложения будут правильно анализировать файлы. Он должен, так как не все операционные системы соответствуют стандарту POSIX, поэтому было бы непрактично для инструментов без ОС (например, браузеров) обрабатывать файлы в соответствии со стандартом POSIX (или любым стандартом уровня ОС).
в результате мы можем быть относительно уверены, что EOL в EOF практически не окажет отрицательного влияния на уровне приложений-независимо от того, если это работает на ОС UNIX.
на данный момент мы можем с уверенностью сказать, что пропуск EOL в EOF безопасен при работе с JS, HTML, CSS на стороне клиента. Фактически, мы можем заявить, что минимизация любого из этих файлов, не содержащих , безопасна.
мы можем сделать этот шаг дальше и сказать, что, насколько это касается NodeJS, он тоже не может придерживаться стандарта POSIX, поскольку он может работать в средах, не совместимых с POSIX.
С чем мы остаемся тогда? Инструменты системного уровня.
это означает, что единственные проблемы, которые могут возникнуть, - это инструменты, которые пытаются придерживаться своей функциональности семантики POSIX (например, определение строки, как показано в wc ).
тем не менее, не все оболочки будут автоматически придерживаться POSIX. Например, Bash не использует поведение POSIX по умолчанию. Есть переключатель, чтобы включить его: POSIXLY_CORRECT .
оставаясь на Инструментальной дорожке, для всех практических целей и задач, давайте рассмотрим это:
давайте работать с файлом, который не имеет EOL. На момент написания этого файла в этом примере это мини-JavaScript без EOL.
уведомления cat размер файла-это точно сумма его отдельных частей. Если конкатенация файлов JavaScript является проблемой для JS-файлов, тем более уместно было бы начать каждый файл JavaScript с запятой.
как кто-то еще упомянул в этой теме: что, если вы хотите cat два файла, выход которых становится только одной строкой вместо двух? Другими словами, cat делает то, что он должен делать.
на man of cat упоминает только чтение ввода до EOF, а не . Обратите внимание, что -n переключатель cat также распечатает не - завершенная строка (или неполная строка) в качестве строка - поскольку счет начинается с 1 (по man .)
-N пронумеруйте выходные линии, начиная с 1.
теперь, когда мы понимаем, как POSIX определяет строка, это поведение становится двусмысленным или действительно несовместимым.
понимание цели и соответствия данного инструмента поможет определить, насколько это важно для конец файлов с помощью EOL. В C, C++, Java (JARs)и т. д. некоторые стандарты будут диктовать новую строку для действительности - такого стандарта не существует для JS, HTML, CSS.
например, вместо использования wc -l filename можно сделать awk '
вывод
будет очень мало реальных случаев использования, когда пропуск EOL в EOF для определенных текстовых файлов, таких как JS, HTML и CSS, окажет негативное влияние - если вообще. Если мы полагаемся на присутствие , мы ограничиваем надежность наших инструментов только файлами, которые мы создаем, и открываем себя для потенциальных ошибок, вносимых сторонними файлами.
мораль истории: инженер tooling который не имеет слабость полагаться на EOL на EOF.
не стесняйтесь публиковать примеры использования, поскольку они применяются к JS, HTML и CSS, где мы можем изучить, как пропуск EOL имеет отрицательный эффект.
Это может быть связано с разницу между:
- текстовый файл (каждая строка должна заканчиваться в конце строки)
- двоичный файл (нет истинных "строк", о которых можно говорить, и длина файла должна быть сохранена)
Если каждая строка заканчивается в конце строки, это позволяет избежать, например, что объединение двух текстовых файлов сделает последнюю строку первого запуска в первую строку второго.
плюс, редактор может проверить при загрузке, заканчивается ли файл в конце строки, сохраняет его в локальном параметре " eol " и использует его при записи файла.
несколько лет назад (2005), многие редакторы (ZDE, Eclipse, Scite. ) неужели "забыли", что окончательный EOL,что было не очень оценено.
Не только это, но они неправильно интерпретировали этот окончательный EOL как "начать новую строку" и фактически начать отображать другую строку, как будто она уже существует.
Это было очень заметно. с "правильным" текстовым файлом с хорошим текстовым редактором, таким как vim, по сравнению с открытием его в одном из вышеупомянутых редакторов. Он отобразил дополнительную строку ниже реальной последней строки файла. Вы видите что-то вроде этого:
некоторые инструменты стиля кода рекомендуют это, и я помню, что видел некоторые инструменты командной строки unix, предупреждающие об отсутствии пустой строки.
каковы причины наличия дополнительной пустой строки?
многие старые инструменты плохо себя ведут, если последняя строка данных в текстовом файле не заканчивается новой строкой или комбинацией возврата каретки / новой строки. Они игнорируют эту строку, поскольку она заканчивается ^Z (eof).
помимо того, что это приятнее позицию курсора при перемещении в конец файла в текстовом редакторе.
с новой строки в конце файла обеспечивает простую проверку того, что файл не был усечен.
Если вы попытаетесь объединить два текстовых файла вместе, вы будете намного счастливее, если первый закончится символом новой строки.
пустая строка в конце файла появляется так, что стандартное чтение из входного потока будет знать, когда прекратить чтение, обычно возвращает EOF, чтобы указать, что вы достигли конца. Большинство языков могут обрабатывать маркер EOF. Именно по этой причине с давних времен, под DOS, маркером EOF была клавиша F6 или Ctrl-Z, для систем *nix это был Ctrl-D.
большинство, если не все, будет фактически читать вплоть до маркера EOF, так что библиотека времени выполнения функция чтения с входного сигнала будет знать, когда прекратить чтение дальше. Когда вы открываете поток для режима добавления, он будет стирать маркер EOF и писать мимо него, пока не будет явно вызвано закрытие, в которое он вставит маркер EOF в этой точке.
старые инструменты ожидали пустой строки, за которой следует маркер EOF. В настоящее время инструменты могут обрабатывать пустую строку и игнорировать ее.
аргумент также может быть сделан для более чистых различий, если вы добавляете в файл, следуя тем же рассуждениям, что и почему Python разрешает конечную запятую в списке?
также, Когда вы изменяете файл и добавляете некоторый код в конце файла - diff (по крайней мере, git diff в стандартной конигурации) покажет, что вы изменили последнюю строку, в то время как единственное, что вы на самом деле сделали - добавили символ новой строки. Таким образом, отчеты cvs становятся менее удобными.
некоторые языки определяют свой входной файл в терминах входных строк, где каждая входная строка представляет собой ряд символов, завершенных возвращением каретки. Если их грамматика определена таким образом, то последняя допустимая строка файла также должна быть завершена возвращением каретки.
Иногда при просмотре диффов коммитов через git log или git diff можно заметить следующий вывод:
Или на GitHub в интерфейсе для просмотра диффов:
Почему это так важно, что Git и GitHub предупреждают нас об этом? Давайте разберемся.
Что может быть проще, чем текстовый файл? Просто текстовые данные — как хранятся на диске, так и отображаются. На самом деле правительство нам врёт всё немного сложнее.
Оффтопик про управляющие символы ASCII
Не все символы, которые содержатся в текстовых файлах, имеют визуальное представление. Такие символы ещё называют "управляющими", и к ним относятся, например:
- нулевой символ ( x00 , \0 ) — часто используется для кодирования конца строки в памяти; т.е. программа считывает символы из памяти по одному до тех пор, пока не встретит нулевой символ, и тогда строка считается завершённой;
- табуляция ( \x09 , \t ) — используется для выравнивания данных по границе столбца, так что это выглядит как таблица;
- перевод строки ( \x0a , \n ) — используется для разделения текстовых данных на отдельные строки;
- возврат каретки ( \x0d , \r ) — переместить курсор в начало строки;
- возврат на один символ ( \x08 , \b ) — переместить курсор на один символ назад;
- звонок ( \x07 , \a ) — если набрать этот символ в терминале, то будет бибикающий символ; именно так консольные программы, типа vim , бибикают на пользователей; .
Многие эти символы пришли к нам из эпохи печатных машинок, поэтому у них такие странные названия. И действительно, в контексте печатной машинки или принтера такие операции, как перевод строки (сместить лист бумаги вверх так, чтобы печатающая головка попала на следующую строку), возврат каретки (переместить печатающую головку в крайнее левое положение) и возврат на один символ назад, обретают смысл. При помощи возврата на один символ назад создавались жирные символы (печатаешь символ, возвращаешься назад и печатаешь его ещё раз) и буквы с диакритическими знаками, такие как à или ã (печатаешь символ, возвращаешься назад и печатаешь апостроф или тильду). Но зачем печатной машинке бибикалка?
Сегодня многие из этих символов потеряли смысл, но некоторые до сих пор выполняют функцию, схожую с исходной.
Текстовые редакторы отображают текстовые файлы в некоем адаптированном виде, преобразуя непечатаемые символы, например, переносы строк и табуляции преобразуются в настоящие отдельные строки или выравнивающие отступы.
Для набора символа переноса строки достаточно нажать клавишу "Enter", но на разных платформах этот символ закодируется по-разному:
- в Unix-совместимых системах (включая современные версии macOS) используется один символ перевода строки ( LF );
- в Windows используется сразу два символа — возврат каретки ( CR ) и перевод строки ( LF );
- в очень старых версиях Mac OS (до 2001 года) использовался один символ CR .
Как видите, Windows точнее всего эмулирует поведение печатной машинки.
В языках программирования символ новой строки часто кодируют при помощи бэкслэш-последовательностей, таких как \n или \r\n . Нужно понимать разницу между такой последовательностью и настоящим символом переноса строки. Если в редакторе в файле *.txt просто набрать \n и сохранить, то вы получите ровно то, что написали. Символом переноса строки оно не станет. Нужно что-то, что заменит эти бэкслэш-последовательности на настоящие символы переноса строки (например, компилятор или интерпретатор языка программирования).
Согласно определению из стандарта POSIX, который тоже пришёл к нам из эпохи печатных машинок:
Строка — это последовательность из нуля или более символов, не являющихся символом новой строки, и терминирующего символа новой строки.
Почему важен этот стандарт? Возможен миллиард способов реализовать одно и то же, и только благодаря стандартам, таким как POSIX, мы имеем сейчас огромное количество качественного ПО, которое не конфликтует друг с другом.
Т.е. если вы не ставите символ переноса строки в конце строки, то формально по стандарту такая строка не является валидной. Множество утилит из Unix, которыми я пользуюсь каждый день, написано в согласии с этим стандартом, и они просто не могут правильно обрабатывать такие "сломанные" строки.
Давайте, например, через Python создадим такой файл со сломанными строками:
Сколько по-вашему в этом файле строк? Три? Давайте посмотрим, что об этом файле думает утилита wc , которая с флагом -l умеет считать количество строк в файле:
Упс! wc нашла только 2 строки!
Давайте создадим еще один файл:
И попробуем теперь склеить два созданных файла при помощи утилиты cat :
Название cat — это сокращение от "конкатенация", и никак не связано с котиками. А жаль.
И опять какой-то странный результат! В большинстве случаев это не то, чего вы бы ожидали, но вполне возможны ситуации, когда вам нужен именно такой результат. Именно поэтому утилита cat не может самостоятельно вставлять отсутствующие символы переноса строки, иначе это сделало бы её поведение неконсистентным.
Это только пара примеров, но многие другие утилиты, которые работают с текстом (например, diff , grep , sed ), имеют такие же проблемы. Собственно говоря, это даже не проблемы, а их задокументированное поведение.
Ещё доводы:
Самый простой способ перестать думать о пустых строках и начать жить — это настроить свой текстовый редактор или IDE на автоматическое добавление символа переноса строки в конец файлов:
- PyCharm и другие IDE JetBrains: Settings > Editor > General > Ensure an empty line at the end of a file on Save ;
- VS Code: "files.insertFinalNewline": true .
Для других редакторов смотрите настройку здесь.
Возможно, такая маленькая деталь, как перенос строки в конце файла и не кажется очень важной, а тема вообще кажется спорной, но боюсь, что у нас нет другого выбора, кроме как принять это правило за данность и просто выработать привычку (или настроить инструментарий) всегда ставить символ новой строки в любых текстовых файлах, даже если этого не требуется явно. Это считается распространённой хорошей практикой, и как минимум убережёт вас и ваших коллег от всяких неожиданных эффектов при работе с утилитами Unix.
В текстовом редакторе это выглядит как лишняя пустая строка в конце файла:
Читайте также: