Який сенс додавати новий рядок до кінця файлу?


166

Деякі компілятори (особливо C або C ++) попереджають про:

No new line at end of file

Я думав, що це буде проблема лише для C-програмістів, але github відображає повідомлення у вікні перегляду:

\ No newline at end of file

для файлу PHP

Я розумію, що в цьому потоці пояснюється справа препроцесора , але що це стосується PHP? Це те include()саме або це пов'язано з темою \r\nvs \n?

Який сенс у новому рядку в кінці файлу?



2
Щоб розлютити людей.
Андрій

3
Якщо ви catфайл, наступний запит буде доданий до остаточного "рядка", якщо він не закінчується новим рядком.
Аарон Франке

Відповіді:


185

Мова не йде про додавання додаткового нового рядка в кінці файлу, це не про видалення нового рядка, який повинен бути там.

Текстовий файл під UNIX, складається з ряду ліній , кожна з яких закінчується з нового рядка ( \n). Файл, який не порожній і не закінчується новим рядком, тому не є текстовим файлом.

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

Якщо GNU відрізняється, якщо один із файлів, що порівнюються, закінчується новим рядком, а не інший, слід обережно зазначити цей факт. Оскільки diff є орієнтованим на рядки, він не може вказати це, зберігаючи новий рядок для одного з файлів, але не для інших - в нових рядках необхідно вказати, де починається і закінчується кожен рядок у файлі diff . Так diff використовує цей спеціальний текст \ No newline at end of fileдля розмежування файлу, який не закінчився в новому рядку від файлу, який був.

До речі, у контексті С вихідний файл аналогічно складається із серії рядків. Точніше, блок перекладу розглядається у реалізації, визначеному як серія рядків, кожен з яких повинен закінчуватися символом нового рядка ( n1256 §5.1.1.1). У системах Unix відображення прямолінійне. У DOS та Windows кожна послідовність CR LF ( \r\n) відображається у новому рядку ( \nце завжди відбувається під час читання файлу, відкритого як текст на цих ОС). Є кілька ОС, які не мають символу нового рядка, а натомість мають записи фіксованого чи змінного розміру; для цих систем відображення від файлів до джерела С вводить a\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

@chux Це речення також присутнє в n1256. Останній рядок повинен закінчуватися символом нового рядка. Рядки, які не є останнім рядком, очевидно, також повинні закінчуватися символом нового рядка, щоб вказати, що цей рядок закінчується і починається наступний рядок. Таким чином, кожен рядок повинен закінчуватися символом нового рядка.
Жиль

Гммм, для мене цей рядок "" Вихідний файл ... відбувається сплайсинг ". Може обмежуватися тим, як розглядаються питання щодо сплайсингу, а не файли взагалі. Але я бачу, як можна було б переглянути інше. Можливо, я буду шукати публікацію що фокусується на цьому.
chux

> "Так diff використовує цей спеціальний текст \ Немає нового рядка в кінці файлу, щоб відрізняти файл, який не закінчився в новому рядку, від файлу, який був." Git показує цей текст не лише при порівнянні файлів. Але навіть коли новий файл додається до git. Тож цей аргумент неправдивий, гадаю.
Віктор Кругліков

> "Утиліти, які повинні працювати над текстовими файлами, можуть не впоратися з файлами, які не закінчуються новим рядком". Я не думаю, що це справа git, щоб піклуватися про такі проблеми низького рівня, як відсутні \ n через POSIX вимоги. Я думаю, що якщо git показує це повідомлення, причина повинна полягати в проблемах управління джерелами .
Віктор Кругліков

41

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

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

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

Якщо перший рядок у file3 починається з foo, але файл2 не має остаточного \nпісля останнього рядка, це виникнення не було б знайдено grep, оскільки останній рядок у file2 та перший рядок у file3 бачиться grep як єдиний рядок.

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


Але чи це справа git - турбота про з'єднання файлів?
Віктор Кругліков

Хіба це не має підстави вважати, що ви повинні просто ввести '\n'операцію з котами ...
Андрій

3
Це як сказати: "Іноді я додаю рядки разом, які мають \nпробіли або пробіли на кінцях, тому для того, щоб все було послідовно, я завжди кладу \n _____в обидва кінці моїх рядків". Ну, ні, правильне, що потрібно зробити, це обрізати свої струни, а потім об'єднати їх належним чином.
Андрій

16

Є два аспекти:

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

  2. diffпрограми (наприклад, використовувані git diff, github тощо) показують різницю між рядками між файлами. Зазвичай вони друкують повідомлення, коли лише один файл закінчується новим рядком, оскільки в іншому випадку ви не побачили б цієї різниці. Наприклад, якщо єдиною різницею між двома файлами є наявність останнього символу нового рядка, без натяку це виглядатиме так, що обидва файли були однаковими, коли diffі cmpповертати вихідний код неоднаковий успіх та контрольні суми файлів (наприклад, через md5sum) не відповідають.


має сенс у програмі diff
Thamaraiselvam

Звуки розрізнень просто повинні бути розумнішими.
Андрій

@Andrew, ні, це не так. 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

POSIX - це набір стандартів, визначених IEEE для підтримки сумісності між операційними системами.

Одне з яких - це визначення "рядка", що є послідовністю нуля або більше не символів плюс закінчуючий символ нового рядка.

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

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

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


3

Існує також сенс зберігання різної історії. Якщо файл закінчується без символу нової лінії, то додавання чого-небудь до кінця файлу розглядатиметься різними утилітами як зміна останнього рядка (тому \nщо до нього додається).

Це може призвести до небажаних результатів за допомогою таких команд, як git blameі hg annotate.


Звуки, що відрізняються, просто повинні бути розумнішими.
Андрій
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.