Brak nowego wiersza na końcu pliku

471

Podczas robienia git diffnapis „Brak nowej linii na końcu pliku” .

Ok, na końcu pliku nie ma nowego wiersza. O co tyle szumu?

Jakie znaczenie ma przesłanie i co próbuje nam powiedzieć?

Pacerier
źródło
11
Być może, jeśli masz plik, który kończy się bez nowej linii, i dodajesz inną linię, git musiałby pokazać, że poprzednia ostatnia linia uległa zmianie, ponieważ zawiera znak nowej linii jako część linii?
nafg

Odpowiedzi:

458

Wskazuje, że nie masz nowego wiersza (zwykle '\n'aka CR lub CRLF) na końcu pliku.

To po prostu ostatni bajt (lub bajty w systemie Windows) w pliku nie jest znakiem nowej linii.

Komunikat jest wyświetlany, ponieważ w przeciwnym razie nie można odróżnić pliku, w którym na końcu znajduje się nowa linia, a gdzie nie ma. Diff i tak musi wypisać nowy wiersz, inaczej wynik będzie trudniejszy do odczytania lub przetworzenia automatycznie.

Zauważ, że dobrym stylem jest zawsze umieszczanie nowego wiersza jako ostatniego znaku, jeśli pozwala na to format pliku. Ponadto, na przykład, w przypadku plików nagłówkowych C i C ++ jest to wymagane przez standard językowy.

Alexander Gladysh
źródło
135
Czy z ciekawości możesz wyjaśnić, dlaczego za dobry styl uważa się zawsze umieszczanie nowej linii za ostatnią postać? Edytuj: znaleziono tę dyskusję .
Paul Bellora,
84
@PaulBellora Historycznie była to decyzja podjęta przez standard języka C stackoverflow.com/a/729725/233098 Praktycznie, ponieważ wiele narzędzi uniksowych wymaga lub oczekuje go do prawidłowego wyświetlania stackoverflow.com/a/729795/233098 . Filozoficznie, ponieważ każda linia w pliku tekstowym kończy się znakiem „końca linii” - ostatni wiersz nie powinien być wyjątkiem. Myśląc o tym inaczej, zbadajmy odwrotność. Jeśli zamiast „końca linii” byłby znacznik „początku linii”, czy pominąłbyś znak „linii początku” w pierwszej linii?
Joe
29
@Joe To nie ma większego sensu. Nowa linia to nowa linia , tzn. Separator między liniami, a nie koniec linii. Nie mamy znaków początku linii, ponieważ nie są one konieczne. Nie mamy znaków końca linii z tego samego powodu.
acjay
6
@acjay Twierdzę, że między „Separator między liniami” a „końcem linii” jest z natury lepsza. Żaden z tych poglądów nie jest z natury właściwy ani zły, tylko jeden sposób, aby na nie spojrzeć. Sugeruję, abyśmy nadal używali punktu widzenia, który jest praktyczny z historycznego punktu widzenia, ponieważ już to robimy w ten sposób i ma to sens, gdy się je akceptuje. Spójność jest ważna. Nie ma potrzeby przerywania tego w imię punktu widzenia „separator między liniami”.
Joe
17
@WORMSS „Nowość dla mnie” to nie to samo, co „nowa konwencja”. To tak, jakby odkryć jakąkolwiek inną konwencję programowania. Po prostu idź z tym. Państwo mogli odstąpić, ale jesteś tylko izolowanie siebie. (Lub w tym przypadku faktycznie łamiące narzędzia.) Zastanów się, ilu innych odkryło jakąś konwencję Railsów lub PEP8 i jak spójne były te społeczności jako całość, ponieważ się poddały - mimo że napisały kod przeciwnie.
Joe
100

To nie tylko zły styl, może prowadzić do nieoczekiwanego zachowania podczas korzystania z innych narzędzi w pliku.

Oto test.txt:

first line
second line

W ostatnim wierszu nie ma znaku nowej linii. Zobaczmy, ile wierszy znajduje się w pliku:

$ wc -l test.txt
1 test.txt

Może właśnie tego chcesz, ale w większości przypadków prawdopodobnie spodziewałbyś się, że w pliku będą 2 linie.

Ponadto, jeśli chcesz połączyć pliki, może nie działać w oczekiwany sposób:

$ cat test.txt test.txt
first line
second linefirst line
second line

Wreszcie, spowodowałoby to, że twoje różnice byłyby nieco głośniejsze, gdybyś dodał nową linię. Jeśli dodasz trzeci wiersz, wyświetli się edycja drugiego wiersza, a także nowy dodatek.

Dziekan
źródło
4
Wynik cat jest w porządku, ale parametr wc „-l, --lines” jest po prostu błędny. Nawet w podręczniku jest napisane: „drukuj liczby nowych linii”, a nie „drukuj liczby nowych linii”.
Niesamowity
I nie mogę nawet tego odtworzyć (wc i cat) z najnowszym util linux (util-linux 2.34).
wget
1
@wget Jestem na util-linux 2.34 i może potwierdzić, że to, co opisuje ta odpowiedź, to bieżące zachowanie. Domyślam się, że twój edytor dodał znak „\ n”.
stephanos
29

Jedynym powodem jest to, że w Uniksie historycznie istniała konwencja wszystkich czytelnych dla ludzi plików tekstowych kończących się nową linią. W tym czasie unikano dodatkowego przetwarzania podczas wyświetlania lub dołączania plików tekstowych i unikano traktowania plików tekstowych inaczej niż plików zawierających inne rodzaje danych (np. Surowe dane binarne, które nie są czytelne dla człowieka).

Z powodu tej konwencji wiele narzędzi z tamtej epoki oczekuje końca nowej linii, w tym edytory tekstu, narzędzia różnicowania i inne narzędzia do przetwarzania tekstu. Mac OS X został zbudowany na BSD Unix, a Linux został opracowany tak, aby był kompatybilny z Unixem, więc oba systemy operacyjne odziedziczyły tę samą konwencję, zachowanie i narzędzia.

System Windows nie został opracowany pod kątem zgodności z systemem Unix, więc nie ma tej samej konwencji, a większość oprogramowania Windows poradzi sobie dobrze bez końca nowej linii.

Ale odkąd Git został opracowany najpierw dla Linuksa, a wiele programów typu open source jest zbudowanych na systemach kompatybilnych z Unixem, takich jak Linux, Mac OS X, FreeBSD itp., Większość społeczności open source i ich narzędzia (w tym języki programowania) nadal trwają przestrzegać tych konwencji.

Istnieją powody techniczne, które miały sens w 1971 roku, ale w tej erze jest to głównie konwencja i utrzymanie zgodności z istniejącymi narzędziami.

Nathan Craike
źródło
23

Jeśli dodasz nową linię tekstu na końcu istniejącego pliku, który nie ma jeszcze newline characterna końcu, diff pokaże starą ostatnią linię jako zmodyfikowaną, nawet jeśli koncepcyjnie tak nie było.

Jest to co najmniej jeden dobry powód, aby dodać newline characterna końcu.

Przykład

Plik zawiera:

A() {
    // do something
}

Hexdump:

00000000: 4128 2920 7b0a 2020 2020 2f2f 2064 6f20  A() {.    // do 
00000010: 736f 6d65 7468 696e 670a 7d              something.}

Teraz go edytujesz

A() {
    // do something
}
// Useful comment

Hexdump:

00000000: 4128 2920 7b0a 2020 2020 2f2f 2064 6f20  A() {.    // do 
00000010: 736f 6d65 7468 696e 670a 7d0a 2f2f 2055  something.}.// U
00000020: 7365 6675 6c20 636f 6d6d 656e 742e 0a    seful comment..

Git diff pokaże:

-}
\ No newline at end of file
+}
+// Useful comment.

Innymi słowy, pokazuje większą różnicę niż występowała koncepcyjnie. To pokazuje, że usunąłeś linię }i dodałeś ją }\n. W rzeczywistości tak się stało, ale nie jest to koncepcyjne , więc może być mylące.

Jaseem
źródło
2
Możemy napisać to samo w innym kierunku: jeśli usuniesz nową linię na końcu istniejącego pliku, który ma już nową linię na końcu, diff pokaże starą ostatnią linię również jako zmodyfikowaną, jeśli koncepcyjnie jej nie ma. Przynajmniej jeden dobry powód, aby usunąć nowy wiersz na końcu.
gentiane
3
@gentiane Mylą się „nowa linia” (nowa linia) i „nowa linia” (1 lub 2 znaki ograniczające koniec linii)
minexew
@minexew Nie, Gentiane nie jest. Może po prostu nie zdajesz sobie sprawy, że „nowa linia” jest taka sama jak „nowa linia”.
Niesamowity
3
@TheincredibleJan Sposób, w jaki są użyte w odpowiedzi, oba terminy mają odrębne znaczenie. Nie wiem, czy starasz się być mądrym dupkiem, czy po prostu źle rozumiesz, co się dzieje.
minexew
18

Wskazuje tylko, że koniec pliku nie ma nowej linii. To nie jest katastrofa, to tylko wiadomość, aby wyjaśnić, że nie ma żadnej, gdy patrzy się na różnicę w linii poleceń.

JohnD
źródło
10

Powodem, dla którego ta konwencja weszła w życie, jest to, że w systemach operacyjnych typu UNIX znak nowej linii jest traktowany jako terminator linii i / lub granica komunikatów (obejmuje to przesyłanie potokowe między procesami, buforowanie linii itp.).

Rozważmy na przykład, że plik zawierający tylko znak nowej linii jest traktowany jako pojedynczy pusty wiersz. I odwrotnie, plik o długości zero bajtów jest w rzeczywistości pustym plikiem z zerowymi liniami. Można to potwierdzić zgodnie zwc -l poleceniem.

W sumie takie zachowanie jest uzasadnione, ponieważ nie byłoby innego sposobu rozróżnienia pustego pliku tekstowego od pliku tekstowego z pojedynczą pustą linią, gdyby \nznak był jedynie separatorem linii, a nie zakończeniem linii. Dlatego prawidłowe pliki tekstowe powinny zawsze kończyć się znakiem nowej linii. Jedynym wyjątkiem jest sytuacja, gdy plik tekstowy ma być pusty (bez wierszy).

Leslie Krause
źródło
1
Dlaczego jestem przegłosowany -2? Zwróciłem uwagę nie tylko na potwierdzenie innych odpowiedzi (tj. Standardowe narzędzia oparte na UNIX-u oczekują nowej linii jako terminatora linii), ale także na to, że nie ma sposobu na odróżnienie pustego pliku od pojedynczej pustej linii, co jest absolutną prawdą . Odpowiedziałem konkretnie na pierwotne pytanie „Jakie jest znaczenie wiadomości i co ona próbuje nam powiedzieć?”
Leslie Krause,
Nie głosowałem za tobą, ale ta odpowiedź wydaje się być specyficzna dla systemów typu Unix, ponieważ ma zastosowanie tylko wtedy, gdy nowa linia jest tylko znakiem nowej linii. Nie jest jasne, czy dotyczy to tutaj. Ostrzeżenie wydaje się również bezużyteczne, jeśli plik składa się z pustej linii. Unikam jednak Stackoverflow, ponieważ ludzie często głosują bez wyjaśnienia.
user34660
9

Jest jedna rzecz, której nie widzę w poprzednich odpowiedziach. Ostrzeżenie o braku końca linii może być ostrzeżeniem, gdy część pliku zostanie obcięta. Może to być objaw braku danych.

użytkownik34660
źródło
Dobra uwaga w ogóle, ale nie sądzę, aby miało to sens w kontekście tego konkretnego pytania.
cst1992
@ cst1992 Odpowiedzi w Stackoverflow powinny być jak najbardziej przydatne, co oznacza, że ​​mają zastosowanie do wszystkich możliwości. Pytanie jest krótkie i nie widzę, gdzie wyklucza to możliwość, którą zasugerowałem.
user34660
7

Podstawowym problemem jest to, co definiujesz linię i czy sekwencja znaków end-on-line jest częścią linii, czy nie. Edytory oparte na UNIX (takie jak VIM) lub narzędzia (takie jak Git) używają sekwencji znaków EOL jako terminatora linii, dlatego jest to część linii. Jest podobny do używania średnika (;) w C i Pascalu. W C średnik kończy instrukcje, w Pascal oddziela je.

mmcorrelo
źródło
4

To faktycznie powoduje problem, ponieważ zakończenia linii są automatycznie modyfikowane, usuwając pliki bez wprowadzania w nich żadnych zmian. Zobacz ten post w celu rozwiązania.

git zastępuje LF przez CRLF

Brian Blum
źródło
3

Pliki źródłowe są często łączone przez narzędzia (C, C ++: pliki nagłówkowe, Javascript: programy pakujące). Jeśli pominiesz znak nowej linii, możesz wprowadzić nieprzyjemne błędy (gdzie ostatnia linia jednego źródła jest połączona z pierwszą linią następnego pliku źródłowego). Mam nadzieję, że wszystkie narzędzia konkatacji kodu źródłowego wstawiają nowy wiersz między połączonymi plikami, ale nie zawsze tak jest.

Sedno problemu polega na tym, że - w większości języków znaki nowej linii mają znaczenie semantyczne, a koniec pliku nie jest zdefiniowaną w języku alternatywą dla znaku nowej linii. Więc powinieneś zakończyć każde wyrażenie / wyrażenie znakiem nowej linii - łącznie z ostatnim.

Doug Coburn
źródło
1
W C / C ++ możesz napisać cały projekt w jednym wierszu. Nie ma potrzeby wprowadzania nowej linii.
Niesamowity
Ty mógł napisać cały swój projekt w jednej linii ... jeśli nie używać //komentarz styl w środku kodu.
Doug Coburn,
2

Twój oryginalny plik prawdopodobnie nie miał znaku nowej linii.

Jednak niektóre edytory, takie jak gedit w systemie Linux, po cichu dodają nową linię na końcu pliku. Nie możesz pozbyć się tej wiadomości podczas korzystania z tego rodzaju edytorów.

Próbowałem przezwyciężyć ten problem, aby otworzyć plik za pomocą edytora kodu Visual Studio

Ten edytor wyraźnie pokazuje ostatni wiersz i możesz go usunąć, jak chcesz.

Berkay92
źródło
0

O ile warto, napotkałem to, gdy utworzyłem projekt IntelliJ na komputerze Mac, a następnie przeniosłem projekt na moją maszynę z systemem Windows. Musiałem ręcznie otworzyć każdy plik i zmienić ustawienie kodowania w prawym dolnym rogu okna IntelliJ. Prawdopodobnie nie zdarzyło się to większości, jeśli ktokolwiek czytał to pytanie, ale to mogło zaoszczędzić mi kilka godzin pracy ...

Lou Morda
źródło