Dlaczego pliki tekstowe powinny kończyć się znakiem nowej linii?

Zakładam, że wszyscy tutaj znają powiedzenie, że wszystkie pliki tekstowe powinny kończyć się znakiem nowej linii. Znam tę "zasadę" od lat, ale zawsze się zastanawiałem-dlaczego?

Author: codeforester, 2009-04-08

17 answers

Ponieważ w ten sposób standard POSIX definiuje linię :

3.206 linia Sekwencja zero lub więcej znaków nie- plus znak kończący .

Dlatego linie nie kończące się znakiem nowej linii nie są uważane za linie rzeczywiste. Dlatego niektóre programy mają problemy z przetwarzaniem ostatniej linii pliku, jeśli nie jest ona zakończona znakiem nowej linii.

Jest co najmniej jedna trudna zaleta tych wytycznych podczas pracy nad terminal emulator: wszystkie narzędzia uniksowe oczekują tej konwencji i pracują z nią. Na przykład, w przypadku łączenia plików z cat, plik zakończony znakiem nowej linii będzie miał inny efekt niż bez:

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

I, jak pokazuje poprzedni przykład, podczas wyświetlania pliku w wierszu poleceń (np. via more), Plik zakończony znakiem nowej linii powoduje prawidłowe wyświetlenie. Nieprawidłowo zakończony plik może być zniekształcony (druga linia).

Dla spójności, bardzo pomocne jest postępuj zgodnie z tą regułą-Robienie w przeciwnym razie będzie wymagało dodatkowej pracy podczas pracy z domyślnymi narzędziami Uniksowymi.

w systemach nie zgodnych ze standardem POSIX (obecnie jest to głównie Windows), kwestia jest dyskusyjna: pliki nie kończą się zwykle znakiem nowej linii, a (nieformalna) definicja linii może na przykład brzmieć: "tekst, który jest oddzielony nowymi liniami" (zwróć uwagę na podkreślenie). Jest to całkowicie ważne. Jednak dla danych ustrukturyzowanych (np. kodu programistycznego) sprawia, że parsowanie minimalnie bardziej skomplikowane: ogólnie oznacza to, że parsery muszą zostać przepisane. Jeśli parser został pierwotnie napisany z myślą o definicji POSIX, to może być łatwiej zmodyfikować strumień tokenów zamiast parsera - innymi słowy, dodać" sztuczny znak nowej linii " na końcu wejścia.

 1065
Author: Konrad Rudolph,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-22 10:11:57

Każda linia powinna być zakończona znakiem nowej linii, włącznie z ostatnią. Niektóre programy mają problemy z przetwarzaniem ostatniej linii pliku, jeśli nie jest ona zakończona znakiem nowej linii.

GCC ostrzega o tym nie dlatego, że nie możeprzetworzyć pliku, ale dlatego, że musi jako część standardu.

Standard języka C mówi Plik źródłowy, który nie jest pusty, kończy się znakiem nowej linii, który nie może być bezpośrednio poprzedzony odwrotnym ukośnikiem charakter.

Ponieważ jest to klauzula "shall", musimy wysłać komunikat diagnostyczny za naruszenie tej reguły.

Jest to w sekcji 2.1.1.2 normy ANSI C 1989. Sekcja 5.1.1.2 normy ISO C 1999 (i prawdopodobnie również normy ISO C 1990).

Referencja: archiwum poczty GCC/GNU.

 249
Author: Bill the Lizard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-08 12:26:46

Ta odpowiedź jest próbą odpowiedzi technicznej, a nie opinii.

Jeśli chcemy być PURYSTAMI POSIX, definiujemy linię jako:

Sekwencja zero lub więcej znaków nie- plus znak kończący .

Źródło: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206

Niekompletny wiersz jako:

Sekwencja jednego lub więcej nie- znaków w koniec pliku.

Źródło: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195

Plik tekstowy jako:

Plik zawierający znaki zorganizowane w zero lub więcej linii. Linie nie zawierają znaków NUL i żaden z nich nie może przekraczać długości {LINE_MAX} bajtów, łącznie ze znakiem . Chociaż POSIX.1-2008 nie rozróżnia plików tekstowych i binarnych (patrz norma ISO C), wiele narzędzia generują przewidywalne lub znaczące dane wyjściowe tylko podczas pracy na plikach tekstowych. Standardowe narzędzia, które mają takie ograniczenia, zawsze określają "pliki tekstowe" w swoich sekcjach STDIN lub INPUT FILES.

Źródło: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_397

A string as:

Ciąg bajtów zakończony i zawierający pierwszy bajt null.

Źródło: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396

Stąd możemy wywnioskować, że jedynym przypadkiem, kiedy potencjalnie napotkamy jakiekolwiek problemy, jest to, że mamy do czynienia z pojęciem linii pliku lub pliku jako pliku tekstowego (ponieważ plik tekstowy jest organizacją zero lub więcej linii, a linia, którą znamy, musi zakończyć się ).

Przypadek: wc -l filename.

Z podręcznika wc czytamy:

Linia jest zdefiniowana jako ciąg znaków oddzielony znakiem .

Jakie są implikacje dla plików JavaScript, HTML i CSS, które są plikami tekstowymi ?

W przeglądarkach, nowoczesnych Idach i innych aplikacjach front-end nie ma problemów z pominięciem EOL w EOF. Aplikacje będą poprawnie analizować pliki. Musi, ponieważ nie wszystkie systemy operacyjne zgodność ze standardem POSIX, więc niepraktyczne byłoby dla narzędzi innych niż system operacyjny (np. przeglądarek) obsługiwanie plików zgodnie ze standardem POSIX (lub dowolnym standardem poziomu systemu operacyjnego).

W rezultacie możemy być stosunkowo pewni, że EOL w EOF praktycznie nie będzie miał negatywnego wpływu na poziomie aplikacji - niezależnie od tego, czy działa na systemie UNIX.

W tym momencie możemy śmiało powiedzieć, że pomijanie EOL w EOF jest bezpieczne, gdy mamy do czynienia z JS, HTML, CSS po stronie klienta. Właściwie, my może stwierdzić, że minifikacja dowolnego z tych plików, nie zawierającego jest Bezpieczna.

Możemy pójść o krok dalej i powiedzieć, że NodeJS również nie może stosować się do standardu POSIX, ponieważ może działać w środowiskach niezgodnych ze standardem POSIX.

Co nam zostało? Oprzyrządowanie na poziomie systemu.

Oznacza to, że jedyne problemy, które mogą się pojawić, to narzędzia, które starają się dostosować swoją funkcjonalność do semantyki POSIX (np. definicja linii pokazana w wc).

Mimo to, nie wszystkie powłoki będą automatycznie stosować się do standardu POSIX. Na przykład Bash nie domyślnie zachowuje się w standardzie POSIX. Istnieje przełącznik, aby go włączyć: POSIXLY_CORRECT.

Pożywka do przemyśleń nad wartością EOL będącą : http://www.rfc-editor.org/EOLstory.txt

Pozostając na ścieżce narzędziowej, we wszystkich praktycznych zamiarach i celach, rozważmy to: {]}

Popracujmy z plikiem, który nie ma EOL. Od tego czasu zapisanie pliku w tym przykładzie jest minified JavaScript bez 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

Zauważ, że rozmiar pliku cat jest dokładnie sumą jego poszczególnych części. Jeśli konkatenacja plików JavaScript jest problemem dla plików JS, bardziej odpowiednim problemem byłoby rozpoczynanie KAŻDEGO pliku JavaScript dwukropkiem.

Jak ktoś inny wspomniał w tym wątku: co jeśli chcesz cat dwa pliki, których wyjście stanie się tylko jedną linią zamiast dwóch? Innymi słowy, cat robi to, co jest tak powinno być.

man z cat wspomina tylko o odczycie danych wejściowych do EOF, a nie . Należy pamiętać, że przełącznik -n z cat wyświetli również linię zakończoną nie - (lub niekompletną linię) jako linię - ponieważ licznik zaczyna się od 1 (zgodnie z man.)

- N numeruje linie wyjściowe, zaczynając od 1.

Teraz, gdy rozumiemy, jak POSIX definiuje linię , to zachowanie staje się niejednoznaczna, a właściwie niezgodna.

Zrozumienie celu i zgodności danego narzędzia pomoże określić, jak ważne jest kończenie plików za pomocą EOL. W języku C, C++, Java (JARs) itp... niektóre standardy będą dyktować nową linię ważności - taki standard nie istnieje dla JS, HTML, CSS.

Na przykład, zamiast używać wc -l filename można zrobić awk '{x++}END{ print x}' filename i mieć pewność, że sukces zadania nie jest zagrożony przez plik, który możemy przetworzyć, którego nie napisaliśmy (np. biblioteki innych firm, takie jak minifikowane JS we curld) - chyba że naszym zamiarem było naprawdę liczyć wiersze w sensie zgodnym z POSIX.

Wniosek

Będzie bardzo niewiele rzeczywistych przypadków użycia, w których pomijanie EOL w EOF dla niektórych plików tekstowych, takich jak JS, HTML i CSS, będzie miało negatywny wpływ - jeśli w ogóle. Jeśli polegamy na obecności , ograniczamy niezawodność naszych narzędzi tylko do plików, które sami tworzymy i otwieramy do potencjalnych błędów wprowadzonych przez pliki stron trzecich.

[[16]}Morał historii: inżynierskie narzędzia, które nie mają słabości polegania na EOL w EOF.

Zachęcamy do publikowania przypadków użycia, ponieważ dotyczą one JS, HTML i CSS, gdzie możemy zbadać, jak pomijanie EOL ma niekorzystny wpływ.

 91
Author: Milan Adamovsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-15 06:47:08

Może być związana z różnicą pomiędzy :

  • plik tekstowy (każda linia ma się kończyć na końcu linii)
  • plik binarny (nie ma prawdziwych "linii" do mówienia, a długość pliku musi być zachowana)

Jeśli każda linia kończy się na końcu linii, pozwala to na przykład uniknąć tego, że połączenie dwóch plików tekstowych spowoduje, że ostatnia linia pierwszego będzie biegać w pierwszej linii drugiego.

Dodatkowo edytor może sprawdzić przy ładowaniu czy plik kończy się znakiem końca linii, zapisuje go w lokalnej opcji 'eol' i używa go podczas zapisu pliku.

Kilka lat temu (2005), wielu redaktorów (ZDE, Eclipse, Scite, ...) "zapomniał" o ostatnim EOL, , który nie był zbyt doceniany.
Nie tylko to, ale zinterpretowali ten końcowy EOL nieprawidłowo, jako 'start a new line' , i faktycznie zaczynają wyświetlać inną linię, tak jakby już istniała.
Było to bardzo widoczne przy "właściwym" pliku tekstowym z dobrze zachowanym tekstem edytor jak vim, w porównaniu do otwarcia go w jednym z powyższych edytorów. Wyświetla dodatkową linię poniżej prawdziwej ostatniej linii pliku. Widzisz coś takiego:

1 first line
2 middle line
3 last line
4
 59
Author: VonC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-04 11:30:07

Niektóre narzędzia tego oczekują. Na przykład, wc oczekuje tego:

$ echo -n "Line not ending in a new line" | wc -l
0
$ echo "Line ending with a new line" | wc -l
1
 39
Author: Flimm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-10-12 14:16:58

Zasadniczo istnieje wiele programów, które nie będą poprawnie przetwarzać plików, jeśli nie otrzymają ostatecznego EOL EOF.

GCC ostrzega przed tym, ponieważ jest to oczekiwane jako część standardu C. (sekcja 5.1.1.2)

"Brak nowej linii na końcu pliku" ostrzeżenie kompilatora

 18
Author: cgp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:49

To pochodzi z bardzo wczesnych dni, kiedy proste terminale były używane. Znak nowej linii został użyty do wywołania "flush" przesłanych danych.

Dzisiaj znak nowej linii nie jest już wymagany. Oczywiście, wiele aplikacji nadal ma problemy, jeśli nie ma nowej linii, ale uznałbym to za błąd w tych aplikacjach.

Jeśli jednak masz format pliku tekstowego, w którym wymagasz nowej linii, otrzymujesz prostą weryfikację danych bardzo tanio: jeśli plik kończy się linią, która nie ma nowy wiersz na końcu, wiesz, że plik jest uszkodzony. Dzięki tylko jednemu dodatkowemu bajtowi dla każdej linii można wykryć uszkodzone pliki z dużą dokładnością i prawie bez czasu procesora.

 12
Author: Stefan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-08 12:41:21

Osobny przypadek użycia: gdy Twój plik tekstowy jest kontrolowany przez wersję (w tym przypadku konkretnie pod git, chociaż dotyczy również innych). Jeśli zawartość zostanie dodana na końcu pliku, to linia, która była wcześniej ostatnią linią, zostanie edytowana tak, aby zawierała znak nowej linii. Oznacza to, że blame w pliku, aby dowiedzieć się, kiedy ta linia była ostatnio edytowana, pojawi się dodanie tekstu, a nie commit, który wcześniej chciałeś zobaczyć.

 11
Author: Robin Whittleton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-29 18:55:59

Jest też praktyczny problem z programowaniem z plikami bez nowych linii na końcu: Wbudowany read Bash (Nie wiem jak inne implementacje read) nie działa zgodnie z oczekiwaniami:

printf $'foo\nbar' | while read line
do
    echo $line
done

To drukuje tylko foo! Powodem jest to, że gdy read napotka ostatnią linię, zapisuje zawartość do $line, ale zwraca kod wyjścia 1, ponieważ osiągnął EOF. Powoduje to przerwanie pętli while, więc nigdy nie docieramy do echo $line. Jeśli chcesz poradzić sobie z tą sytuacją, musisz zrobić następujące:

while read line || [ -n "${line-}" ]
do
    echo $line
done < <(printf $'foo\nbar')

To znaczy wykonaj echo, Jeśli read nie powiodło się z powodu niepustej linii na końcu pliku. Oczywiście, w tym przypadku na wyjściu będzie jeden dodatkowy znak nowej linii, którego nie było na wejściu.

 10
Author: l0b0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-01 09:06:49

Przypuszczalnie po prostu, że jakiś parsujący kod spodziewał się, że tam będzie.

Nie jestem pewien, czy uznałbym to za "regułę" , a na pewno nie jest to coś, czego wyznaję religijnie. Najbardziej rozsądny kod będzie wiedział, jak parsować tekst (w tym kodowanie) linia po linii (dowolny wybór zakończeń linii), z-lub-bez nowego wiersza w ostatniej linii.

Rzeczywiście - jeśli kończy się nową linią: czy istnieje (teoretycznie) pusta linia końcowa między EOL i EOF? Jeden do przemyślenia...

 9
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-08 12:19:54

Poza powyższymi praktycznymi powodami, nie zdziwiłoby mnie, gdyby twórcy Uniksa (Thompson, Ritchie, et al.) lub ich wielcy poprzednicy zdali sobie sprawę, że istnieje teoretyczny powód, aby używać Terminatorów liniowych zamiast separatorów liniowych: za pomocą Terminatorów liniowych można zakodować wszystkie możliwe Pliki linii. W przypadku separatorów linii nie ma różnicy między plikiem zawierającym zero linii a plikiem zawierającym pojedynczą pustą linię; oba są zakodowane jako plik zawierający zero postaci.

Więc powody są następujące:

  1. ponieważ tak to definiuje POSIX.
  2. ponieważ niektóre narzędzia tego oczekują lub "źle się zachowują" bez tego. Na przykład, wc -l nie zliczy końcowej "linii", jeśli nie kończy się nową linią.
  3. Ponieważ jest to proste i wygodne. Na Unixie, cat po prostu działa i działa bez komplikacji. Po prostu kopiuje bajty KAŻDEGO pliku, bez potrzeby interpretacji. Nie wydaje mi się, że istnieje odpowiednik DOS cat. Użycie copy a+b c zakończy się połączeniem ostatniej linii pliku a z pierwszą linią pliku b.
  4. ponieważ plik (lub strumień) linii zerowych można odróżnić od Pliku jednej pustej linii.
 9
Author: John Wiersba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-25 12:23:09

Dlaczego pliki (tekstowe) powinny kończyć się znakiem nowej linii?

Jak również wyrażona przez wielu, ponieważ:

  1. Wiele programów nie zachowuje się dobrze lub zawodzi bez niego.

  2. Nawet programy, które dobrze obsługują plik, nie mają końcówki '\n', funkcjonalność narzędzia może nie spełniać oczekiwań użytkownika - co może być niejasne w tym przypadku.

  3. Programy rzadko disallow final '\n' (Nie wiem o any).


Nasuwa się jednak kolejne pytanie:

Co kod powinien zrobić z plikami tekstowymi bez nowej linii?

  1. Najważniejsze - nie zapisuj kodu, który zakłada, że plik tekstowy kończy się znakiem nowej linii . zakładając, że plik jest zgodny z formatem, prowadzi do uszkodzenia danych, ataków hakerskich i awarii. Przykład:

    // 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. Jeśli konieczne jest ostateczne zakończenie '\n', Powiadom użytkownika o jego braku i działaniu zajęte. IOWs, potwierdź format pliku. Uwaga: Może to obejmować ograniczenie maksymalnej długości linii, kodowania znaków itp.

  3. Zdefiniuj wyraźnie, dokument, obsługę kodu brakującego finalnego '\n'.

  4. Nie należy, jak to możliwe, generować pliku z końcówką '\n'.

 7
Author: chux,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-30 13:59:13

Zastanawiałam się nad tym od lat. Ale natknąłem się dziś na dobry powód.

Wyobraź sobie Plik z rekordem w każdej linii (np. plik CSV). I że komputer zapisywał zapisy na końcu pliku. Ale nagle się rozbił. Gee była ostatnia linia zakończona? (not a nice situation)

Ale jeśli zawsze zakończymy ostatnią linię, wtedy będziemy wiedzieć(po prostu sprawdź, czy ostatnia linia jest zakończona). W przeciwnym razie prawdopodobnie musielibyśmy za każdym razem odrzucać ostatnią linijkę, aby być bezpiecznie.

 6
Author: symbiont,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-08 14:22:52

Zawsze miałem wrażenie, że reguła pochodzi z czasów, kiedy parsowanie pliku Bez kończącego się znaku nowego wiersza było trudne. Oznacza to, że skończysz pisząc kod, w którym koniec linii został zdefiniowany przez znak EOL lub EOF. Prostsze było założenie linii zakończonej EOL.

Jednak uważam, że reguła pochodzi z kompilatorów C wymagających nowej linii. I jak zaznaczono na ostrzeżeniu kompilatora "Brak nowej linii na końcu pliku" , #include nie doda nowej linii.

 3
Author: he_the_great,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:00

Wyobraź sobie, że plik jest przetwarzany, podczas gdy plik jest nadal generowany przez inny proces.

To może mieć z tym coś wspólnego? Flaga wskazująca, że plik jest gotowy do przetworzenia.

 0
Author: Pippen_001,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-08 12:36:30

Osobiście lubię nowe linie na końcu plików kodu źródłowego.

Może mieć swój początek z Linuksem lub wszystkimi systemami UNIKSOWYMI. Pamiętam tam błędy kompilacji (gcc jeśli się nie mylę), ponieważ pliki kodu źródłowego nie kończyły się pustą nową linią. Dlaczego to zostało zrobione w ten sposób, pozostaje się zastanawiać.

 -4
Author: User,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-08 12:23:08

IMHO, to kwestia osobistego stylu i opinii.

W dawnych czasach, nie umieściłem tej nowej linii. Zapisany znak oznacza większą prędkość dzięki modemowi 14,4 K.

Później umieściłem tę nową linię, aby łatwiej było wybrać ostatnią linię za pomocą shift+downarrow.

 -7
Author: Torben Gundtofte-Bruun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-04-08 12:38:48