Quel est l'intérêt d'ajouter une nouvelle ligne à la fin d'un fichier?

166

Certains compilateurs (notamment C ou C ++) vous avertissent des points suivants:

No new line at end of file

Je pensais que ce serait un problème réservé aux programmeurs C, mais github affiche un message dans la vue commit:

\ No newline at end of file

pour un fichier PHP.

Je comprends le préprocesseur expliqué dans ce fil de discussion , mais qu'est-ce que cela a à voir avec PHP? Est-ce la même include()chose ou est-ce lié au sujet \r\nvs \n?

Quel est l'intérêt d'avoir une nouvelle ligne à la fin d'un fichier?

Philipp Stephan
la source
Dupliquer de SO: stackoverflow.com/questions/729692/...
AlikElzin-Kilaka
2
Pisser les gens.
Andrew
4
Si vous catle fichier, l'invite suivante sera ajoutée à la "ligne" finale si elle ne se termine pas par une nouvelle ligne.
Aaron Franke

Réponses:

188

Il ne s'agit pas d'ajouter une nouvelle ligne à la fin d'un fichier, mais de ne pas supprimer la nouvelle ligne qui devrait y figurer.

Un fichier texte , sous unix, consiste en une série de lignes , chacune d’elles se terminant par un caractère de nouvelle ligne ( \n). Un fichier qui n'est pas vide et qui ne se termine pas par une nouvelle ligne n'est donc pas un fichier texte.

Les utilitaires censés fonctionner sur des fichiers texte risquent de ne pas bien gérer les fichiers ne se terminant pas par une nouvelle ligne; Par exemple, les utilitaires Unix historiques peuvent ignorer le texte après la dernière nouvelle ligne. Les utilitaires GNU ont pour politique de se comporter convenablement avec les fichiers non textuels, comme la plupart des utilitaires modernes, mais vous pouvez toujours rencontrer un comportement étrange avec des fichiers pour lesquels il manque une nouvelle ligne¹.

Avec GNU diff, si l’un des fichiers comparés se termine par une nouvelle ligne mais pas l’autre, il convient de noter ce fait. Comme diff est orienté ligne, il ne peut pas indiquer cela en stockant une nouvelle ligne pour l'un des fichiers mais pas pour les autres; les nouvelles lignes sont nécessaires pour indiquer où chaque ligne du fichier diff commence et se termine. So diff utilise ce texte spécial \ No newline at end of filepour différencier un fichier ne se terminant pas par un retour à la ligne.

À propos, dans un contexte C, un fichier source est constitué d’une série de lignes. Plus précisément, une unité de traduction est vue dans une implémentation définie comme une série de lignes, dont chacune doit se terminer par un caractère de nouvelle ligne ( n1256 §5.1.1.1). Sur les systèmes Unix, le mappage est simple. Sous DOS et Windows, chaque séquence CR LF ( \r\n) est mappée sur une nouvelle ligne ( \nc'est toujours ce qui se produit lors de la lecture d'un fichier ouvert sous forme de texte sur ces systèmes d'exploitation). Il existe quelques systèmes d'exploitation qui n'ont pas de caractère de nouvelle ligne, mais ont plutôt des enregistrements de taille fixe ou variable; sur ces systèmes, le mappage des fichiers vers la source C introduit un\nà la fin de chaque enregistrement. Bien que cela ne concerne pas directement les systèmes Unix, cela signifie que si vous copiez un fichier source C dont le saut de ligne final est manquant sur un système contenant des fichiers texte basés sur des enregistrements, puis que vous le recopiez, vous obtiendrez le fichier incomplet. dernière ligne tronquée lors de la conversion initiale ou ajout d’une nouvelle ligne lors de la conversion inverse.

¹ Exemple: la sortie du tri GNU se termine toujours par une nouvelle ligne. Ainsi, si le fichier foomanque sa nouvelle ligne finale, vous constaterez qu'il sort foo | wc -cindique un caractère de plus que cat foo | wc -c.

Gilles
la source
Concernant "... une série de lignes, chacune d'entre elles doit se terminer par un caractère de nouvelle ligne (n1256 §5.1.1.1)" -> Lors de la relecture d'un C11dr N1570 plus récent, n'a pas trouvé de support pour cela, sauf peut-être: "Un fichier source qui n'est pas vide doit se terminer par un caractère de nouvelle ligne, qui ne doit pas être immédiatement précédé d'un caractère de barre oblique inversée avant qu'un tel épissage ne se produise." §5.1.1.2 2, mais cela semble être limité aux spécifications d'épissure.
Chux
@chux Cette phrase est aussi présente dans n1256. La dernière ligne doit se terminer par un caractère de nouvelle ligne. Les lignes qui ne sont pas la dernière ligne doivent évidemment aussi se terminer par un caractère de nouvelle ligne pour indiquer que cette ligne se termine et que la ligne suivante commence. Ainsi, chaque ligne doit se terminer par un caractère de nouvelle ligne.
Gilles
Hmmm, pour moi, cette ligne "" Un fichier source ... une épissure a lieu. "Pourrait être limitée à la façon dont les considérations d'épissage et non aux fichiers en général. Pourtant, je vois comment on pourrait voir le contraire. Peut-être que je chercherai un post qui se concentre sur cela.
Chux
> "So diff utilise ce texte spécial \ Aucune nouvelle ligne à la fin du fichier pour différencier un fichier ne se terminant pas par une nouvelle ligne d'un fichier qui le fait." Git affiche ce texte non seulement lorsqu'il compare des fichiers. Mais même quand un nouveau fichier a été ajouté à git. Donc, cet argument n'est pas valide, je suppose.
Viktor Kruglikov
> "Les utilitaires supposés fonctionner sur des fichiers texte risquent de ne pas bien gérer les fichiers ne se terminant pas par un saut de ligne" exigences. Je pense que si git affiche ce message, la raison devrait être liée à des problèmes de contrôle de source .
Viktor Kruglikov
42

Pas nécessairement la raison, mais une conséquence pratique des fichiers ne se terminant pas par une nouvelle ligne:

Considérez ce qui arriverait si vous vouliez traiter plusieurs fichiers avec cat. Par exemple, si vous voulez trouver le mot fooau début de la ligne dans 3 fichiers:

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

Si la première ligne de fichier3 commence par foo, mais que fichier2 ne comporte pas de finale \naprès sa dernière ligne, cette occurrence ne sera pas trouvée par grep, car la dernière ligne de fichier2 et la première ligne de fichier3 seraient vues par grep comme une simple ligne.

Donc, par souci de cohérence et afin d’éviter les surprises, j’essaie de garder mes fichiers se terminant toujours par une nouvelle ligne.

Sergio Acosta
la source
Mais est-ce une affaire de git de se soucier de la concaténation de fichiers?
Viktor Kruglikov
N'est-il pas logique de penser que vous devriez vous contenter d' '\n'opérer un chat ...
Andrew
3
C'est comme si on disait: "Parfois, je joins des chaînes ensemble avec des \nespaces ou des espaces, ainsi, afin de maintenir la cohérence, je mets toujours \n _____aux deux extrémités de mes chaînes." Eh bien non, la bonne chose à faire est de couper vos chaînes et de les concaténer correctement.
Andrew
16

Il y a deux aspects:

  1. Certains compilateurs C ne peuvent pas analyser la dernière ligne si elle ne se termine pas par une nouvelle ligne. La norme C spécifie qu'un fichier C doit se terminer par une nouvelle ligne (C11, 5.1.1.2, 2.) et qu'une dernière ligne sans nouvelle ligne donne un comportement indéfini (C11, J.2, 2e élément). Peut-être pour des raisons historiques, car un fournisseur de ce type de compilateur faisait partie du comité lors de la rédaction de la première norme. Ainsi l'avertissement de GCC.

  2. diffles programmes (comme ceux utilisés par git diffgithub, etc.) affichent des différences ligne par ligne entre les fichiers. Ils impriment généralement un message lorsqu'un seul fichier se termine par une nouvelle ligne, sinon vous ne verrez pas cette différence. Par exemple, si la seule différence entre deux fichiers est la présence du dernier caractère de nouvelle ligne, sans l'indication, il semblerait que les deux fichiers sont identiques, diffet cmprenvoient un code de sortie inégal succès et les sommes de contrôle des fichiers (par exemple, via md5sum) ne correspondent pas.

maxschlepzig
la source
sens avec le programme diff
Thamaraiselvam
On dirait que les diffs devraient être plus intelligents.
Andrew
@ Andrew, non, ce n'est pas le cas. diffdevrait imprimer les différences s’il y en a. Et si un fichier a une nouvelle ligne comme dernier caractère tandis que l'autre n'en a pas, cette différence doit être perceptible dans le résultat.
maxschlepzig
Votre dernière déclaration est correcte. Cependant, le visualiseur de différences n'a pas besoin d'afficher les "nouvelles lignes" ( \n) pour commencer, il peut simplement afficher les "nouvelles lignes".
Andrew
10

Le message que\ No newline at end of file vous recevez de github apparaît à la fin du patch (au diffformat , voir la note à la fin de la section "Format unifié").

Les compilateurs ne se soucient pas de savoir s'il y a un saut de ligne ou non à la fin d'un fichier, mais git(et les utilitaires diff/ patch) doivent en tenir compte. Il y a plusieurs raisons à cela. Par exemple, oublier d'ajouter ou de supprimer une nouvelle ligne à la fin d'un fichier changerait son hashsum ( md5sum/ sha1sum). De plus, les fichiers ne sont pas toujours des programmes et une finale \npeut faire la différence.

Remarque : à propos de l’avertissement des compilateurs C, je suppose qu’ils insistent pour une nouvelle ligne finale à des fins de compatibilité ascendante. Les très vieux compilateurs risquent de ne pas accepter la dernière ligne si elle ne se termine pas \n(ou une autre séquence de caractères de fin de ligne dépendante du système).

Stéphane Gimenez
la source
7
"J'imagine qu'ils insistent pour une nouvelle ligne finale à des fins de compatibilité ascendante" - Non, ils insistent, car la norme C l' exige .
MestreLion
1
@MestreLion C nécessite une nouvelle ligne finale pour le code source C (C11 §5.1.1.2 2). Notez que, pour les E / S de fichier texte , C indique "Si la dernière ligne nécessite un caractère de nouvelle ligne de fin est définie par l'implémentation." §7.21.2 2
chux
Qui utilise de très vieux compilateurs? Arrête de les utiliser.
Andrew
1
@MestreLion: Et pourquoi pensez-vous que la norme C l'exige…
Stéphane Gimenez
@ StéphaneGimenez: cohérence, meilleure compatibilité et interopérabilité entre différents systèmes d'exploitation (POSIX définit également les lignes se terminant par '\ n')
MestreLion
4

Il y a aussi l'intérêt de garder l'histoire des diff. Si un fichier se termine sans un caractère de nouvelle ligne, l'ajout d'un élément à la fin du fichier sera considéré par les utilitaires diff comme modifiant cette dernière ligne (car elle y \nest ajoutée).

Cela pourrait entraîner des résultats indésirables avec des commandes telles que git blameet hg annotate.

Hosam Aly
la source
On dirait que les diffs doivent simplement être plus intelligents.
Andrew
1
Les outils de diffing sont intelligents. Ils remarquent le changement subtil apporté au fichier (ce qui est important car il modifiera inévitablement le hachage du fichier). Et GNU diff et git diff acceptent tous les deux une -woption permettant d’ignorer les changements d’espace lors de la sortie des données pour les humains.
joeytwiddle
4

POSIX, il s'agit d'un ensemble de normes spécifiées par IEEE pour maintenir la compatibilité entre les systèmes d'exploitation.

L'une d'elles est la définition d'une "ligne" qui est une séquence de zéro ou plusieurs non-caractères plus un caractère de fin de ligne.

Donc, pour que cette dernière ligne soit reconnue comme une "ligne" réelle, elle doit avoir un caractère de nouvelle ligne de fin.

Ceci est important si vous dépendez des outils du système d'exploitation pour indiquer le nombre de lignes ou le fractionnement / aide lors de l'analyse de votre fichier. Étant donné que PHP est un langage de script, il est tout à fait possible, surtout à ses débuts ou même maintenant (je n’ai aucune idée / postulation), il avait des dépendances de système d’exploitation de ce type.

En réalité, la plupart des systèmes d’exploitation ne sont pas entièrement compatibles avec POSIX et les utilisateurs n’ont pas cette machine à la recherche de nouvelles lignes. Donc, pour la plupart des choses, c'est un mélange de tout ce qui compte, que ce soit de mettre en garde ou de simplement lire que le dernier morceau de texte est vraiment une ligne, il suffit de l'inclure.

utilisateur3379747
la source