Немає нового рядка в кінці файлу


472

Виконуючи це, git diffвін говорить "Немає нового рядка в кінці файлу" .

Гаразд, немає нового рядка в кінці файлу. Яка велика справа?

Яке значення повідомлення і що він намагається нам сказати?


11
Можливо, якщо у вас є файл, який закінчується без нового рядка, і ви додаєте інший рядок, git повинен був би показати, що колишній останній рядок змінився, оскільки він включає символ нового рядка як частину рядка?
нафг

Відповіді:


458

Це вказує на те, що у вас немає нового рядка (зазвичай він '\n'називається CR або CRLF) в кінці файлу.

Тобто, просто кажучи, останній байт (або байти, якщо ви працюєте в Windows) у файлі - це не новий рядок.

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

Зауважте, що це гарний стиль завжди ставити новий рядок як останній символ, якщо це дозволяє формат файлу. Крім того, наприклад, для файлів заголовків C і C ++ це вимагається мовним стандартом.


136
З цікавості, чи можете ви пояснити, чому вважається хорошим стилем завжди ставити новий рядок як останнього персонажа? Редагувати: знайшов це обговорення .
Пол Беллора

84
@PaulBellora Історично, це було рішення, прийняте стандартом мови С stackoverflow.com/a/729725/233098 Практично, тому що багато інструментів Unix вимагають або очікують його для належного відображення stackoverflow.com/a/729795/233098 . По-філософськи, оскільки кожен рядок у текстовому файлі закінчується символом "кінець рядка" - останній рядок не повинен бути винятком. Думаючи про це інакше, давайте вивчимо зворотне. Якщо б замість "кінця рядка" був маркер "початковий рядок", чи не упустіть ви символ "початок рядка" на першому рядку?
Джо

29
@Joe Це не має великого сенсу. Новий рядок - це новий рядок , тобто роздільник між рядками, а не кінцевий рядок. У нас немає початку символів рядка, оскільки вони не потрібні. У нас немає символів кінця рядка з тієї ж причини.
acjay

6
@acjay Я стверджую, що між "роздільником між рядками" від "кінцевим рядком" по суті краще. Жоден погляд по суті не є правильним чи неправильним, лише один із способів поглянути на це. Я пропоную продовжувати використовувати точку зору, яка є історично практичною, оскільки ми вже робимо це так, і це має сенс, коли ви приймаєте це. Важлива послідовність. Немає необхідності переривати це на точку зору "роздільник між лініями".
Джо

17
@WORMSS "Нове для мене" - це не те саме, що "нова конвенція". Це подібно до виявлення будь-якого іншого способу програмування. Ви просто підете з цим. Ви могли відхилитися, але ви лише ізолюєте себе. (Або в цьому випадку насправді ламають інструменти.) Поміркуйте, скільки інших виявили певну конвенцію про рейлів чи PEP8, і наскільки послідовні ці громади залишилися в цілому, оскільки вони поступилися - незважаючи на те, що написали код, що суперечить.
Джо

100

Це не просто поганий стиль, це може призвести до несподіваної поведінки під час використання інших інструментів у файлі.

Ось test.txt:

first line
second line

Немає символу нової лінії в останньому рядку. Давайте подивимося, скільки рядків у файлі:

$ wc -l test.txt
1 test.txt

Можливо, це ви хочете, але в більшості випадків ви, напевно, очікували, що у файлі буде 2 рядки.

Крім того, якщо ви хочете комбінувати файли, він може не вести себе так, як ви очікували:

$ cat test.txt test.txt
first line
second linefirst line
second line

Нарешті, це зробило б ваші відмінності трохи шумнішими, якби ви додали новий рядок. Якщо ви додали третій рядок, він відображатиме редагування другого рядка, а також нове доповнення.


4
Результат "cat" в порядку, але параметр wc "-l, --lines" - це просто неправильно. Навіть у посібнику написано "надрукувати кількість ліній нового рядка", а не "надрукувати кількість ліній".
Неймовірний

І я навіть не можу відтворити це (wc та cat) за допомогою недавнього util linux (util-linux 2.34).
wget

1
@wget Я перебуваю на util-linux 2.34, і це може підтвердити, що ця відповідь описує поточну поведінку. Думаю, що ваш редактор додав символ "\ n".
Стефанос

29

Єдина причина полягає в тому, що в Unix історично було встановлено, що всі текстові файли, прочитані людиною, закінчуються новим рядком. У той час це уникало додаткової обробки під час відображення або приєднання текстових файлів, а також уникало поводження з текстовими файлами по-різному до файлів, що містять інші види даних (наприклад, необроблені бінарні дані, які не читаються людиною).

Через цю конвенцію багато інструментів тієї епохи очікують закінчення нового рядка, включаючи редактори тексту, різні інструменти та інші засоби обробки тексту. Mac OS X був побудований на BSD Unix, а Linux був розроблений як сумісний з Unix, тому обидві операційні системи успадкували однакові конвенції, поведінку та інструменти.

Windows не була розроблена як сумісна з Unix, тому вона не має тієї ж умовності, і більшість програмного забезпечення для Windows буде чудово справлятися з відсутностім нового рядка.

Але, оскільки Git був розроблений спочатку для Linux, і багато програмного забезпечення з відкритим кодом побудовано на сумісних з Unix системах, таких як Linux, Mac OS X, FreeBSD тощо, більшість спільнот з відкритим кодом та їх інструменти (включаючи мови програмування) продовжуються слідувати цим умовам.

Існують технічні причини, які мали сенс у 1971 році, але в цю епоху це здебільшого конвенція та підтримка сумісності з існуючими інструментами.


23

Якщо ви додасте новий рядок тексту в кінці наявного файлу, який ще не маєnewline character кінці, diff покаже старий останній рядок як змінений, навіть якщо концептуально це не було.

Це хоча б одна вагома причина додати newline character кінці.

Приклад

Файл містить:

A() {
    // do something
}

Hexdump:

00000000: 4128 2920 7b0a 2020 2020 2f2f 2064 6f20  A() {.    // do 
00000010: 736f 6d65 7468 696e 670a 7d              something.}

Тепер ви редагуєте його

A() {
    // do something
}
// Useful comment

Hexdump:

00000000: 4128 2920 7b0a 2020 2020 2f2f 2064 6f20  A() {.    // do 
00000010: 736f 6d65 7468 696e 670a 7d0a 2f2f 2055  something.}.// U
00000020: 7365 6675 6c20 636f 6d6d 656e 742e 0a    seful comment..

Git diff покаже:

-}
\ No newline at end of file
+}
+// Useful comment.

Іншими словами, це показує більшу різницю, ніж це було концептуально. Це показує, що ви видалили рядок }і додали рядок }\n. Це насправді те, що трапилося, але це не те, що концептуально сталося, тому може бути заплутаним.


2
Ми можемо записати те саме в іншому напрямку: Якщо ви видалите новий рядок в кінці існуючого файлу, який вже має кінець нового рядка, diff буде відображати старий останній рядок також як змінений, якщо концептуально його немає. Принаймні одна вагома причина видалити новий рядок наприкінці.
гентіян

3
@gentiane Ви плутаєте "новий рядок" (новий рядок) і "новий рядок" (1 або 2 символи, що обмежують кінець рядка)
minexew

@minexew Ні, гентіян - ні. Можливо, ви просто не усвідомлюєте, що "новий рядок" - це те саме, що "новий рядок".
Неймовірний січень

3
@TheincredibleJan У тому, як вони використовуються у відповіді, два терміни мають різні значення. Я не знаю, чи ти намагаєшся бути розумним дупою чи просто нерозумієш, що відбувається.
minexew

18

Це просто вказує, що в кінці файлу немає нового рядка. Це не катастрофа, це лише повідомлення, щоб зрозуміти, що немає такого, коли дивиться на розріз у командному рядку.


10

Причина, що ця конвенція застосована на практиці, полягає в тому, що в операційних системах, схожих на UNIX, символ нового рядка розглядається як термінатор лінії та / або межа повідомлення (це включає в себе трубопроводи між процесами, буферизацію рядків тощо).

Розглянемо, наприклад, що файл із символом нового рядка трактується як один, порожній рядок. І навпаки, файл довжиною нульових байтів - це фактично порожній файл із нульовими рядками. Це можна підтвердити відповідно доwc -l команди.

Загалом така поведінка є розумною, оскільки не було б іншого способу розрізнити порожній текстовий файл від текстового файлу з одним порожнім рядком, якби \nсимвол був просто роздільником рядків, а не строковим термінатором. Таким чином, дійсні текстові файли завжди повинні закінчуватися символом нового рядка. Винятком є ​​лише те, що текстовий файл має бути порожнім (без рядків).


1
Чому я знищений -2? Я вказав не лише на підтвердження того, що сказано в інших відповідях (тобто стандартні інструменти на основі UNIX очікують, що новий рядок буде термінатором для рядків), але й те, що немає можливості відрізнити порожній файл від одного порожнього рядка, що абсолютно вірно . Я спеціально відповів на початкове запитання "Яке значення повідомлення і що він намагається нам сказати?"
Леслі Краузе

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

9

Є одне, чого я не бачу в попередніх відповідях. Попередження про відсутність кінця рядка може бути попередженням, коли частина файлу врізана. Це може бути симптомом відсутніх даних.


Добре в цілому, але я не думаю, що це має сенс у контексті цього конкретного питання.
cst1992

@ cst1992 Відповіді в Stackoverflow повинні бути максимально корисними, це означає, що вони повинні застосовуватися до всіх можливостей. Питання коротке, і я не бачу, де це виключає можливість, яку я запропонував.
користувач34660

7

Основна проблема полягає в тому, що ви визначаєте рядок і чи є послідовність кінцевих символів частиною рядка чи ні. Редактори на основі UNIX (наприклад, VIM) або інструменти (наприклад, Git) використовують послідовність символів EOL як термінатор рядка, тому це частина рядка. Це схоже на використання крапки з комою (;) у C та Pascal. У C крапкою з комою закінчуються висловлювання, у Паскалі вони їх розділяють.


4

Це насправді викликає проблеми, оскільки закінчення рядків автоматично змінюються брудними файлами, не вносячи до них жодних змін. Дивіться цю публікацію для вирішення.

git замінює LF на CRLF


3

Вихідні файли часто об'єднуються інструментами (C, C ++: файли заголовків, Javascript: пакети). Якщо ви опустите символ нового рядка, ви можете ввести неприємні помилки (де останній рядок одного джерела з'єднаний з першим рядком наступного вихідного файлу). Будемо сподіватися, що всі інструменти, що містяться у фіксації вихідного коду, все-таки вставляють новий рядок між об'єднаними файлами, але це не завжди так.

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


1
В C / C ++ ви можете написати весь проект одним рядком. Не потрібно нових рядків.
Неймовірний

Ви можете написати весь проект одним рядком ... якщо ви не використовуєте //коментар стилю посередині коду.
Дуг Коберн

2

Ваш оригінальний файл, ймовірно, не мав символу нового рядка.

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

Що я намагався подолати це питання - це відкрити файл редактором коду візуальної студії

Цей редактор чітко показує останній рядок, і ви можете видалити рядок за своїм бажанням.


0

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

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