A che serve aggiungere una nuova riga alla fine di un file?


166

Alcuni compilatori (in particolare quelli C o C ++) forniscono avvisi su:

No new line at end of file

Ho pensato che questo sarebbe stato un problema solo per i programmatori C, ma github visualizza un messaggio nella vista commit:

\ No newline at end of file

per un file PHP.

Capisco la cosa preprocessore spiegata in questo thread , ma cosa c'entra questo con PHP? È la stessa include()cosa o è correlato all'argomento \r\nvs \n?

Qual è il punto di avere una nuova riga alla fine di un file?



2
Fare incazzare la gente.
Andrew,

4
Se si è catil file, il prompt successivo verrà aggiunto alla "riga" finale se non termina con una nuova riga.
Aaron Franke,

Risposte:


188

Non si tratta di aggiungere una nuova riga alla fine di un file, non si tratta di rimuovere la nuova riga che dovrebbe essere lì.

Un file di testo , in unix, è costituito da una serie di righe , ciascuna delle quali termina con un carattere di nuova riga ( \n). Un file che non è vuoto e non termina con una nuova riga non è quindi un file di testo.

Le utility che dovrebbero funzionare su file di testo potrebbero non far fronte bene ai file che non terminano con una nuova riga; i programmi di utilità storici Unix potrebbero ignorare il testo dopo l'ultima riga, ad esempio. Le utility GNU hanno una politica di comportamento decente con i file non di testo, così come la maggior parte delle altre utility moderne, ma potresti comunque riscontrare un comportamento strano con i file che mancano di una nuova riga finale¹.

Con GNU diff, se uno dei file confrontati termina con una nuova riga ma non con l'altro, è importante notare questo fatto. Poiché diff è orientato alla linea, non può indicarlo memorizzando una nuova riga per uno dei file ma non per gli altri - le nuove righe sono necessarie per indicare dove ogni riga nel file diff inizia e finisce. Quindi diff usa questo testo speciale \ No newline at end of fileper differenziare un file che non è finito in una nuova riga da un file che lo ha fatto.

A proposito, in un contesto C, un file sorgente è similmente formato da una serie di linee. Più precisamente, un'unità di traduzione viene visualizzata in un'implementazione definita come una serie di righe, ognuna delle quali deve terminare con un carattere di nuova riga ( n1256 §5.1.1.1). Sui sistemi unix, la mappatura è semplice. Su DOS e Windows, ogni sequenza CR LF ( \r\n) è mappata su una nuova riga ( \n; questo è ciò che accade sempre quando si legge un file aperto come testo su questi sistemi operativi). Ci sono alcuni sistemi operativi là fuori che non hanno un carattere di nuova riga, ma invece hanno record di dimensioni fisse o variabili; su questi sistemi, la mappatura dai file alla sorgente C introduce a\nalla fine di ogni record. Anche se questo non è direttamente rilevante per unix, significa che se copi un file sorgente C in cui manca la sua nuova riga finale in un sistema con file di testo basati su record, quindi copiarlo indietro, finirai con l'incompleto ultima linea troncata nella conversione iniziale o una nuova riga aggiuntiva attaccata su di essa durante la conversione inversa.

¹ Esempio: l'output dell'ordinamento GNU termina sempre con una nuova riga. Quindi se nel file foomanca la sua nuova riga finale, scoprirai che sort foo | wc -criporta un carattere in più di cat foo | wc -c.


Per quanto riguarda "... serie di righe, ognuna delle quali deve terminare con un carattere di nuova riga (n1256 §5.1.1.1)" -> Nel riesaminare un C11dr N1570 più recente, non è stato trovato supporto per quello diverso da forse: "Un file di origine che non è vuoto deve terminare con un carattere di nuova riga, che non deve essere immediatamente preceduto da un carattere di barra rovesciata prima che avvenga una simile giunzione." §5.1.1.2 2, ma ciò sembra essere limitato alle specifiche di giunzione.
chux

@chux Quella frase è presente anche nell'n1256. L'ultima riga deve terminare con un carattere di nuova riga. Le righe che non sono l'ultima riga devono ovviamente terminare anche con un carattere di nuova riga per indicare che quella riga termina e inizia la riga successiva. Pertanto ogni riga deve terminare con un carattere di nuova riga.
Gilles,

Hmmm, per me, quella riga "" Un file sorgente ... ha luogo lo splicing "potrebbe essere limitata al modo in cui considerazioni sullo splicing e non ai file in generale. Eppure vedo come si potrebbe vedere diversamente. Forse cercherò un post che si concentra su quello.
Chux

> "Quindi diff utilizza questo testo speciale \ No newline alla fine del file per differenziare un file che non è terminato in una nuova riga da un file che lo ha fatto." Git mostra questo testo non solo quando confronta i file. Ma anche quando un nuovo file è stato aggiunto a git. Quindi questo argomento non è valido, suppongo.
Viktor Kruglikov,

> "Le utility che dovrebbero funzionare su file di testo potrebbero non far fronte bene ai file che non terminano con una nuova riga" Non penso che sia compito di Git preoccuparsi di problemi di così basso livello come il mancato \ n a causa di POSIX requisiti. Penso che se git mostra questo messaggio, la ragione dovrebbe essere nei problemi di controllo del codice sorgente .
Viktor Kruglikov,

42

Non necessariamente il motivo, ma una conseguenza pratica dei file che non terminano con una nuova riga:

Considera cosa succederebbe se volessi elaborare diversi file usando cat. Ad esempio, se si desidera trovare la parola fooall'inizio della riga su 3 file:

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

Se la prima riga in file3 inizia con foo, ma file2 non ha una finale \ndopo l'ultima riga, questa occorrenza non sarebbe trovata da grep, perché l'ultima riga in file2 e la prima riga in file3 sarebbero viste da grep come una singola linea.

Quindi, per coerenza e per evitare sorprese, cerco di mantenere i miei file finendo sempre con una nuova riga.


Ma è compito di git preoccuparsi della concatenazione dei file?
Viktor Kruglikov,

Non ha ragione a pensare che dovresti semplicemente mettere in scena '\n'l'operazione con il gatto ...
Andrew,

3
È come dire "A volte aggiungo stringhe insieme che hanno \no spazi bianchi alle estremità, quindi per mantenere le cose coerenti, metto sempre \n _____ad entrambe le estremità delle mie stringhe". Bene, no, la cosa giusta da fare lì è tagliare le corde e poi concatenarle correttamente.
Andrew,

16

Ci sono due aspetti:

  1. Ci sono / erano alcuni compilatori C che non possono analizzare l'ultima riga se non termina con una nuova riga. Lo standard C specifica che un file C dovrebbe terminare con una nuova riga (C11, 5.1.1.2, 2.) e che un'ultima riga senza una nuova riga produce un comportamento indefinito (C11, J.2, 2a voce). Forse per ragioni storiche, perché alcuni venditori di un simile compilatore facevano parte del comitato quando fu scritto il primo standard. Quindi l'avvertimento di GCC.

  2. diffi programmi (come quelli usati da git diff, github ecc.) mostrano differenze riga per riga tra i file. Di solito stampano un messaggio quando un solo file termina con una nuova riga perché altrimenti non vedresti questa differenza. Ad esempio, se l'unica differenza tra due file è la presenza dell'ultimo carattere di nuova riga, senza il suggerimento sembrerebbe che entrambi i file fossero uguali, quando diffe cmprestituire un codice di uscita diseguale successo e i checksum dei file (ad es. Via md5sum) non corrispondono.


ha senso con il programma diff
Thamaraiselvam

Sembra che i diff dovrebbero essere più intelligenti.
Andrew,

@Andrew, no, non lo fa. diffsi prevede di stampare le differenze se ce ne sono. E se un file ha una nuova riga come ultimo carattere mentre l'altro no, allora quella differenza deve essere in qualche modo evidente nell'output.
maxschlepzig,

La tua ultima affermazione è corretta. Tuttavia, il visualizzatore di differenze non deve visualizzare "newlines" ( \n) per cominciare, può invece semplicemente mostrare "new lines".
Andrew,

10

Quello \ No newline at end of fileche ottieni da github appare alla fine di una patch (in diffformato , vedi la nota alla fine della sezione "Unified Format").

I compilatori non si preoccupano se c'è una nuova riga o meno alla fine di un file, ma git(e le diff/ patchutility) devono tenerne conto. Ci sono molte ragioni per questo. Ad esempio, dimenticare di aggiungere o rimuovere una nuova riga alla fine di un file cambierebbe il suo hashsum ( md5sum/ sha1sum). Inoltre, i file non sono sempre programmi e un finale \npotrebbe fare la differenza.

Nota : A proposito dell'avvertimento dei compilatori C, suppongo che insistano per una nuova riga finale ai fini della compatibilità con le versioni precedenti. I compilatori molto vecchi potrebbero non accettare l'ultima riga se non termina con \n(o altra sequenza di caratteri di fine riga dipendente dal sistema).


7
"Immagino che insistano per una nuova riga finale ai fini della retrocompatibilità" - No, insistono perché lo standard C lo impone .
MestreLion,

1
@MestreLion C richiede una nuova riga finale per il codice sorgente C (C11 §5.1.1.2 2). Si noti che per l' I / O del file di testo , C ha "Definito l'implementazione se l'ultima riga richiede un carattere di nuova riga che termina". §7.21.2 2
chux

Chi usa compilatori molto vecchi? Smetti di usarli.
Andrew,

1
@MestreLion: E perché pensi che lo standard C lo imponga ...
Stéphane Gimenez,

@ StéphaneGimenez: coerenza, migliore compatibilità e interoperabilità tra diversi sistemi operativi (POSIX definisce anche le linee che terminano in '\ n')
MestreLion,

4

C'è anche il punto di mantenere la cronologia delle differenze. Se un file termina senza un carattere di nuova riga, l'aggiunta di qualcosa alla fine del file verrà visualizzata dalle utility diff come modifica dell'ultima riga (perché \nviene aggiunta ad esso).

Ciò potrebbe causare risultati indesiderati con comandi come git blamee hg annotate.


Sembra che i diff debbano solo essere più intelligenti.
Andrew,

1
Gli strumenti diversi sono intelligenti. Notano la sottile modifica al file (che è importante perché cambierà inevitabilmente l'hash del file). E sia GNU diff che git diff accettano -wun'opzione per ignorare i cambiamenti di spazio bianco quando si trasmettono dati per l'uomo.
joeytwiddle

4

POSIX, questo è un insieme di standard specificati dall'IEEE per mantenere la compatibilità tra i sistemi operativi.

Uno dei quali è la definizione di una "linea" che è una sequenza di zero o più non caratteri più un carattere di nuova riga che termina.

Quindi, affinché l'ultima riga sia riconosciuta come una "linea" effettiva, dovrebbe avere un carattere di nuova riga che termina.

Questo è importante se dipendi dagli strumenti del sistema operativo per dire il conteggio delle righe o dividere / aiutare ad analizzare il tuo file. Dato che PHP è un linguaggio di script, è del tutto possibile soprattutto nei suoi primi giorni o anche ora (non ho idea / postulazione) aveva dipendenze del sistema operativo del genere.

In realtà, la maggior parte dei sistemi operativi non è completamente conforme a POSIX e gli umani non sono simili alla macchina o si preoccupano nemmeno di terminare nuove linee. Quindi, per la maggior parte delle cose, è una smorgasbord di tutto ciò che si prende cura di esso, avvisa o semplicemente che l'ultimo pezzo di testo sia davvero una linea, quindi includilo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.