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

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

17 ответов


потому что это как стандарт POSIX определяет строка:

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

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

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

$ more a.txt
foo$ more b.txt
bar
$ more c.txt
baz
$ cat *.txt
foobar
baz

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

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

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


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

GCC предупреждает об этом не потому, что это не могу обработать файл, но потому что это до в рамках стандарта.

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

поскольку это предложение "shall", мы должны выдать диагностическое сообщение о нарушении этого правила.

Это в разделе 2.1.1.2 стандарта ANSI C 1989. Раздел 5.1.1.2 стандарта ISO c 1999 (и, возможно, также стандарта ISO C 1990).

ссылки: почтовый архив GCC/GNU.


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

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

последовательность нулевых или более символов, отличных от символов , плюс завершающий символ .

источник: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206

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

последовательность одного или нескольких символов, отличных от в конце файла.

источник: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195

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

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

источник: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_397

строку:

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

источник: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396

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

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

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

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

каковы последствия для JavaScript, HTML и CSS-файлов, тогда они являются текст файлов?

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

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

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

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

С чем мы остаемся тогда? Инструменты системного уровня.

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

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

пища для размышлений о значении EOL being : http://www.rfc-editor.org/EOLstory.txt

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

давайте работать с файлом, который не имеет EOL. На момент написания этого файла в этом примере это мини-JavaScript без EOL.

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 размер файла-это точно сумма его отдельных частей. Если конкатенация файлов JavaScript является проблемой для JS-файлов, тем более уместно было бы начать каждый файл JavaScript с запятой.

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

на man of cat упоминает только чтение ввода до EOF, а не . Обратите внимание, что -n переключатель cat также распечатает не - завершенная строка (или неполная строка) в качестве строка - поскольку счет начинается с 1 (по man.)

-N пронумеруйте выходные линии, начиная с 1.

теперь, когда мы понимаем, как POSIX определяет строка, это поведение становится двусмысленным или действительно несовместимым.

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

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

вывод

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

мораль истории: инженер tooling который не имеет слабость полагаться на EOL на EOF.

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


Это может быть связано с разницу между:

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

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

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

несколько лет назад (2005), многие редакторы (ZDE, Eclipse, Scite,...) неужели "забыли", что окончательный EOL,что было не очень оценено.
Не только это, но они неправильно интерпретировали этот окончательный 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 видимо)

" нет новой строки в конце файла " предупреждение компилятора


это происходит с самых ранних дней, когда использовались простые терминалы. Символ новой строки использовался для запуска "промывки" передаваемых данных.

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

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


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


существует также практическая проблема программирования с файлами, в которых отсутствуют новые строки в конце: the 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? Один обдумывать...


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

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

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

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

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

  1. многие программы не ведут себя хорошо или терпят неудачу без него.

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

  3. программы редко запретить финал '\n' (Я не знаю).


и все же напрашивается следующий вопрос:

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

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

    // 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] = '';  // attempt to rid trailing \n
      ...
    }
    
  2. если окончательный трейлинг '\n' необходимо, предупредить пользователя о его отсутствии и принятых мерах. IOWs, проверьте формат файла. Примечание: это может включать в себя ограничения на максимальную длину строки, кодировки и т. д.

  3. определите ясно, документ, обработку кода недостающего финала '\n'.

  4. Не делайте, насколько это возможно,создать файлу не хватает окончания '\n'.


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

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

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


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

однако я считаю, что правило получено из компиляторов C, требующих новой строки. И как указано на "нет новой строки в конце файла" предупреждение компилятора, #include не добавит новая строка.


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

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


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

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


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

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

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