Po co dodawać nową linię na końcu pliku?

165

Niektóre kompilatory (zwłaszcza C lub C ++) ostrzegają o:

No new line at end of file

Myślałem, że będzie to problem tylko dla programistów C, ale github wyświetla komunikat w widoku zatwierdzania:

\ No newline at end of file

dla pliku PHP.

Rozumiem proces preprocesora wyjaśniony w tym wątku , ale co to ma wspólnego z PHP? Czy to to samo include(), czy jest związane z tematem \r\nvs \n?

Po co mieć nową linię na końcu pliku?

Philipp Stephan
źródło
Duplikat z SO: stackoverflow.com/questions/729692/...
AlikElzin-kilaka
2
Wkurzyć ludzi.
Andrew
3
Jeśli masz catplik, następny monit zostanie dodany do ostatniej „linii”, jeśli nie kończy się na nowej linii.
Aaron Franke

Odpowiedzi:

185

Tu nie chodzi o dodanie nowej linii na końcu pliku, nie chodzi o usunięcie nowej linii, która powinna tam być.

Plik tekstowy , pod Uniksem, składa się z szeregu linii , z których każda kończy się znakiem nowej linii ( \n). Plik, który nie jest pusty i nie kończy się na nowej linii, nie jest zatem plikiem tekstowym.

Narzędzia, które powinny działać na plikach tekstowych, mogą nie radzić sobie dobrze z plikami, które nie kończą się znakiem nowej linii; historyczne narzędzia uniksowe mogą na przykład ignorować tekst po ostatniej nowej linii. Programy narzędziowe GNU zachowują się przyzwoicie w przypadku plików nietekstowych, podobnie jak większość innych nowoczesnych programów narzędziowych, ale nadal możesz napotkać dziwne zachowanie w przypadku plików, które nie mają ostatniej nowej linii¹.

W przypadku GNU diff, jeśli jeden z porównywanych plików kończy się znakiem nowej linii, ale nie drugim, należy to zauważyć. Ponieważ diff jest zorientowany liniowo, nie może tego wskazać, przechowując nową linię dla jednego z plików, ale nie dla innych - nowe linie są konieczne do wskazania, gdzie każda linia w pliku diff zaczyna się i kończy. Tak więc diff używa tego specjalnego tekstu \ No newline at end of filedo odróżnienia pliku, który nie kończył się nową linią od pliku, który tak zrobił.

Nawiasem mówiąc, w kontekście C plik źródłowy podobnie składa się z szeregu linii. Mówiąc dokładniej, jednostka tłumacząca jest postrzegana w implementacji zdefiniowanej jako seria wierszy, z których każda musi kończyć się znakiem nowej linii ( n1256 §5.1.1.1). W systemach uniksowych mapowanie jest proste. W systemach DOS i Windows każda sekwencja CR LF ( \r\n) jest odwzorowana na nową linię ( \n; to zawsze dzieje się podczas odczytu pliku otwartego jako tekst w tych systemach operacyjnych). Istnieje kilka systemów operacyjnych, które nie mają znaku nowej linii, ale zamiast tego mają rekordy o stałej lub zmiennej wielkości; w tych systemach mapowanie plików do źródła C wprowadza\nna końcu każdego rekordu. Chociaż nie ma to bezpośredniego związku z Uniksem, oznacza to, że jeśli skopiujesz plik źródłowy C, w którym brakuje jego ostatniej nowej linii, do systemu z plikami tekstowymi opartymi na rekordach, a następnie skopiujesz go z powrotem, albo skończysz z niekompletnym ostatnia linia została obcięta w początkowej konwersji lub dodatkowa nowa linia przyczepiona do niej podczas konwersji do tyłu.

¹ Przykład: wynik sortowania GNU zawsze kończy się nową linią. Więc jeśli w pliku foobrakuje ostatniego nowego wiersza, zauważysz, że sort foo | wc -czgłasza on o jeden znak więcej niż cat foo | wc -c.

Gilles
źródło
Jeśli chodzi o „... serię wierszy, z których każda musi kończyć się znakiem nowej linii (n1256 §5.1.1.1)” -> Przeglądając nowszą wersję C11dr N1570, nie znalazł wsparcia dla innych niż może: „Plik źródłowy, który nie jest pusty, kończy się znakiem nowej linii, który nie może być poprzedzony znakiem odwrotnego ukośnika, zanim nastąpi takie splicing.” § 5.1.1.2 2, ale wydaje się, że ogranicza się to do specyfikacji łączenia.
chux
@chux To zdanie jest również obecne w n1256. Ostatni wiersz musi kończyć się znakiem nowej linii. Linie, które nie są ostatnimi liniami, muszą oczywiście kończyć się znakiem nowej linii, wskazując, że linia ta kończy się i zaczyna kolejna linia. Dlatego każda linia musi kończyć się znakiem nowej linii.
Gilles
Hmmm, dla mnie ten wiersz „Plik źródłowy ... splicing ma miejsce.” Może być ograniczony do tego, w jaki sposób rozważania dotyczące splicingu, a nie plików w ogóle. Jednak widzę, jak można by inaczej spojrzeć. Być może poszuka posta na tym się skupia
chux,
> „Więc diff używa tego specjalnego tekstu \ Brak nowej linii na końcu pliku, aby odróżnić plik, który nie kończy się nową linią od pliku, który to zrobił.” Git pokazuje ten tekst nie tylko podczas porównywania plików. Ale nawet po dodaniu nowego pliku do git. Więc ten argument jest nieważny.
Viktor Kruglikov
> „Narzędzia, które powinny działać na plikach tekstowych, mogą nie radzić sobie dobrze z plikami, które nie kończą się na nowej linii” Nie sądzę, że troską jest dbanie o problemy z niskim poziomem, takie jak brak \ nz powodu POSIX wymagania Myślę, że jeśli git wyświetla ten komunikat, przyczyną powinny być problemy z kontrolą źródła .
Viktor Kruglikov,
41

Niekoniecznie przyczyna, ale praktyczna konsekwencja plików, które nie kończą się na nowej linii:

Zastanów się, co by się stało, gdybyś chciał przetworzyć kilka plików przy użyciu cat. Na przykład, jeśli chcesz znaleźć słowo foona początku wiersza w 3 plikach:

cat file1 file2 file3 | grep -e '^foo'

Jeśli pierwsza linia w pliku3 zaczyna się od foo, ale plik2 nie ma końca \npo ostatniej linii, to wystąpienie nie zostanie znalezione przez grep, ponieważ ostatnia linia w pliku2 i pierwsza linia w pliku3 byłyby widoczne przez grep jako pojedynczy linia.

Aby zachować spójność i uniknąć niespodzianek, staram się, aby moje pliki zawsze kończyły się nową linią.

Sergio Acosta
źródło
Ale czy to git dbać o łączenie plików?
Viktor Kruglikov
Czy nie oznacza to, że powinieneś po prostu wziąć udział '\n'w operacji kota ...
Andrew
3
To tak, jakby powiedzieć: „Czasami łączę ze sobą ciągi \nznaków, które mają białe znaki lub białe znaki, więc aby zachować spójność, zawsze umieszczam \n _____oba końce moich łańcuchów”. Cóż, nie, właściwą rzeczą do zrobienia jest przycięcie Strun, a następnie ich prawidłowe połączenie.
Andrew
16

Istnieją dwa aspekty:

  1. Istnieją / były niektóre kompilatory C, które nie mogą przeanalizować ostatniego wiersza, jeśli nie kończy się on nowym wierszem. Standard C określa, że ​​plik C powinien kończyć się nową linią (C11, 5.1.1.2, 2.) i że ostatni wiersz bez nowej linii daje niezdefiniowane zachowanie (C11, J.2, 2. pozycja). Być może z przyczyn historycznych, ponieważ jakiś sprzedawca takiego kompilatora był częścią komitetu, kiedy napisano pierwszy standard. Zatem ostrzeżenie GCC.

  2. diffprogramy (takie jak używane przez git diffgithub itp.) pokazują różnice między wierszami między plikami. Zwykle drukują komunikat, gdy tylko jeden plik kończy się znakiem nowej linii, ponieważ inaczej nie zobaczyłbyś tej różnicy. Na przykład, jeśli jedyną różnicą między dwoma plikami jest obecność ostatniego znaku nowej linii, bez podpowiedzi wyglądałoby to tak, jakby oba pliki były takie same, kiedy diffi cmpzwracają kod wyjścia niejednakowy sukces i sumy kontrolne plików (np. Przez md5sum) nie pasują.

maxschlepzig
źródło
ma sens z programem diff
Thamaraiselvam
Brzmi jak różnice powinny być po prostu mądrzejsze.
Andrew
@Andrew, nie, nie ma. diffoczekuje się, że wydrukuje różnice, jeśli takie istnieją. A jeśli jeden plik ma nowy wiersz jako ostatni znak, podczas gdy drugi go nie ma, to różnica musi być jakoś zauważalna w wyniku.
maxschlepzig
Twoje ostatnie stwierdzenie jest poprawne. Jednak przeglądarka różnic nie musi na początku wyświetlać „nowych linii” ( \n), może po prostu pokazywać „nowe linie”.
Andrew
10

Otrzymane \ No newline at end of filez github pojawia się na końcu łatki (w diffformacie , patrz uwaga na końcu sekcji „Unified Format”).

Kompilatory nie dbają o to, czy na końcu pliku jest nowa linia, czy nie, ale git(i diff/ patchutilities) muszą wziąć to pod uwagę. Jest wiele powodów. Na przykład zapomnienie o dodaniu lub usunięciu nowego wiersza na końcu pliku zmieniłoby jego skrót ( md5sum/ sha1sum). Ponadto pliki nie zawsze są programami, a finał \nmoże coś zmienić.

Uwaga : Jeśli chodzi o ostrzeżenie z kompilatorów C, myślę, że nalegają na końcową nową linię dla celów zgodności wstecznej. Bardzo stare kompilatory mogą nie zaakceptować ostatniego wiersza, jeśli się nie kończą \n(lub inną zależną od systemu sekwencją znakową końca wiersza).

Stéphane Gimenez
źródło
7
„Wydaje mi się, że nalegają na ostateczny nowy wiersz do celów kompatybilności wstecznej” - Nie, upierają się przy tym, ponieważ nakazuje to standard C.
MestreLion
1
@MestreLion C wymaga ostatniej nowej linii dla kodu źródłowego C (C11 §5.1.1.2 2). Zauważ, że dla I / O pliku tekstowego C ma „To, czy ostatni wiersz wymaga zakończenia znaku nowej linii, jest zdefiniowane w implementacji”. § 7.21.2 2
Chux
Kto używa bardzo starych kompilatorów? Przestań ich używać.
Andrew
1
@MestreLion: A jak myślisz, dlaczego standard C nakazuje…
Stéphane Gimenez
@ StéphaneGimenez: spójność, lepsza kompatybilność i interoperacyjność między różnymi systemami operacyjnymi (POSIX definiuje także linie kończące się na „\ n”)
MestreLion
4

POSIX, jest to zestaw standardów określonych przez IEEE w celu utrzymania zgodności między systemami operacyjnymi.

Jednym z nich jest definicja „linii”, która jest ciągiem zerowym lub większą liczbą znaków niebędących znakami oraz kończącym znakiem nowej linii.

Aby ostatnia linia została rozpoznana jako rzeczywista „linia”, powinna ona mieć znak kończący nowy wiersz.

Jest to ważne, jeśli polegasz na narzędziach systemu operacyjnego, aby powiedzieć liczbę wierszy lub podzielić / pomóc przeanalizować plik. Biorąc pod uwagę, że PHP jest językiem skryptowym, jest całkowicie możliwe, szczególnie w jego początkowych dniach lub nawet teraz (nie mam pojęcia / postuluje), że miał takie zależności OS.

W rzeczywistości większość systemów operacyjnych nie jest w pełni zgodna z POSIX, a ludzie nie są tacy jak maszyny, a nawet nie dbają o zakończenie nowych linii. Więc dla większości rzeczy jest to smorgasbord wszystkiego, co się o to troszczy, ostrzega lub po prostu idzie, że ostatnia część tekstu jest naprawdę wierszem, więc po prostu to dołącz.

użytkownik3379747
źródło
3

Warto również zachować historię różnic. Jeśli plik kończy się bez znaku nowej linii, to dodawanie czegokolwiek na końcu pliku będzie postrzegane przez narzędzia różnicujące jako zmiana tego ostatniego wiersza (ponieważ \njest do niego dodawany).

Może to powodować niepożądane wyniki za pomocą poleceń takich jak git blamei hg annotate.

Hosam Aly
źródło
Brzmi jak diffy, po prostu trzeba być mądrzejszym.
Andrew