Почему текстовые файлы заканчиваются символом новой строки?
===========================================================

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

 

 Потому [что так стандарт POSIX определяет **строку**](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206%23tag_03_206):

 
>   **3.206 Линия**  Последовательность из нуля или более символов non- <newline> плюс завершающий символ <newline>.

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

  При работе с эмулятором терминала есть, по крайней мере, одно серьезное преимущество: все инструменты Unix ожидают этого соглашения и работают с ним. Например, при объединении файлов с помощью `cat` файл, оканчивающийся символом новой строки, будет иметь другой эффект, чем файл без:

 
```
*$ more a.txt
foo
$* **more** b.txt
bar*$ more c.txt
baz
$* **cat** {a,b,c}.txt
foo
barbaz
```
  И, как показано в предыдущем примере, при отображении файла в командной строке (например, через `more`) файл с новой строкой в конце приводит к правильному отображению. Неправильно завершенный файл может быть искажен (вторая строка).

  Для согласованности очень полезно следовать этому правилу - в противном случае потребуется дополнительная работа при работе со стандартными инструментами Unix.

 .i1a { width: 336px; height: 280px; }
 @media(min-width: 768px) { .i1a { width: 728px; height: 90px; } }
 @media(min-width: 992px) { .i1a { width: 468px; height: 60px; } }
 @media(min-width: 1083px) { .i1a { width: 728px; height: 90px; } }

 (adsbygoogle = window.adsbygoogle || []).push({});

---

 Подумайте об этом по-другому: если строки не заканчиваются символом новой строки, сделать такие команды, как `cat` полезными, гораздо сложнее: как создать команду для объединения файлов таким образом, чтобы

 1. каждый файл начинается с новой строки, что вам нужно в 95% случаев; но
2. это позволяет объединить последнюю и первую строку двух файлов, как в примере выше между `b.txt` и `c.txt`?

 Конечно, это решаемо, но вам нужно сделать использование `cat` более сложным (добавив позиционные аргументы командной строки, например, `cat a.txt --no-newline b.txt c.txt`), а теперь команду, а не каждого отдельного человека. Файл управляет тем, как он вставляется вместе с другими файлами. Это почти наверняка не удобно.

 ... Или вам нужно ввести специальный символ стража, чтобы пометить строку, которая должна быть продолжена, а не завершена. Что ж, теперь вы застряли в той же ситуации, что и в POSIX, за исключением перевернутого (продолжение строки, а не символ завершения строки).

 

---

 Теперь, в не POSIX-совместимых системах (в настоящее время это в основном Windows), суть спорная: файлы обычно не заканчиваются символом новой строки, и (неофициальное) определение строки может, например, быть "текстом, разделенным символами новой строки" (примечание Акцент).Это полностью верно.Однако для структурированных данных (например, программного кода) это делает синтаксический анализ минимально более сложным: обычно это означает, что анализаторы должны быть переписаны.Если синтаксический анализатор изначально был написан с учетом определения POSIX, то может быть проще изменить поток токенов, чем синтаксический анализатор - другими словами, добавить токен "искусственного перевода строки" в конец ввода.

 

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

GCC предупреждает об этом не потому, что не может обработать файл, а потому, что он должен быть частью стандарта.

(window.AdMediatorTag = window.AdMediatorTag || []).push({v: 1, el: 'admediator-54575-361975', c: 54575, b: 361975})

>  В стандарте C-языка Исходный файл, который не является пустым, должен заканчиваться символом новой строки, которому не следует сразу же следовать символ обратной косой черты.
> 
>  Так как это предложение "должно", мы должны исправить диагностическое сообщение для нарушения этого правила.
> 
>  Это в разделе 2.1.1.2 стандарта ANSI C 1989. Раздел 5.1.1.2 стандарта ISO C 1999 (и, возможно, также стандарта ISO C 1990).
> 
> 
> 

Ссылка: [Архив сообщений GCC/GNU](http://gcc.gnu.org/ml/gcc/2003-11/msg01568.html).

 

 Этот ответ является попыткой технического ответа, а не мнения.

  Если мы хотим быть пуристами POSIX, мы определяем строку как:

 
>   Последовательность из нуля или более символов non- <newline> плюс завершающий символ <newline>.
> 
> 

  Источник: [https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1\_chap03.html#tag\_03\_206](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206%23tag_03_206)

  Неполная строка как:

 
>   Последовательность из одного или нескольких символов non- <newline> в конце файла.
> 
> 

  Источник: [https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1\_chap03.html#tag\_03\_195](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195%23tag_03_195)

  Текстовый файл как:

 
>   Файл, содержащий символы, организованные в ноль или более строк. Строки не содержат символов NUL, и ни одна из них не может превышать длину {LINE\_MAX} байтов, включая символ <newline>. Хотя POSIX.1-2008 не делает различий между текстовыми файлами и двоичными файлами (см. Стандарт ISO C), многие утилиты производят только предсказуемый или значимый вывод при работе с текстовыми файлами. Стандартные утилиты, имеющие такие ограничения, всегда указывают "текстовые файлы" в своих разделах STDIN или INPUT FILES.
> 
> 

  Источник: [https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1\_chap03.html#tag\_03\_397](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_397%23tag_03_397)

  Строка как:

 
>   Непрерывная последовательность байтов, оканчивающаяся первым нулевым байтом и включающая его.
> 
> 

  Источник: [https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1\_chap03.html#tag\_03\_396](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396%23tag_03_396)

  Из этого мы можем сделать вывод, что единственное время, когда мы потенциально можем столкнуться с проблемами любого типа, это если мы имеем дело с концепцией строки файла или файла как текстового файла (поскольку текстовый файл является организацией с нулевым или больше строк, и известная нам строка должна заканчиваться символом <newline>).

  `wc -l filename` пример: `wc -l filename`.

  С `wc` руководства мы читаем:

 
>   Строка определяется как строка символов, разделенных символом <newline>.
> 
> 

  В браузерах, современных IDE и других интерфейсных приложениях нет проблем с пропуском EOL в EOF. Приложения будут правильно анализировать файлы. Это связано с тем, что не все операционные системы соответствуют стандарту POSIX, поэтому для инструментов non- ОС (например, браузеров) было бы нецелесообразно обрабатывать файлы в соответствии со стандартом POSIX (или любым стандартом ОС -l).

  В результате мы можем быть относительно уверены, что EOL в EOF практически не окажет негативного влияния на уровне приложений - независимо от того, работает ли он на ОС UNIX.

  На данный момент мы можем с уверенностью сказать, что пропуск EOL в EOF безопасен при работе с JS, HTML, CSS на стороне клиента. На самом деле, мы можем констатировать, что минимизация любого из этих файлов, не содержащих <newline>, безопасна.

  Мы можем сделать еще один шаг вперед и сказать, что в отношении NodeJS он также не может придерживаться стандарта POSIX, поскольку он может работать в non- POSIX-совместимых средах.

  Что же нам тогда осталось? Инструменты системного уровня.

  Это означает, что единственные проблемы, которые могут возникнуть, связаны с инструментами, которые прилагают усилия, чтобы привязать их функциональность к семантике POSIX (например, определение строки, как показано в `wc`).

  Тем не менее, не все оболочки будут автоматически придерживаться POSIX. Например, Bash не использует POSIX по умолчанию. Для этого есть переключатель: `POSIXLY_CORRECT`.

  Пищу для размышлений о ценности EOL, являющейся <newline>: <https://www.rfc-editor.org/old/EOLstory.txt>

  Оставаясь на пути к инструменту, для всех практических целей и задач, давайте рассмотрим это:

 
```
curl http://cdnjs.cloudflare.com/ajax/libs/AniJS/0.5.0/anijs-min.js -o x.js
curl http://cdnjs.cloudflare.com/ajax/libs/AniJS/0.5.0/anijs-min.js -o y.js

$ cat x.js y.js > z.js

-rw-r--r--  1 milanadamovsky   7905 Aug 14 23:17 x.js
-rw-r--r--  1 milanadamovsky   7905 Aug 14 23:17 y.js
-rw-r--r--  1 milanadamovsky  15810 Aug 14 23:18 z.js

  Как кто-то еще упомянул в этой теме: что если вы хотите cat два файла, вывод которых становится одной строкой вместо двух? Другими словами, cat делает то, что должна делать.

  man cat упоминает только чтение ввода до EOF, а не <newline>. Обратите внимание, что переключатель -n команды cat также выводит строку с ограничением non- <newline> (или неполную строку) в виде строки, поскольку счет начинается с 1 (по словам man).

 
  -n Нумерация выходных строк, начиная с 1.



  Теперь, когда мы понимаем, как POSIX определяет линию, это поведение становится неоднозначным или действительно совместимым с non-.

  Понимание цели и соответствия заданному инструменту поможет определить, насколько важно завершить файлы EOL. В C, C++, Java (JAR) и т.д.... некоторые стандарты будут предписывать новую строку для валидности - такого стандарта для JS, HTML, CSS не существует.

  Например, вместо использования wc -l filename awk '{x++}END{ print x}' filename, и будьте уверены, что выполнение задачи не будет поставлено под угрозу файлом, который мы, возможно, захотим обработать, который мы сделали не писать (например, библиотеку третьей стороны, такие как уменьшенная JS мы curl г) - если наше намерение не было действительно считать строки в совместимом смысле POSIX.

  Заключение

  В реальных случаях будет очень мало случаев, когда пропуск EOL в EOF для определенных текстовых файлов, таких как JS, HTML и CSS, будет иметь негативное влияние - если вообще будет. Если мы полагаемся на присутствие <newline>, мы ограничиваем надежность наших инструментов только теми файлами, которые мы создаем, и открываем себя для потенциальных ошибок, допущенных сторонними файлами.

  Мораль истории: Инженерные инструменты, у которых нет слабости полагаться на EOL в EOF.

  Не стесняйтесь публиковать варианты использования, так как они относятся к JS, HTML и CSS, где мы можем изучить, как пропуск EOL отрицательно сказывается.

 

Это может быть связано с [разница между](http://markmail.org/message/5jsn4epq7ad3vyvt):

 текстовый файл (каждая строка должна заканчиваться в конце строки)
 (нет истинных "строк", о которых нужно говорить, и длина файла должна быть сохранена)

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

Кроме того, редактор может проверить при загрузке, заканчивается ли файл в конце строки, сохраняет его в своей локальной опции "eol" и использует это при записи файла.

Несколько лет назад (2005) многие редакторы (ZDE, Eclipse, Scite,...) "забыли", что окончательный EOL, [который не был очень ценится](http://slashdot.org/comments.pl?sid=165492&cid=13808398).   

Не только это, но они неправильно интерпретировали этот окончательный EOL, так как "начали новую строку" и фактически начали отображать другую строку, как если бы она уже существовала.   

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

1 first line
2 middle line
3 last line
4

 

Некоторые инструменты ожидают этого. Например, wc ожидает следующее:

$ echo -n "Line not ending in a new line" | wc -l
0
$ echo "Line ending with a new line" | wc -l
1

 

В основном существует много программ, которые не будут обрабатывать файлы правильно, если они не получат окончательный EOL EOF.

GCC предупреждает вас об этом, поскольку он ожидается как часть стандарта C. (см. раздел 5.1.1.2)

  ["Нет новой строки в конце файла" предупреждение компилятора](/questions/8057/no-newline-at-end-of-file-compiler-warning)

 

Это происходит с самых первых дней использования простых терминалов. Новая строка char использовалась для запуска "сброса" переданных данных.

Сегодня новая строка char больше не требуется. Конечно, во многих приложениях все еще есть проблемы, если новая строка не существует, но я считаю, что ошибка в этих приложениях.

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

 

В дополнение к приведенным выше практическим соображениям меня не удивило бы, если бы создатели Unix (Thompson, Ritchie и др.) или их предшественники Multics поняли, что существует теоретическая причина использовать ограничители строк, а не разделители строк: С терминаторами строк вы можете кодировать все возможные файлы строк. С разделителями строк нет никакой разницы между файлом нулевых строк и файлом, содержащим одну пустую строку; оба они закодированы как файл, содержащий нулевые символы.

Итак, причины таковы:

 Потому что это определяет POSIX.
 Потому что некоторые инструменты ожидают этого или "плохо себя ведут" без него. Например, wc -l не будет считать окончательную "строку", если она не заканчивается новой строкой.
 Потому что это просто и удобно. В Unix cat работает, и он работает без осложнений. Он просто копирует байты каждого файла, без необходимости интерпретации. Я не думаю, что эквивалент DOS для cat. Использование copy a+b c приведет к объединению последней строки файла a с первой строкой файла b.
 Поскольку файл (или поток) нулевых строк можно отличить от файла одной пустой строки.

 

Отдельный прецедент: когда ваш текстовый файл контролируется версией (в данном случае специально под git, хотя это относится и к другим). Если содержимое добавлено в конец файла, тогда строка, которая была ранее последней строкой, будет отредактирована, чтобы включить символ новой строки. Это означает, что blame файл, чтобы узнать, когда эта строка была отредактирована последним, покажет добавление текста, а не фиксацию до того, что вы действительно хотели увидеть.

 

Также существует проблема с программированием с файлами, в которых нет новых строк: встроенный read Bash (я не знаю о других реализациях read) работает не так, как ожидалось:

printf $'foo\nbar' | while read line
do
    echo $line
done

Отпечатывается только foo! Причина в том, что когда read встречается с последней строкой, он записывает содержимое в $line`, но возвращает код выхода 1, поскольку он достиг EOF. Это нарушает цикл `while`, поэтому мы никогда не достигаем части `echo $line. Если вы хотите справиться с этой ситуацией, вы должны сделать следующее:

while read line || [ -n "${line-}" ]
do
    echo $line
done < <(printf $'foo\nbar')

```

То есть, `echo`, если `read` не удалось из-за непустой строки в конце файла. Естественно, в этом случае на выходе будет еще одна новая строка, которая не была на входе.

 

Предположительно просто, чтобы какой-то код синтаксического анализа ожидал, что он будет там.

Я не уверен, что считаю это "правилом", и это, безусловно, не то, что я придерживаюсь религиозно. Наиболее разумный код будет знать, как разбор текста (включая кодировки) по очереди (любой выбор окончаний строк), с или без новой строки в последней строке.

В самом деле, если вы закончите с новой строкой: существует ли (теоретически) пустая конечная строка между EOL и EOF? Один, чтобы обдумать...

 

>  Почему текстовые файлы заканчиваются символом новой строки?
> 
> 
> 

Также выражается многими, потому что:

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

---

Но это вызывает следующий вопрос:

>  Что должен делать код с текстовыми файлами без новой строки?
> 
> 
> 

* Самое главное -  **Не писать код, предполагающий, что текстовый файл заканчивается новой строкой**. Предполагая, что файл соответствует формату, он приводит к повреждению данных, хакерским атакам и сбоям. Пример:

```
// Bad code
while (fgets(buf, sizeof buf, instream)) {
  // What happens if there is no \n, buf[] is truncated leading to who knows what
  buf[strlen(buf) - 1] = '\0';  // attempt to rid trailing \n
  ...
}

```
* Если требуется конечный трейлинг `'\n'`, предупредите пользователя об его отсутствии и предпринятом действии. IOW, проверьте формат файла. Примечание. Это может включать ограничение на максимальную длину строки, кодировку символов и т.д.
* Четко определить документ, обработать код отсутствующего окончательного `'\n'`.
* Невозможно создать файл, в котором отсутствует окончание `'\n'`.

 

Я сам это задавался годами. Но сегодня я столкнулся с серьезной причиной.

Представьте файл с записью на каждой строке (например: файл CSV). И что компьютер записывал записи в конце файла. Но он внезапно упал. Джи была последней строкой? (не хорошая ситуация)

Но если мы всегда завершаем последнюю строку, тогда мы бы знали (просто проверьте, завершена ли последняя строка). В противном случае нам, вероятно, придется каждый раз отбрасывать последнюю строку, чтобы быть в безопасности.

 

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

Однако я верю, что правило выведено из компиляторов C, требующих новую строку. И как указано в  ["Нет новой строки в конце файла" , предупреждение компилятора](/questions/8057/no-newline-at-end-of-file-compiler-warning), #include не добавит новую строку.

 

 Здесь очень поздно, но я столкнулся с одной ошибкой в обработке файлов, которая произошла из-за того, что файлы не заканчивались пустым переводом строки. Мы обрабатывали текстовые файлы с помощью `sed` и `sed` опускал последнюю строку в выводе, что приводило к неправильной структуре json и отправляло остальную часть процесса в состояние сбоя.

  Все, что мы делали, было:

  Есть один пример файла: `foo.txt` с некоторым содержанием `json` внутри.

 
```
[{
    someProp: value
},
{
    someProp: value
}] <-- No newline here

```
  Файл был создан на машине вдов, и оконные скрипты обрабатывали этот файл с помощью команд powershall. Все хорошо.

  Когда мы обработали тот же файл, используя `sed` в командной `sed 's|value|newValue|g' foo.txt > foo.txt.tmp` в `sed 's|value|newValue|g' foo.txt > foo.txt.tmp` Вновь созданный файл был

 
```
[{
    someProp: value
},
{
    someProp: value

```
  и бум, он отказал остальным процессам из-за недопустимого JSON.

  Поэтому всегда полезно заканчивать свой файл пустой новой строкой.

 

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

Это может быть связано с этим? Флаг, который указывает, что файл готов к обработке.

 

Мне лично нравятся новые строки в конце файлов исходного кода.

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

 

ИМХО, это вопрос личного стиля и мнения.

В старые времена я не ставил эту новую строку. Сохраненный символ означает большую скорость через этот 14.4K модем.

Позже я поместил эту новую строку, чтобы было легче выбрать финальную строку с помощью shift + downarrow.