Нет новой строки в конце файла


473

При этом git diffон говорит: «В конце файла нет новой строки» .

Хорошо, нет новой строки в конце файла. Подумаешь?

Каково значение сообщения и что оно пытается нам сказать?


11
Возможно, если у вас есть файл, который заканчивается без новой строки, и вы добавляете еще одну строку, git должен будет показать, что предыдущая последняя строка изменилась, так как он включает символ новой строки как часть строки?
nafg

Ответы:


458

Это указывает на то, что '\n'в конце файла нет новой строки (обычно это также CR или CRLF).

То есть, проще говоря, последний байт (или байты, если вы работаете в Windows) в файле не является новой строкой.

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

Обратите внимание, что это хороший стиль - всегда ставить символ новой строки как последний символ, если это разрешено форматом файла. Кроме того, например, для заголовочных файлов C и C ++ это требуется стандартом языка.


136
Из любопытства, можете ли вы объяснить, почему считается хорошим стилем всегда ставить символ новой строки в качестве последнего символа? Редактировать: нашел это обсуждение .
Пол Беллора

84
@PaulBellora Исторически это было решение, принятое стандартом языка C stackoverflow.com/a/729725/233098 Практически, потому что многие инструменты Unix требуют или ожидают его для правильного отображения stackoverflow.com/a/729795/233098 . С философской точки зрения, поскольку каждая строка в текстовом файле заканчивается символом конца строки - последняя строка не должна быть исключением. Думая об этом по-другому, давайте рассмотрим обратное. Если бы вместо «конца строки» был маркер «начала строки», вы бы не указали символ «начало строки» в первой строке?
Джо

29
@Joe Это не имеет особого смысла. Новая строка - это новая строка , т.е. разделитель между строками, а не конец строки. У нас нет символов начала строки, потому что они не нужны. У нас нет символов конца строки по той же причине.
Acjay

6
@ Acjay ​​Я утверждаю, что между "Разделителем между строками" и "концом строки" по сути лучше. Ни одно из представлений не является правильным или неправильным, просто один из способов взглянуть на это. Я предлагаю, чтобы мы продолжали использовать точку зрения, которая исторически практична, так как мы уже делаем это таким образом, и это имеет смысл, когда вы принимаете это. Последовательность важна. Нет необходимости ломать это в названии точки зрения «разделитель между строками».
Джо

17
@WORMSS «Новое для меня» - это не то же самое, что «новое соглашение». Это подобно открытию любого другого соглашения о программировании. Вы просто идете с этим. Вы можете отклониться, но вы только изолируете себя. (Или в этом случае, фактически ломая инструменты.) Подумайте о том, сколько других открыло какое-то соглашение Rails или 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

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


10

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

Предположим, например, что файл с символом перевода строки рассматривается как одна пустая строка. И наоборот, файл с длиной нулевых байтов на самом деле является пустым файлом с нулевыми строками. Это может быть подтверждено в соответствии с wc -lкомандой.

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


1
Почему меня понизили -2? Я указал не только на подтверждение того, что сформулировали другие ответы (т.е. стандартные инструменты на основе UNIX ожидают символ новой строки в качестве ограничителя для строк), но также на том, что нет способа отличить пустой файл от одной пустой строки, что абсолютно верно , Я специально ответил на оригинальный вопрос «Каково значение сообщения и что оно пытается нам сказать?»
Лесли Краузе

Я не отрицал вас, но этот ответ, по-видимому, специфичен для систем типов Unix в том смысле, что он применяется только тогда, когда символ новой строки является просто символом новой строки. Не ясно, что это применимо здесь. Кроме того, предупреждение кажется бесполезным, если файл состоит только из пустой строки. Однако я избегаю Stackoverflow, потому что люди часто понижают голос без объяснения причин.
user34660

9

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


Хороший вопрос в целом, но я не думаю, что это имеет смысл в контексте этого конкретного вопроса.
cst1992

@ cst1992 Ответы в Stackoverflow должны быть максимально полезными, а значит, они должны применяться ко всем возможностям. Вопрос короткий, и я не вижу, где он исключает возможность, которую я предложил.
user34660

7

Основная проблема заключается в том, что вы определяете строку и является ли последовательность символов конца строки онлайновой частью строки или нет. Редакторы на основе UNIX (например, VIM) или инструменты (например, Git) используют последовательность символов EOL в качестве ограничителя строки, поэтому она является частью строки. Это похоже на использование точки с запятой (;) в Си и Паскале. В Си точка с запятой завершает операторы, в Паскале - разделяет их.


4

Это на самом деле вызывает проблему, потому что в конце строки автоматически изменяются файлы, не внося в них никаких изменений. Смотрите этот пост для разрешения.

Git заменяет LF на CRLF


3

Исходные файлы часто объединяются инструментами (C, C ++: заголовочные файлы, Javascript: упаковщики). Если вы пропустите символ новой строки, вы можете ввести неприятные ошибки (когда последняя строка одного источника объединяется с первой строкой следующего исходного файла). Надеюсь, что все инструменты конкатата исходного кода вставляют новую строку между конкатенированными файлами в любом случае, но это не всегда так.

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


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

Вы можете написать весь свой проект в одну строку ... если вы не используете //комментарий стиля в середине кода.
Дуг Коберн

2

Ваш исходный файл, вероятно, не имел символа новой строки.

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

Что я пытался преодолеть эту проблему, чтобы открыть файл с редактора кода Visual Studio

Этот редактор четко показывает последнюю строку, и вы можете удалить строку по своему усмотрению.


0

Что бы это ни стоило, я столкнулся с этим, когда создал проект IntelliJ на Mac, а затем перенес проект на мою машину с Windows. Мне пришлось вручную открывать каждый файл и изменять настройки кодировки в правом нижнем углу окна IntelliJ. Вероятно, этого не случится с большинством, кто читает этот вопрос, но это могло бы сэкономить мне пару часов работы ...

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.