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


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

17 1128

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

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

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

источник: 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

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

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

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

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

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

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

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

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

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

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

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

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

пища для размышлений о значении EOL быть : 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 делает то, что он должен делать.

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

-N число выходных линий, начиная с 1.

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

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

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

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

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

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

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

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

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

существует также практическая проблема программирования с файлами, не имеющими новых строк в конце: 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 (Томпсон, Ричи и др.) или их предшественники 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 не добавит a новая строка.

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

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

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

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

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

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

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