Zakładam, że wszyscy tutaj znają powiedzenie, że wszystkie pliki tekstowe powinny kończyć się nową linią. Znam tę „zasadę” od lat, ale zawsze zastanawiałem się - dlaczego?
file
unix
text-files
newline
Will Robertson
źródło
źródło
Odpowiedzi:
Ponieważ w ten sposób standard POSIX definiuje linię :
Dlatego wiersze nie kończące się znakiem nowej linii nie są uważane za wiersze rzeczywiste. Dlatego niektóre programy mają problemy z przetwarzaniem ostatniego wiersza pliku, jeśli nie jest on zakończony znakiem nowej linii.
Podczas pracy na emulatorze terminali jest co najmniej jedna twarda zaleta: wszystkie narzędzia uniksowe oczekują tej konwencji i działają z nią. Na przykład podczas łączenia plików z
cat
plikiem zakończonym znakiem nowej linii będzie mieć inny efekt niż ten bez:I, jak pokazuje również poprzedni przykład, podczas wyświetlania pliku w wierszu poleceń (np. Przez
more
) plik zakończony znakiem nowej linii powoduje poprawne wyświetlanie. Nieprawidłowo zakończony plik może być zniekształcony (druga linia).Aby zachować spójność, bardzo pomocne jest przestrzeganie tej reguły - w przeciwnym razie będzie to wymagać dodatkowej pracy w przypadku domyślnych narzędzi uniksowych.
Pomyśl o tym inaczej: jeśli wiersze nie są kończone znakiem nowej linii,
cat
znacznie trudniej jest sprawić, by takie polecenia były przydatne: w jaki sposób można wykonać polecenie łączenia plików w taki sposób, abyb.txt
ic.txt
?Oczywiście jest to możliwe do rozwiązania, ale musisz uczynić korzystanie z
cat
bardziej złożonego (dodając np. Argumenty pozycyjnego wiersza poleceńcat a.txt --no-newline b.txt c.txt
), a teraz polecenie a nie każdy plik, kontroluje sposób wklejania go wraz z innymi plikami. To prawie na pewno nie jest wygodne.… Lub musisz wprowadzić specjalny znak wartownika, aby zaznaczyć linię, która powinna być kontynuowana, a nie zakończona. Cóż, teraz utknąłeś w takiej samej sytuacji jak w POSIX, z wyjątkiem odwróconego (kontynuacja linii zamiast znaku zakończenia linii).
Teraz w systemach niezgodnych z POSIX (obecnie to głównie Windows) chodzi o dyskusję: pliki na ogół nie kończą się nową linią, a (nieformalna) definicja linii może na przykład być „tekstem oddzielonym nowymi liniami” (zwróć uwagę na nacisk). Jest to całkowicie ważne. Jednak w przypadku danych strukturalnych (np. Kodu programowania) parsowanie jest minimalnie bardziej skomplikowane: ogólnie oznacza to, że parsery muszą zostać przepisane. Jeśli parser został pierwotnie napisany z myślą o definicji POSIX, może być łatwiej zmodyfikować strumień tokenów niż parser - innymi słowy, dodaj token „sztucznej nowej linii” na końcu wejścia.
źródło
cat
użyteczne i spójne.Każda linia powinna być zakończona znakiem nowej linii, w tym ostatnią. Niektóre programy mają problemy z przetwarzaniem ostatniego wiersza pliku, jeśli nie jest on zakończony znakiem nowej linii.
GCC ostrzega przed tym nie dlatego, że nie może przetworzyć pliku, ale dlatego, że musi to być częścią standardu.
Odniesienie: Archiwum poczty GCC / GNU .
źródło
wc -l
nie policzy ostatniego wiersza pliku, jeśli nie jest on zakończony znakiem nowej linii. Ponadtocat
połączy ostatni wiersz pliku z pierwszym wierszem następnego pliku w jeden, jeśli ostatni wiersz pierwszego pliku nie jest zakończony znakiem nowej linii. Niemal każdy program, który szuka nowych linii jako separatora, może to zepsuć.wc
ma już wspomniano ....cat
iwc
)?Ta odpowiedź jest raczej próbą odpowiedzi technicznej niż opinii.
Jeśli chcemy być purystami POSIX, definiujemy linię jako:
Źródło: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206
Niekompletna linia jako:
Źródło: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195
Plik tekstowy jako:
Źródło: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_397
Ciąg jako:
Źródło: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396
Na tej podstawie możemy wywnioskować, że jedynym problemem, z którym możemy potencjalnie napotkać jakiekolwiek problemy, jest koncepcja linii pliku lub pliku jako pliku tekstowego (ponieważ plik tekstowy jest organizacją zerową lub więcej linii, a linia, którą znamy, musi kończyć się <nową linią>).
Sprawa w punkcie:
wc -l filename
.Z
wc
podręcznika czytamy:Jakie są konsekwencje dla plików JavaScript, HTML i CSS, ponieważ są one plikami tekstowymi ?
W przeglądarkach, nowoczesnych IDE i innych aplikacjach front-end nie ma problemów z pomijaniem EOL w EOF. Aplikacje poprawnie parsują pliki. Musi, ponieważ nie wszystkie systemy operacyjne są zgodne ze standardem POSIX, więc niepraktyczne byłoby, gdyby narzędzia inne niż OS (np. Przeglądarki) obsługiwały pliki zgodnie ze standardem POSIX (lub jakimkolwiek standardem na poziomie systemu operacyjnego).
W rezultacie możemy być stosunkowo pewni, że EOL w EOF nie będzie miał praktycznie żadnego negatywnego wpływu na poziomie aplikacji - niezależnie od tego, czy działa w systemie operacyjnym 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 rzeczywistości możemy stwierdzić, że zminimalizowanie któregokolwiek z tych plików, które nie zawiera <newline>, jest bezpieczne.
Możemy pójść o krok dalej i powiedzieć, że jeśli chodzi o NodeJS, to również nie może on być zgodny ze standardem POSIX, ponieważ może działać w środowiskach niezgodnych z POSIX.
Co nam zatem pozostało? Oprzyrządowanie na poziomie systemu.
Oznacza to, że jedyne problemy, które mogą się pojawić, dotyczą narzędzi, które starają się dostosować swoją funkcjonalność do semantyki POSIX (np. Definicja linii, jak pokazano w
wc
).Mimo to nie wszystkie powłoki będą automatycznie dostosowywać się do POSIX. Na przykład Bash nie domyślnie zachowuje się w POSIX. Jest to przełącznik, aby włączyć go:
POSIXLY_CORRECT
.Zastanów się nad wartością EOL jako <newline>: https://www.rfc-editor.org/old/EOLstory.txt
Pozostając na torze narzędziowym, we wszystkich praktycznych celach i celach, zastanówmy się nad tym:
Pracujmy z plikiem, który nie ma EOL. W chwili pisania tego pliku w tym przykładzie jest zminimalizowanym JavaScript bez EOL.
Zauważ, że
cat
rozmiar pliku jest dokładnie sumą jego poszczególnych części. Jeśli łączenie plików JavaScript stanowi problem dla plików JS, bardziej odpowiednie byłoby uruchomienie każdego pliku JavaScript z średnikiem.Jak ktoś wspomniany w tym wątku: co zrobić, jeśli chcesz
cat
dwa pliki, których dane wyjściowe stają się tylko jedną linią zamiast dwóch? Innymi słowy,cat
robi to, co powinien.man
Zcat
tylko wspomina czytanie wejście do EOF, a nie <nowalinia>. Zauważ, że-n
przełącznikcat
wypisze również linię nie zakończoną <nowąw>> (lub linię niekompletną ) jako linię - ponieważ liczenie zaczyna się od 1 (zgodnie zman
.)Teraz, gdy rozumiemy, jak POSIX definiuje linię , to zachowanie staje się niejednoznaczne lub w rzeczywistości niezgodne.
Zrozumienie celu i zgodności danego narzędzia pomoże w określeniu, jak ważne jest zakończenie plików za pomocą EOL. W C, C ++, Java (JAR) itp. ... niektóre standardy będą dyktować nowy wiersz ważności - nie ma takiego standardu dla JS, HTML, CSS.
Na przykład, zamiast korzystać z
wc -l filename
jednego, można zrobićawk '{x++}END{ print x}' filename
i mieć pewność, że powodzenie zadania nie jest zagrożone przez plik, który możemy chcieć przetworzyć, którego nie napisaliśmy (np. Bibliotekę strony trzeciej, taką jak zminimalizowany JScurl
d) - chyba że nasz naprawdę chodziło o policzenie wierszy w sensie zgodnym z POSIX.Wniosek
Będzie bardzo niewiele rzeczywistych przypadków użycia, w których pominięcie 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 <newline>, ograniczamy niezawodność naszego narzędzia tylko do plików, które tworzymy i otwieramy się na potencjalne błędy wprowadzone przez pliki stron trzecich.
Morał tej historii: oprzyrządowanie inżynierskie, które nie ma słabości polegania na EOL w EOF.
Publikuj przypadki użycia, które dotyczą JS, HTML i CSS, gdzie możemy zbadać, w jaki sposób pomijanie EOL ma niekorzystny wpływ.
źródło
Może to być związane z różnicą między :
Jeśli każda linia kończy się na końcu linii, pozwala to na przykład uniknąć sytuacji, w której konkatenacja dwóch plików tekstowych sprawiłaby, że ostatni wiersz pierwszego byłby uruchomiony w pierwszym wierszu drugiego.
Dodatkowo, edytor może sprawdzić przy ładowaniu, czy plik kończy się na końcu linii, zapisuje go w lokalnej opcji „eol” i używa tego podczas zapisywania pliku.
Kilka lat temu (2005) wielu redaktorów (ZDE, Eclipse, Scite, ...) „zapomniało” o ostatecznym EOL, co nie było bardzo doceniane .
Nie tylko to, ale nieprawidłowo zinterpretowali ten końcowy EOL jako „rozpocznij nowy wiersz” i faktycznie wyświetlają inny wiersz, jakby już istniał.
Było to bardzo widoczne w przypadku „właściwego” pliku tekstowego z dobrze zachowującym się edytorem tekstu, takim jak vim, w porównaniu do otwierania go w jednym z powyższych edytorów. Wyświetliła dodatkową linię poniżej rzeczywistej ostatniej linii pliku. Widzisz coś takiego:
źródło
Niektóre narzędzia tego oczekują. Na przykład
wc
oczekuje:źródło
wc
tego się nie spodziewa , ponieważ działa on w ramach definicji POSIX-a „linii”, w przeciwieństwie do intuicyjnego rozumienia „linii” przez większość ludzi.wc -l
do drukowania1
w obu przypadkach, ale niektórzy ludzie mogą powiedzieć, że druga skrzynka powinna zostać wydrukowana2
.\n
terminatorze linii, a nie o separatorze linii, tak jak robi to POSIX / UNIX, to oczekiwanie na drugi przypadek wydrukowania 2 jest absolutnie szalone.Zasadniczo istnieje wiele programów, które nie przetwarzają poprawnie plików, jeśli nie otrzymają ostatecznego EOL EOL.
GCC ostrzega cię przed tym, ponieważ jest to oczekiwane jako część standardu C. (najwyraźniej sekcja 5.1.1.2)
Ostrzeżenie kompilatora „Brak nowego wiersza na końcu pliku”
źródło
Wynika to z bardzo wczesnych dni, kiedy używane były proste terminale. Znak nowej linii został użyty do uruchomienia „opróżnienia” przesłanych danych.
Dziś znak nowej linii nie jest już wymagany. Oczywiście, wiele aplikacji nadal ma problemy, jeśli nie ma nowej linii, ale uważam, że błąd w tych aplikacjach.
Jeśli jednak masz format pliku tekstowego, w którym jesteś potrzebujesz nowej linii, otrzymujesz prostą weryfikację danych bardzo tanio: jeśli plik kończy się linią, która nie ma nowej linii na końcu, wiesz, że plik jest uszkodzony. Mając tylko jeden dodatkowy bajt dla każdej linii, możesz wykryć uszkodzone pliki z wysoką dokładnością i prawie bez czasu procesora.
źródło
Osobny przypadek użycia: gdy plik tekstowy jest kontrolowany pod względem wersji (w tym przypadku konkretnie pod git, chociaż dotyczy to również innych). Jeśli zawartość zostanie dodana na końcu pliku, linia, która była poprzednio ostatnią linią, zostanie poddana edycji w celu włączenia znaku nowej linii. Oznacza to, że
blame
sprawdzenie pliku, aby dowiedzieć się, kiedy ostatnio edytowano ten wiersz, pokaże dodanie tekstu, a nie zatwierdzenie przed tym, co naprawdę chciałeś zobaczyć.źródło
\n
). Problem rozwiązany.Oprócz powyższych praktycznych powodów, nie zaskoczyłoby mnie, gdyby twórcy Unixa (Thompson, Ritchie i inni) lub ich poprzednicy Multics zdali sobie sprawę, że istnieje teoretyczny powód, aby używać terminatorów linii zamiast separatorów linii: Z linią terminatory, możesz zakodować wszystkie możliwe pliki linii. W przypadku separatorów linii nie ma różnicy między plikiem zerowym a plikiem zawierającym pojedynczą pustą linię; oba są zakodowane jako plik zawierający zero znaków.
Przyczyny są następujące:
wc -l
nie policzy ostatniej „linii”, jeśli nie kończy się na nowej linii.cat
po prostu działa i działa bez komplikacji. Po prostu kopiuje bajty każdego pliku, bez potrzeby interpretacji. Nie sądzę, że istnieje odpowiednik DOScat
. Użyciecopy a+b c
spowoduje połączenie ostatniego wiersza plikua
z pierwszym wierszem plikub
.źródło
Zastanawiam się nad tym od lat. Ale dzisiaj spotkałem dobry powód.
Wyobraź sobie plik z zapisem w każdej linii (np. Plik CSV). I że komputer zapisuje zapisy na końcu pliku. Ale nagle się zawiesił. Gee, czy ostatnia linia była kompletna? (niezła sytuacja)
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 będziemy musieli odrzucić ostatnią linię za każdym razem, aby być bezpiecznym.
źródło
Przypuszczalnie po prostu ten kod parsujący oczekiwał, że go tam będzie.
Nie jestem pewien, czy uznałbym to za „regułę” iz pewnością nie jest to coś, do czego stosuję się religijnie. Najbardziej rozsądny kod będzie wiedział, jak parsować tekst (w tym kodowanie) wiersz po wierszu (dowolny wybór zakończeń linii), z lub bez nowego wiersza w ostatnim wierszu.
Rzeczywiście - jeśli skończysz z nową linią: czy (teoretycznie) jest pusta linia końcowa między EOL a EOF? Do rozważenia ...
źródło
Istnieje również praktyczny problem z programowaniem, w którym na końcu brakuje plików nowego wiersza:
read
Wbudowane Bash (nie wiem o innychread
implementacjach) nie działa zgodnie z oczekiwaniami:To drukuje tylko
foo
! Powodem jest to, że gdyread
napotka ostatni wiersz, zapisuje zawartość,$line
ale zwraca kod wyjścia 1, ponieważ osiągnął EOF. To przerywawhile
pętlę, więc nigdy nie osiągamyecho $line
części. Jeśli chcesz poradzić sobie z tą sytuacją, musisz wykonać następujące czynności:To znaczy, wykonaj
echo
jeśliread
nie powiodło się z powodu niepustej linii na końcu pliku. Oczywiście w tym przypadku na wyjściu będzie jeden dodatkowy nowy wiersz, którego nie było na wejściu.źródło
Dobrze wyrażone przez wielu, ponieważ:
Wiele programów nie zachowuje się dobrze lub kończy się niepowodzeniem.
Nawet programy, które dobrze obsługują plik, nie mają zakończenia
'\n'
, funkcjonalność narzędzia może nie spełniać oczekiwań użytkownika - co może być niejasne w tym narożnym przypadku.Programy rzadko zabraniają finału
'\n'
(nie znam żadnego).Ale to nasuwa kolejne pytanie:
Najważniejsze - nie pisz kodu, który zakłada, że plik tekstowy kończy się znakiem nowej linii . Zakładanie, że plik jest zgodny z formatem, prowadzi do uszkodzenia danych, ataków hakerów i awarii. Przykład:
Jeśli końcowy ślad
'\n'
jest potrzebny, powiadom użytkownika o jego braku i podjętych działaniach. IOW, sprawdź format pliku. Uwaga: może to obejmować ograniczenie maksymalnej długości linii, kodowania znaków itp.Zdefiniuj jasno, dokument, sposób obsługi brakującego finału przez kod
'\n'
.Nie generuj , jak to możliwe, pliku, który nie ma zakończenia
'\n'
.źródło
Jest tu bardzo późno, ale napotkałem tylko jeden błąd w przetwarzaniu plików, który pojawił się, ponieważ pliki nie kończyły się pustym znakiem nowej linii. Przetwarzaliśmy pliki tekstowe
sed
ised
pomijaliśmy ostatni wiersz z danych wyjściowych, co powodowało nieprawidłową strukturę JSON i wysyłanie pozostałej części procesu do stanu awarii.Wszystko, co robiliśmy, to:
Jest jeden przykładowy plik:
foo.txt
zjson
zawartością.Plik został utworzony w maszynie dla wdów, a skrypty okna przetwarzały ten plik za pomocą poleceń PowerShell. Wszystko dobrze.
Kiedy przetwarzaliśmy ten sam plik za pomocą
sed
poleceniased 's|value|newValue|g' foo.txt > foo.txt.tmp
Nowo wygenerowany plik to
i boom, zawiodło pozostałe procesy z powodu niepoprawnego JSON.
Dlatego zawsze dobrą praktyką jest kończenie pliku pustą nową linią.
źródło
Zawsze miałem wrażenie, że reguła pochodzi z dni, kiedy parsowanie pliku bez kończącego nowego wiersza było trudne. Oznacza to, że skończyłbyś pisaniem kodu, w którym koniec linii został zdefiniowany przez znak EOL lub EOF. Po prostu łatwiej było założyć linię zakończoną EOL.
Jednak uważam, że reguła ta wywodzi się z kompilatorów C wymagających nowej linii. I jak wskazano w ostrzeżeniu kompilatora „Brak nowej linii na końcu pliku” , #include nie doda nowej linii.
źródło
Wyobraź sobie, że plik jest przetwarzany, gdy plik jest nadal generowany przez inny proces.
Może to mieć z tym związek? Flaga wskazująca, że plik jest gotowy do przetworzenia.
źródło
Osobiście lubię nowe wiersze na końcu plików kodu źródłowego.
Może mieć pochodzenie w Linuksie lub we wszystkich systemach UNIX. Pamiętam, że wystąpiły błędy kompilacji (gcc, jeśli się nie mylę), ponieważ pliki kodu źródłowego nie zakończyły się pustą nową linią. Dlaczego zrobiono to w ten sposób, można się zastanawiać.
źródło
IMHO, to kwestia osobistego stylu i opinii.
W dawnych czasach nie wstawiałem tej nowej linii. Zapisana postać oznacza większą prędkość dzięki modemowi 14,4 tys.
Później umieściłem tę nową linię, aby łatwiej było wybrać ostatnią linię za pomocą Shift + Strzałka w dół.
źródło