Какой смысл добавлять новую строку в конец файла?

166

Некоторые компиляторы (особенно C или C ++) выдают предупреждения о:

No new line at end of file

Я думал, что это будет проблема только для C-программистов, но github отображает сообщение в коммит-представлении:

\ No newline at end of file

для файла PHP.

Я понимаю, что препроцессор объясняется в этой теме , но какое это имеет отношение к PHP? Это то же include()самое или это связано с темой \r\nпротив \n?

Какой смысл иметь новую строку в конце файла?

Филипп Стефан
источник
Дубликат SO: stackoverflow.com/questions/729692/…
AlikElzin-kilaka
2
Разозлить людей.
Андрей
4
Если вы catиспользуете файл, следующая подсказка будет добавлена ​​к последней «строке», если она не заканчивается новой строкой.
Аарон Франке

Ответы:

188

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

Текстовый файл под UNIX, состоит из ряда линий , каждая из которых заканчивается с новой строки ( \n). Поэтому файл, который не является пустым и не заканчивается новой строкой, не является текстовым файлом.

Утилиты, которые должны работать с текстовыми файлами, могут плохо работать с файлами, которые не заканчиваются символом новой строки; например, исторические утилиты Unix могут игнорировать текст после последней новой строки. Утилиты GNU придерживаются политики приличного поведения с нетекстовыми файлами, как и большинство других современных утилит, но вы все равно можете столкнуться со странным поведением с файлами, в которых отсутствует финальный символ новой строки¹.

При использовании GNU diff, если один из сравниваемых файлов заканчивается новой строкой, но не другой, следует обратить внимание на этот факт. Так как diff ориентирован на строки, он не может указать это путем сохранения новой строки для одного из файлов, но не для других - новые строки необходимы, чтобы указать, где каждая строка в файле diff начинается и заканчивается. Так что diff использует этот специальный текст, \ No newline at end of fileчтобы отличить файл, который не заканчивался переводом строки, от файла, который сделал.

Кстати, в контексте C исходный файл аналогичным образом состоит из серии строк. Точнее говоря, единица перевода рассматривается в реализации, определенной как последовательность строк, каждая из которых должна заканчиваться символом новой строки ( n1256 §5.1.1.1). В системах Unix отображение является простым. В DOS и Windows каждая последовательность CR LF ( \r\n) отображается на новую строку ( \nэто то, что всегда происходит при чтении файла, открытого как текст в этих ОС). Есть несколько ОС, которые не имеют символа новой строки, но вместо этого имеют записи фиксированного или переменного размера; в этих системах отображение из файлов на источник C вводит\nв конце каждой записи. Хотя это не имеет непосредственного отношения к unix, это означает, что если вы скопируете исходный файл C, в котором отсутствует заключительный символ новой строки, в систему с текстовыми файлами на основе записей, а затем скопируете ее обратно, вы либо получите неполное последняя строка усекается при первоначальном преобразовании или добавляется дополнительная строка при обратном преобразовании.

¹ Пример: вывод сортировки GNU всегда заканчивается новой строкой. Так что, если в файле fooотсутствует последняя строка, вы обнаружите, что он sort foo | wc -cсообщает еще на один символ больше, чем cat foo | wc -c.

жилль
источник
Относительно "... серии строк, каждая из которых должна заканчиваться символом новой строки (n1256 §5.1.1.1)" -> При повторном просмотре более свежего C11dr N1570 не нашел поддержки для этого, кроме, возможно,: «Исходный файл, который не является пустым, должен заканчиваться символом новой строки, которому не должен предшествовать символ обратной косой черты, прежде чем произойдет любое такое соединение». §5.1.1.2 2, но это, по-видимому, ограничивается спецификациями сращивания.
Чукс
@chux Это предложение присутствует и в n1256. Последняя строка должна заканчиваться символом новой строки. Строки, которые не являются последней строкой, очевидно, также должны заканчиваться символом новой строки, чтобы указать, что эта строка заканчивается и начинается следующая строка. Таким образом, каждая строка должна заканчиваться символом новой строки.
Жиль
Хммм, мне эта строка "" Исходный файл ... происходит слияние. "Может быть ограничена тем, как соображения по сращиванию, а не файлы в целом. И все же я вижу, как можно посмотреть иначе. Возможно, я поищу пост что фокусируется на этом.
Chux
> «Так что diff использует этот специальный текст \ Нет новой строки в конце файла, чтобы отличить файл, который не заканчивался новой строкой, от файла, который сделал». Git показывает этот текст не только при сравнении файлов. Но даже когда новый файл добавлен в git. Так что этот аргумент недействителен, я полагаю.
Виктор Кругликов
> «Утилиты, которые должны работать с текстовыми файлами, могут не справиться с файлами, которые не заканчиваются символом новой строки». Я не думаю, что дело в git - заботиться о таких низкоуровневых проблемах, как пропущенные \ n из-за POSIX требования. Я думаю, что если git показывает это сообщение, причина должна быть в проблемах контроля версий.
Виктор Кругликов
42

Не обязательно причина, но практическое следствие того, что файлы не заканчиваются новой строкой:

Подумайте, что произойдет, если вы захотите обработать несколько файлов с помощью cat. Например, если вы хотите найти слово fooв начале строки в 3 файлах:

cat file1 file2 file3 | grep -e '^foo'

Если первая строка в file3 начинается с foo, но file2 не имеет финала \nпосле своей последней строки, это вхождение не будет найдено grep, потому что последняя строка в file2 и первая строка в file3 будут рассматриваться grep как единый линия.

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

Серхио Акоста
источник
Но стоит ли заботиться о конкатенации файлов?
Виктор Кругликов
Разве это не значит, что вы должны просто поставить '\n'операцию на кошку ...
Эндрю
3
Это все равно, что сказать: «Иногда я складываю строки, которые имеют \nили пропускают на концах, поэтому, чтобы все было согласованно, я всегда ставлю \n _____на обоих концах свои строки». Что ж, нет, правильная вещь - это обрезать строки и затем соединять их должным образом.
Андрей
16

Есть два аспекта:

  1. Существуют / были некоторые компиляторы Си, которые не могут проанализировать последнюю строку, если она не заканчивается новой строкой. Стандарт C определяет, что файл C должен заканчиваться символом новой строки (C11, 5.1.1.2, 2.) и что последняя строка без символа новой строки приводит к неопределенному поведению (C11, J.2, 2-й элемент). Возможно, по историческим причинам, потому что какой-то поставщик такого компилятора был частью комитета, когда был написан первый стандарт. Таким образом, предупреждение от GCC.

  2. diffпрограммы (например, используемые git diffGitHub и т. д.) показывают различия между строками между файлами. Они обычно печатают сообщение, когда только один файл заканчивается новой строкой, потому что иначе вы не увидите этой разницы. Например, если единственное различие между двумя файлами - это наличие последнего символа новой строки, без подсказки будет выглядеть так, как если бы оба файла были одинаковыми, когда diffи при cmpвозврате кода выхода получится неравный успех и контрольные суммы файлов (например, через md5sum) не совпадают.

maxschlepzig
источник
имеет смысл с программой diff
Thamaraiselvam
Похоже, различия должны быть просто умнее.
Андрей
@ Андрей, нет, это не так. diffожидается, что распечатает различия, если они есть. И если один файл имеет символ новой строки в качестве последнего символа, а другой - нет, то эта разница должна быть как-то заметна в выводе.
maxschlepzig
Ваше последнее утверждение верно. Однако, для начала просмотра diff не обязательно отображать \n«новые строки» ( ), вместо этого он может просто отображать «новые строки».
Андрей
10

\ No newline at end of fileВы получаете от GitHub появляется в конце патча (в diffформате , смотрите примечание в конце раздела «Унифицированный формат»).

Компиляторам не важно, есть ли новая строка или нет в конце файла, но git(и утилиты diff/ patch) должны принять это во внимание. Есть много причин для этого. Например, если вы забудете добавить или удалить символ новой строки в конце файла, это изменит его хэш-сумму ( md5sum/ sha1sum). Кроме того, файлы не всегда являются программами, и финал \nможет иметь какое-то значение.

Примечание : по поводу предупреждения от компиляторов C, я думаю, они настаивают на заключительном переводе строки для целей обратной совместимости. Очень старые компиляторы могут не принять последнюю строку, если она не заканчивается \n(или другой системно-зависимой последовательностью символов конца строки).

Стефан Хименес
источник
7
«Я думаю, они настаивают на заключительном переводе строки для целей обратной совместимости» - Нет, они настаивают на этом, потому что стандарт C предписывает это.
MestreLion
1
@MestreLion C требует заключительной новой строки для исходного кода C (C11 §5.1.1.2 2). Обратите внимание, что для ввода / вывода текстового файла C имеет «Требуется ли для последней строки завершающий символ новой строки, определяется реализацией». §7.21.2 2
chux
Кто использует очень старые компиляторы? Прекратите использовать их.
Андрей
1
@MestreLion: И почему, как вы думаете, стандарт C предписывает это…
Стефан Гименес
@ StéphaneGimenez: согласованность, лучшая совместимость и совместимость между различными ОС (POSIX также определяет строки, заканчивающиеся на '\ n')
MestreLion
4

Есть также смысл сохранять историю изменений. Если файл заканчивается без символа новой строки, то добавление чего-либо в конец файла будет рассматриваться утилитами diff как изменение последней строки (потому что \nона добавляется к нему).

Это может привести к нежелательным результатам с такими командами, как git blameи hg annotate.

Хосам Али
источник
Похоже, различия просто должны быть умнее.
Андрей
1
Разные инструменты умны. Они замечают тонкие изменения в файле (что важно, потому что это неизбежно изменит хеш файла). И GNU diff, и git diff допускают -wвозможность игнорировать изменения пробелов при выводе данных для людей.
Joeytwiddle
4

POSIX, это набор стандартов, определенных IEEE для обеспечения совместимости между операционными системами.

Одним из них является определение «строки», представляющей собой последовательность из нуля или более не-символов плюс завершающий символ новой строки.

Таким образом, чтобы эта последняя строка была распознана как фактическая «строка», она должна иметь завершающий символ новой строки.

Это важно, если вы зависите от инструментов ОС, чтобы сказать количество строк или разбить / помочь разобрать ваш файл. Учитывая, что PHP является языком сценариев, его вполне возможно, особенно в его ранние времена или даже сейчас (я понятия не имею / постулирую), что у него были такие зависимости от ОС.

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

user3379747
источник