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

1235

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

  • 23
    просто придурок. это не «новая строка» в конце файла. Это «разрыв строки» в конце последней строки. Также, посмотрите лучший ответ на связанный вопрос: stackoverflow.com/questions/16222530/…
  • 288
    Просто чтобы придираться еще, он на самом деле не написал «новую строку», он написал «новую строку», что правильно.
Показать ещё 9 комментариев
Теги:
file
newline
text-files

18 ответов

1171
Лучший ответ

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

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.


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

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

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

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


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

  • 1
    Хотя в настоящее время исправление довольно непрактично, ясно, что POSIX допустил ошибку при определении линии - в качестве доказательства по количеству вопросов, касающихся этой проблемы. Строка должна быть определена как ноль или более символов, оканчивающихся на <eol>, <eof> или <eol> <eof>. Сложность парсера не является действительной проблемой. Сложность, где это возможно, должна быть перенесена из головы программистов в библиотеку.
  • 3
    @DougCoburn В этом ответе шла исчерпывающая техническая дискуссия, объясняющая, почему это не так и почему POSIX поступил правильно. К сожалению, эти комментарии были, по-видимому, недавно удалены чрезмерно усердным модератором. Вкратце, речь идет не о разборе сложности; скорее, из-за вашего определения гораздо сложнее создавать такие инструменты, как cat , которые будут полезны и последовательны.
Показать ещё 13 комментариев
268

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

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

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

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

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

Ссылка: Архив сообщений GCC/GNU.

  • 9
    пожалуйста, напишите хорошие программы, которые либо позволяют вставлять эту новую строку, где это необходимо при обработке, либо способны правильно обрабатывать «пропущенные» ... которые, по сути, не отсутствуют
  • 4
    @BilltheLizard, Какие есть примеры «У некоторых программ возникают проблемы с обработкой последней строки файла, если он не завершен переводом строки» ?
Показать ещё 8 комментариев
98

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

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

Последовательность из нуля или более символов и lt; новых символов > плюс символ завершающей < новой строки.

Источник: 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} байтов, включая символ <newline> . Хотя 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

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

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

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

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

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

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

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

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

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

Что мы оставили с этим? Инструмент уровня системы.

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

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

Пища для размышлений о значении EOL, которое <newline> : 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, а не <newline> . Обратите внимание, что переключатель -n cat также распечатает строку с завершающей линией (или неполной линией) в качестве строки - это то, что счетчик начинается с 1 (согласно man.)

-n Число строк вывода, начиная с 1.

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

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

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

Заключение

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

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

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

  • 3
    Ссылка на rfc-editor должна быть rfc-editor.org/old/EOLstory.txt
  • 0
    POSIX не помечен в вопросе ... Что насчет окончаний строк в MVS / OS? или окончания строк MS-DOS? Между прочим, все известные системы posix допускают текстовые файлы без окончательного окончания строки (не найдено ни одного случая для системы подачи заявок, соответствующей posix, в которой «текстовый файл» имеет специальную обработку в ядре для вставки правильной новой строки в случае, если она не имеет Это)
Показать ещё 1 комментарий
59

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

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

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

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

Несколько лет назад (2005) многие редакторы (ZDE, Eclipse, Scite,...) "забыли", что окончательный EOL, который не был очень ценится.
Не только это, но они неправильно интерпретировали этот окончательный EOL, так как "начали новую строку" и фактически начали отображать другую строку, как если бы она уже существовала.
Это было прекрасно видно с помощью "правильного" текстового файла с хорошо подобранным текстовым редактором, например, vim, по сравнению с открытием его в одном из вышеупомянутых редакторов. Он отобразил дополнительную строку под реальной последней строкой файла. Вы видите что-то вроде этого:

1 first line
2 middle line
3 last line
4
  • 9
    +1. Я нашел этот ТАК вопрос, испытывая эту самую проблему. Eclipse очень раздражает, когда показывает эту «фальшивую» последнюю строку, и если я удаляю ее, то жалуется git (и все другие инструменты Unix, ожидающие EOL). Также обратите внимание, что это происходит не только в 2005 году: в Eclipse 4.2 Juno эта проблема все еще существует.
  • 0
    @MestreLion, продолжение на stackoverflow.com/questions/729692/…
40

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

$ echo -n "Line not ending in a new line" | wc -l
0
$ echo "Line ending with a new line" | wc -l
1
  • 18
    Я бы не сказал «некоторые», я говорю, что большинство инструментов ожидают, что для текстовых файлов, если не все. cat, git, diff, wc, grep, sed ... список огромный
  • 0
    Может быть, можно сказать, что wc этого не ожидает , поскольку он просто работает в POSIX-определении «line», в отличие от интуитивного понимания «line» большинством людей.
Показать ещё 2 комментария
18

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

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

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

  • 5
    GCC не способен обработать файл, он должен выдавать предупреждение как часть стандарта C.
  • 0
    Хороший вопрос, обновил с соответствующим разделом)
Показать ещё 1 комментарий
12

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

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

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

  • 12
    в настоящее время новая строка в EOF для текстовых файлов может не требоваться, но это полезное соглашение, которое заставляет большинство инструментов Unix работать вместе с согласованными результатами. Это совсем не ошибка.
  • 10
    Многие из нас вообще не используют инструменты Unix, и нам все равно.
Показать ещё 6 комментариев
11

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

10

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

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

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

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

9

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

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

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

  • 11
    Это не правило, это соглашение: строка - это то, что заканчивается концом строки . Так что нет, между EOL и EOF нет «пустой финальной линии».
  • 3
    @MestreLion: Но рассматриваемый символ не называется «конец строки», он называется «перевод строки» и / или «перевод строки». Разделитель строк, а не разделитель строк. И результат - окончательная пустая строка.
Показать ещё 6 комментариев
7

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

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

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

  • Даже в программах, которые хорошо обрабатывают файл, нет конца '\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'.

6

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

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

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

3

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

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

3

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

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

0

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

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

-3

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

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

-8

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

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

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

  • 4
    Я серьезно лох на последней линии, ха-ха

Ещё вопросы

Сообщество Overcoder
Наверх
Меню