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

1470

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

Уилл Робертсон
источник
30
просто придурок. это не «новая строка» в конце файла. Это «разрыв строки» в конце последней строки. Кроме того, см. Лучший ответ на связанный вопрос: stackoverflow.com/questions/16222530/…
gcb
346
Просто чтобы придираться еще, он на самом деле не написал «новую строку», он написал «новую строку», и это правильно.
Синдренм
5
не знакомо, но мне интересно, потому что число случаев, когда этот лишний перевод строки ломает вещи, слишком велико для моих вкусов
tobibeer
2
В настоящее время я использую потоки Node.js для построчного анализа текстовых данных, и отсутствие терминального разрыва строки раздражает, так как я должен добавить дополнительную логику, когда закончится входная сторона потока / закрыт, чтобы гарантировать, что последняя строка обработана.
Марк К Коуэн
23
Путь Unix рассматривает свое общее поведение в конце файлов выглядит следующим образом : \ п символов не начинаются строки; вместо этого они заканчивают их. Таким образом, \ n является разделителем строки, а не разделителем строки. Первая строка (как и все строки) не нуждается в \ n для ее запуска. Последняя строка (как и все строки) нуждается в \ n для ее завершения. \ N в конце файла не создает дополнительную строку. Однако иногда текстовые редакторы добавляют туда видимую пустую строку. Даже Emacs делает это, по желанию .
MarkDBlackwell

Ответы:

1383

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

3.206 Линия
Последовательность из нуля или более не-символов <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, то может быть проще изменить поток токенов, чем синтаксический анализатор - другими словами, добавить токен «искусственной новой строки» в конец ввода.

Конрад Рудольф
источник
9
Хотя в настоящее время исправление довольно непрактично, ясно, что POSIX допустил ошибку при определении линии - в качестве доказательства по количеству вопросов, касающихся этой проблемы. Строка должна быть определена как ноль или более символов, оканчивающихся на <eol>, <eof> или <eol> <eof>. Сложность парсера не является действительной проблемой. Сложность, где это возможно, должна быть перенесена из головы программистов в библиотеку.
Дуг Кобурн
23
@DougCoburn Этот ответ имел исчерпывающее техническое обсуждение, объясняющее, почему это не так, и почему POSIX поступил правильно. К сожалению, эти комментарии были, по-видимому, недавно удалены чрезмерно усердным модератором. Вкратце, речь идет не о разборе сложности; скорее, из-за вашего определения намного сложнее создавать инструменты, такие как catполезные и непротиворечивые.
Конрад Рудольф
8
@Leon Правило POSIX - все о сокращении числа крайних случаев. И это так красиво. На самом деле я немного растерялся, потому что люди не понимают этого: это самое простое, самосогласованное определение линии.
Конрад Рудольф
6
@BT Я думаю, вы предполагаете, что причиной этого решения является мой пример более удобного рабочего процесса . Это не так, это просто следствие. Причина в том , что правило POSIX является правилом , что это самый простой, и что делает обработку строк в парсер простой. Единственная причина, по которой мы ведем дискуссию, заключается в том, что Windows делает это по-другому и, как следствие, существует множество инструментов, которые не работают с файлами POSIX. Если бы все делали POSIX, не было бы никаких проблем. Тем не менее, люди жалуются на POSIX, а не на Windows.
Конрад Рудольф
7
@BT Я имею в виду только Windows, чтобы указать на случаи, когда правила POSIX не имеют смысла (другими словами, я бросил вам кость). Я более чем счастлив никогда больше не упоминать об этом в этой дискуссии. Но тогда ваше утверждение имеет еще меньший смысл: на платформах POSIX просто не имеет смысла обсуждать текстовые файлы с различными соглашениями об окончании строк, потому что нет причин их создавать. В чем преимущество? Там буквально нет. - В общем, я действительно не понимаю ненависти, которую вызывает этот ответ (или правило POSIX). Честно говоря, это совершенно иррационально.
Конрад Рудольф
282

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

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

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

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

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

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

Билл Ящерица
источник
17
пожалуйста, напишите хорошие программы, которые либо позволят вставлять эту новую строку там, где это необходимо во время обработки, либо способны правильно обрабатывать «пропущенные» ... которые, по сути, не пропущены
tobibeer
4
@BilltheLizard, Какие примеры: «У некоторых программ возникают проблемы с обработкой последней строки файла, если он не завершен переводом строки» ?
Пейсер
4
@Pacerier wc -lне будет считать последнюю строку файла, если он не завершен новой строкой. Также, catобъединит последнюю строку файла с первой строкой следующего файла в одну, если последняя строка первого файла не завершена новой строкой. Практически любая программа, которая ищет новые строки в качестве разделителя, может испортить это.
Билл Ящерица
2
@BilltheLizard, я имею в виду wc, уже упоминалось ....
Pacerier
2
@BilltheLizard, мой плохой, чтобы уточнить: каковы некоторые примеры программ, у которых есть проблемы с обработкой последней строки файла, если он не завершен новой строкой (кроме тех, которые уже были упомянуты в потоке как catи wc)?
Пейсер
116

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

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

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

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

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

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

Источник: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_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_396

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

Дело в точке: wc -l filename.

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

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

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

В браузерах, современных IDE и других интерфейсных приложениях нет проблем с пропуском 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>: https://www.rfc-editor.org/old/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Из catтолько упоминает чтение входных данных до EOF, а не <строки>. Обратите внимание, что -nпереключатель catтакже выведет не завершенную <(новую строку>) строку (или неполную строку ) в виде строки, поскольку счет начинается с 1 (в соответствии с man.)

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

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

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

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

Вывод

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

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

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

Милан Адамовский
источник
2
POSIX не помечен в вопросе ... Что насчет окончаний строк в MVS / OS? или окончания строк MS-DOS? Между прочим, все известные системы posix допускают текстовые файлы без окончательного окончания строки (не найдено ни одного случая для системы подачи заявок, соответствующей posix, в которой «текстовый файл» имеет специальную обработку в ядре для вставки правильной новой строки в случае, если она не имеет это)
Луис Колорадо
62

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

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

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

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

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

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

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

$ echo -n "Line not ending in a new line" | wc -l
0
$ echo "Line ending with a new line" | wc -l
1
Флимм
источник
22
Я бы не сказал «некоторые», я говорю, что большинство инструментов ожидают, что для текстовых файлов, если не все. кот, мерзавец, diff, туалет, grep, sed ... список огромен
MestreLion
Возможно, можно сказать, что wcэтого не ожидает , поскольку он просто работает в рамках определения «линии» в POSIX, а не в интуитивном понимании «линии» большинством людей.
Гильденстерн
@Guildenstern Интуитивное определение предназначено для wc -lпечати 1в обоих случаях, но некоторые люди могут сказать, что второй случай должен печатать 2.
Flimm
@Flimm Если вы воспринимаете \nтерминатор строки, а не разделитель строк, как это делает POSIX / UNIX, то ожидать, что второй случай выведет 2, просто безумие.
точка с запятой
21

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

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

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

CGP
источник
5
GCC не способен обработать файл, он должен выдавать предупреждение как часть стандарта C.
Билл Ящерица
IIRC, MSVC 2005 жаловался на файлы C, которые заканчивались неполными строками и, возможно, отказывались компилировать их.
Марк К Коуэн
16

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

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

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

Стефан
источник
15
в настоящее время новая строка в EOF для текстовых файлов может не требоваться, но это полезное соглашение, которое заставляет большинство инструментов Unix работать вместе с согласованными результатами. Это совсем не ошибка.
MestreLion
14
Многие из нас вообще не используют инструменты Unix, и нам все равно.
DaveWalley
12
Это не просто инструменты Unix, любой инструмент будет работать лучше и / или будет проще кодироваться, если он может принимать разумные форматы файлов.
Сэм Уоткинс
2
@ Сэм Уоткинс Согласитесь, хорошо иметь простые четко определенные форматы . Тем не менее, код все еще нуждается в достоверности и не предполагает, что данные соответствуют формату.
chux - Восстановить Монику
8
@MestreLion Это бесполезное наследие из набора плохих инструментов, соответствующих глупым стандартам. Эти артефакты экстремистского программирования (т. Е. Все файлы! Все должно быть простым текстом!) Не умерли вскоре после их изобретения, потому что они были единственными доступными инструментами такого рода в определенный момент истории. C был заменен C ++, он не является частью POSIX, он не требует EOL в EOF, и его использование (очевидно) не поощряется * nix luddists.
polkovnikov.ph
14

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

Робин Уиттлтон
источник
1
Дифференциал и обвинение должны быть просто обновлены, чтобы обнаруживать «новые строки», а не «новые строки» ( \n). Задача решена.
Андрей
1
Вы можете использовать тег -w, чтобы игнорировать изменения пробелов, но они не используются по умолчанию.
Робин Уиттлтон
11

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

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

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

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

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

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

симбионт
источник
10

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

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

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

Марк Гравелл
источник
12
Это не правило, это соглашение: строка - это то, что заканчивается концом строки . Так что нет, между EOL и EOF нет «пустой финальной линии».
MestreLion
4
@MestreLion: Но рассматриваемый символ не называется «конец строки», он называется «перевод строки» и / или «перевод строки». Разделитель строк, а не разделитель строк. И результат - окончательная пустая строка.
Бен Фойгт
2
Ни один (нормальный) инструмент не будет считать последний EOL (CR, LF и т. Д.) Файла как дополнительную пустую строку. И все инструменты POSIX не будут считать последние символы файла как строку, если нет конечного EOL. Независимо от характера EOL названием является «линия подачей» или «возврат каретки» (нет никакого символа имени «новой строка»), для всех практических курсовых разумных средств лечения как линии терминатора , а не в качестве линии сепаратора .
МестреЛион
2
@MestreLion, вы уверены, что "терминатор строки" вменяемый? Возьмите несколько непрограммистов и сделайте быстрый опрос. Вы быстро поймете, что концепция линий ближе к понятию «разделители строк». Понятие «ограничитель строки» просто странно .
Pacerier
4
@Sahuagin: Это не мое мнение, это то, как стандарт POSIX определяет линию. Пустой файл с 0 байт имеет 0 строк, следовательно , нет EOL, и файл будет рассматриваться как имеющий только одну пустую строку, это делает требует EOL. Также обратите внимание, что это уместно, только если вы хотите посчитать строки в файле, поскольку очевидно, что любой редактор позволит вам «добраться» до следующей (или первой) строки независимо от того, есть ли там EOL.
MestreLion
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сбой из-за непустой строки в конце файла. Естественно, в этом случае будет одна дополнительная новая строка в выходных данных, которых не было во входных данных.

l0b0
источник
9

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

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

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

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

  4. Не, как это возможно, сгенерировать файл Недостатки концовки '\n'.

Chux - Восстановить Монику
источник
4

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

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

Существует один пример файла foo.txtс некоторым jsonсодержанием внутри него.

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

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

Когда мы обработали тот же файл с помощью sedкомандыsed 's|value|newValue|g' foo.txt > foo.txt.tmp

Недавно сгенерированный файл был

[{
    someProp: value
},
{
    someProp: value

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

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

Arpit
источник
3

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

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

he_the_great
источник
0

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

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

Pippen_001
источник
-4

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

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

пользователь
источник
-6

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

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

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

Торбен Гундтофте-Бруун
источник