Чому текстові файли повинні закінчуватися новим рядком?


1467

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


30
просто нитка. це не "новий рядок" в кінці файлу. Це "розрив рядка" в кінці останнього рядка. Крім того , див краща відповідь на відповідне питання: stackoverflow.com/questions/16222530 / ...
GCB

345
Тільки для того, щоб набрати низку, він насправді не написав "новий рядок", він написав "новий рядок", що правильно.
sindrenm

5
не знайоме, але цікаво, що я насправді, тому що кількість випадків, коли ця зайва
нова лінія

2
Наразі я використовую потоки Node.js для розбору даних у простому тексті по черзі, і відсутність розриву кінцевих рядків дратує, оскільки мені доводиться додавати додаткову логіку, коли вхідна частина потоку закінчена / закрито, щоб забезпечити обробку останнього рядка.
Марк К Коуан

23
Шлях Unix розглядає своє загальне поведінка в кінці файлів виглядає наступним чином : \ п символів не починаються рядки; натомість вони закінчують їх. Отже, \ n - це лінійний термінатор, а не роздільник рядків. Перший рядок (як і всі рядки) не потребує \ n для його запуску. Останній рядок (як і всі рядки) потребує \ n, щоб закінчити його. \ N в кінці файлу не створює додатковий рядок. Однак іноді текстові редактори додають туди видимий порожній рядок. Навіть emacs робить це, необов'язково .
MarkDBlackwell

Відповіді:


1381

Тому що ось стандарт 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 на увазі, можливо, буде легше змінити потік токена, а не аналізатор - іншими словами, додати маркер "штучної нової лінії" до кінця вводу.


8
Незважаючи на те, що зараз виправляти доволі недоцільно, 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 попереджає про це не тому, що він не може обробити файл, а тому, що він повинен бути частиною стандарту.

Стандарт мови мови говорить, що вихідний файл, який не є порожнім, повинен закінчуватися символом нового рядка, якому не повинно негайно передувати символ зворотної косої риски.

Оскільки це пункт "повинен", ми повинні вислати діагностичне повідомлення про порушення цього правила.

Про це йдеться в розділі 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)?
Pacerier

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також буде виводити не <newline> завершений рядок (або неповний рядок ) як рядок - оскільки кількість починається з 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

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
Я б не сказав "деякі", я кажу, що більшість інструментів очікують, що для текстових файлів, якщо не всіх. cat, git, diff, wc, 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)

Попередження компілятора "Немає нового рядка в кінці файлу"


5
GCC не в змозі обробити файл, він повинен надіслати попередження як частину стандарту C.
Білл Ящірка

IIRC, MSVC 2005 скаржився на файли C, які закінчувалися неповними рядками і, можливо, відмовилися їх компілювати.
Марк К Коуан

16

Це походить із самих ранніх часів, коли використовувалися прості термінали. Зображення нового рядка використовувалося для запуску "спалаху" переданих даних.

Сьогодні значок нового рядка більше не потрібен. Звичайно, у багатьох додатків все ще виникають проблеми, якщо нового рядка немає, але я вважаю, що помилка в цих додатках.

Якщо у вас є формат текстового файлу, де вам потрібен новий рядок, ви отримуєте просту перевірку даних дуже дешево: якщо файл закінчується рядком, у якому в кінці немає нового рядка, ви знаєте, що файл зламаний. Маючи лише один додатковий байт для кожного рядка, ви можете виявляти пошкоджені файли з високою точністю і майже без часу процесора.


15
в даний час нової рядок в EOF для текстових файлів не може бути вимогою, але воно є корисною умовністю , що робить більшість інструментів UNIX працюють разом з послідовними результатами. Це зовсім не помилка.
MestreLion

14
Багато з нас взагалі не використовують інструменти Unix, і нас це не хвилює.
DaveWalley

12
Це не просто інструменти Unix, будь-який інструмент буде працювати краще і / або кодуватися простіше, якщо він може приймати розумні формати файлів.
Сем Уоткінс

2
@Sam Watkins Погоджуюся, що прості чітко визначені формати добре. Однак код все ще повинен бути достовірним, а не припускати, що дані відповідають формату.
chux

8
@MestreLion Це марний спадок із набору поганих інструментів, що відповідають дурним стандартам. Ці артефакти екстремістського програмування (тобто всі файли! Все повинно говорити простим текстом!) Не вмерли незабаром після їх винаходу, оскільки вони були єдиними доступними інструментами такого роду в певний момент історії. C витіснив C ++, він не є частиною POSIX, він не вимагає EOL в EOF, і його використання (очевидно) відлякує * nix luddists.
polkovnikov.ph

14

Окремий випадок використання: коли ваш текстовий файл контролюється версією (в даному випадку спеціально під git, хоча це стосується і інших). Якщо вміст буде додано в кінці файлу, то рядок, який раніше був останнім рядком, буде відредагований таким чином, щоб він містив символ нового рядка. Це означає, що blameу файлі, щоб дізнатися, коли цей рядок востаннє редагувався, буде показано додаток до тексту, а не фіксація перед тим, що ви насправді хотіли бачити.


1
diff і вину слід просто оновити, щоб виявити "нові рядки", а не "нові рядки" ( \n). Проблема вирішена.
Андрій

1
Ви можете використовувати тег -w, щоб ігнорувати зміни пробілів, але вони не є типовими.
Робін Віттлтон

11

Окрім перерахованих вище практичних причин, мене не здивує, якби автори Unix (Томпсон, Річі та ін.) Або їх попередники Multics зрозуміли, що існує теоретична причина використовувати термінатори, а не роздільники ліній: With line термінатори, ви можете кодувати всі можливі файли рядків. У роздільниках рядків немає різниці між файлом нульових рядків і файлом, що містить один порожній рядок; обидва вони кодуються як файл, що містить нульові символи.

Отже, причини:

  1. Тому що це визначає POSIX.
  2. Тому що деякі інструменти очікують цього або «погано поводяться» без нього. Наприклад, wc -lне буде зараховано остаточний "рядок", якщо він не закінчується новим рядком.
  3. Тому що це просто і зручно. У Unix catпросто працює і працює без ускладнень. Він просто копіює байти кожного файлу, не потребуючи інтерпретації. Я не думаю, що DOS еквівалентний cat. Використання copy a+b cпризведе до об'єднання останнього рядка файлу aз першим рядком файлу b.
  4. Тому що файл (або потік) нульових рядків можна відрізнити від файлу одного порожнього рядка.

11

Я сам це дивувався роками. Але я сьогодні зіткнувся з вагомою причиною.

Уявіть файл із записом у кожному рядку (наприклад: файл CSV). І що комп'ютер писав записи в кінці файлу. Але воно раптом розбилося. Джи, чи був останній рядок завершеним? (не приємна ситуація)

Але якщо ми завжди припиняємо останній рядок, то ми б знали (просто перевіримо, чи закінчується останній рядок). Інакше нам, мабуть, доведеться відкидати останній рядок кожен раз, лише щоб бути в безпеці.


10

Імовірно просто, що якийсь код розбору очікував, що він буде там.

Я не впевнений, що вважав би це "правилом", і це, звичайно, не те, чого я релігійно дотримуюся. Більшість розумних кодів знатиме, як розбирати текст (включаючи кодування) рядок за рядком (будь-який вибір закінчень рядків), без або без нового рядка в останньому рядку.

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


12
Це не правило, це умова: рядок - це те, що закінчується кінцевим рядком . Тож ні, між EOL та EOF немає "порожнього остаточного рядка".
MestreLion

4
@MestreLion: Але відповідний персонаж не названий "кінцевим рядком", він називається "newline" та / або "linefeed". Розділювач ліній, а не лінійний термінатор. І результат - це заключний порожній рядок.
Бен Войт

2
Жоден (розумний) інструмент не вважатиме останній EOL (CR, LF тощо) як додатковий порожній рядок. І всі інструменти POSIX не будуть рахувати останні символи файлу як рядки, якщо EOL не закінчується. Незалежно від того, що назва символу EOL - "канал каналу" або "повернення каретки" (немає символу з назвою "нова лінія"), усі практичні завдання розсудливі інструменти розглядають його як термінатор , а не як роздільник рядків .
MestreLion

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не вдалося через не порожній рядок у кінці файлу. Природно, що в цьому випадку буде один зайвий новий рядок у виході, який не був у введенні.


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'.


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.

Тож завжди добре закінчувати файл порожнім новим рядком.


3

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

Однак я вважаю, що правило походить від компіляторів С, що вимагають нового рядка. І як зазначено у попередженні компілятора "Немає нового рядка в кінці файлу" , #include не додасть новий рядок.


0

Уявіть, що файл обробляється, поки файл ще створюється іншим процесом.

Це може мати відношення до цього? Прапор, який вказує, що файл готовий до обробки.


-4

Мені особисто подобаються нові рядки в кінці файлів вихідного коду.

Він може мати своє походження з Linux або всіма системами UNIX з цього питання. Я пам’ятаю, є помилки компіляції (якщо я не помиляюся), оскільки файли вихідного коду не закінчувались порожнім новим рядком. Чому це було зроблено таким чином, залишається дивуватися.


-6

ІМХО, це питання особистого стилю та думки.

У старі часи я не ставив цей новий рядок. Збережений персонаж означає більшу швидкість через цей 14,4K модем.

Пізніше я помістив цей новий рядок, щоб було легше вибрати остаточний рядок, використовуючи shift + спадання.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.