Frage Warum sollten Textdateien mit einem Zeilenumbruch enden?


Ich nehme an, dass jeder hier mit dem Sprichwort vertraut ist, dass alle Textdateien mit einem Zeilenumbruch enden sollten. Ich kenne diese "Regel" seit Jahren, aber ich habe mich immer gewundert - warum?


1090
2018-04-08 12:16


Ursprung


Antworten:


Weil das ist Wie definiert der POSIX-Standard a Linie:

3.206 Linie
Eine Folge von null oder mehr nicht <newline> -Zeichen plus einem abschließenden <newline> -Zeichen.

Daher werden Zeilen, die nicht mit einem Zeilenumbruchzeichen enden, nicht als tatsächliche Zeilen betrachtet. Aus diesem Grund haben einige Programme Probleme, die letzte Zeile einer Datei zu verarbeiten, wenn sie nicht mit Zeilenumbrüchen beendet wurde.

Diese Richtlinie hat bei der Arbeit an einem Terminalemulator mindestens einen großen Vorteil: Alle Unix-Tools erwarten diese Konvention und arbeiten damit. Zum Beispiel beim Verketten von Dateien mit cat, eine Datei, die mit Newline beendet wird, hat eine andere Wirkung als eine ohne:

$ more a.txt
foo$ more b.txt
bar
$ more c.txt
baz
$ cat *.txt
foobar
baz

Und wie das vorherige Beispiel auch zeigt, wenn die Datei in der Befehlszeile angezeigt wird (z. B. über more), führt eine Newline-terminierte Datei zu einer korrekten Anzeige. Eine nicht ordnungsgemäß abgeschlossene Datei kann verstümmelt sein (zweite Zeile).

Aus Gründen der Konsistenz ist es sehr hilfreich, diese Regel zu befolgen. Wenn Sie etwas anderes tun, müssen Sie mit den Standard-Unix-Tools arbeiten.

Nun, weiter nicht POSIX-konform Systeme (heutzutage ist das meist Windows), der Punkt ist strittig: Dateien enden in der Regel nicht mit einer Zeilenumbruch, und die (informelle) Definition einer Zeile könnte zum Beispiel "Text, der ist getrennt durch Zeilenumbrüche "(beachten Sie den Schwerpunkt). Dies ist absolut gültig. Für strukturierte Daten (z. B. Programmiercode) macht es jedoch das Parsen minimal komplizierter: es bedeutet im Allgemeinen, dass Parser neu geschrieben werden müssen. Wenn ein Parser ursprünglich mit der POSIX-Definition geschrieben wurde, ist es möglicherweise einfacher, den Token-Stream als den Parser zu ändern - mit anderen Worten, fügen Sie ein "künstliches Newline" -Token am Ende der Eingabe hinzu.


1021
2018-04-08 12:46



Jede Zeile sollte in einem Newline-Zeichen enden, einschließlich des letzten. Einige Programme haben Probleme, die letzte Zeile einer Datei zu verarbeiten, wenn sie nicht neu terminiert ist.

GCC warnt davor nicht deswegen kippen verarbeiten Sie die Datei, aber weil es muss als Teil des Standards.

Der C-Standard sagt   Eine Quelldatei, die nicht leer ist, endet in einem Zeichen für eine neue Zeile, dem kein umgekehrter Schrägstrich vorangestellt werden darf.

Da dies eine "Soll" -Klausel ist, müssen wir eine Diagnosemeldung für einen Verstoß gegen diese Regel ausgeben.

Dies ist in Abschnitt 2.1.1.2 des Standards ANSI C 1989. Abschnitt 5.1.1.2 der Norm ISO C 1999 (und wahrscheinlich auch die Norm ISO C 1990).

Referenz: Das GCC / GNU-Mail-Archiv.


245
2018-04-08 12:26



Diese Antwort ist ein Versuch einer technischen Antwort statt einer Meinung.

Wenn wir POSIX-Puristen sein wollen, definieren wir eine Linie als:

Eine Folge von null oder mehr nicht <newline> -Zeichen plus einem abschließenden <newline> -Zeichen.

Quelle: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_206

Eine unvollständige Zeile als:

Eine Folge von einem oder mehreren nicht <newline> Zeichen am Ende der Datei.

Quelle: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_195

Eine Textdatei als:

Eine Datei, die Zeichen enthält, die in null oder mehr Zeilen organisiert sind. Die Zeilen enthalten keine NUL-Zeichen, und keines darf mehr als {LINE_MAX} Byte lang sein, einschließlich des <newline> -Zeichens. Obwohl POSIX.1-2008 nicht zwischen Textdateien und Binärdateien unterscheidet (siehe ISO C-Standard), produzieren viele Dienstprogramme nur vorhersagbare oder sinnvolle Ausgaben, wenn sie mit Textdateien arbeiten. Die Standarddienstprogramme, die solche Einschränkungen aufweisen, geben in ihren Abschnitten STDIN oder INPUT FILES immer "Textdateien" an.

Quelle: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_397

Eine Zeichenfolge als:

Eine zusammenhängende Folge von Bytes, die durch das erste Nullbyte abgeschlossen werden und dieses enthalten.

Quelle: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_396

Von diesem können wir dann das einzige Mal ableiten, dass wir werden möglicherweise Irgendwelche Arten von Problemen sind, wenn wir uns mit dem Konzept von a befassen Linie einer Datei oder einer Datei als Textdatei (dass das a Textdatei ist eine Organisation von null oder mehr Zeilen, und eine Zeile, die wir kennen, muss mit einem <newline> enden.

Fallbeispiel: wc -l filename.

Von dem wcHandbuch lesen wir:

Eine Zeile ist definiert als eine Zeichenfolge, die durch ein <newline> -Zeichen begrenzt wird.

Was bedeutet das für JavaScript-, HTML- und CSS-Dateien? Text  Dateien?

In Browsern, modernen IDEs und anderen Front-End-Anwendungen gibt es keine Probleme beim Überspringen von EOL bei EOF. Die Anwendungen werden die Dateien ordnungsgemäß analysieren. Da nicht alle Betriebssysteme dem POSIX-Standard entsprechen, ist es für Nicht-OS-Tools (z. B. Browser) nicht praktikabel, Dateien gemäß dem POSIX-Standard (oder irgendeinem OS-Standard) zu behandeln.

Daher können wir relativ sicher sein, dass EOL bei EOF praktisch keine negativen Auswirkungen auf die Anwendungsebene hat - unabhängig davon, ob es unter einem UNIX-Betriebssystem ausgeführt wird.

An dieser Stelle können wir getrost sagen, dass das Überspringen von EOL bei EOF beim Umgang mit JS, HTML, CSS auf der Clientseite sicher ist. Tatsächlich können wir feststellen, dass das Minimieren einer dieser Dateien, die kein <newline> enthalten, sicher ist.

Wir können noch einen Schritt weiter gehen und sagen, dass es für NodeJS auch nicht möglich ist, den POSIX-Standard einzuhalten, da es in nicht POSIX-konformen Umgebungen laufen kann.

Was haben wir dann? Werkzeuge auf Systemebene.

Dies bedeutet, dass die einzigen Probleme auftreten können, die mit Tools auftreten, die sich bemühen, ihre Funktionalität an die Semantik von POSIX anzuhängen (z. B. die Definition einer Zeile wie in wc).

Trotzdem werden nicht alle Shells automatisch an POSIX angepasst. Bash ist beispielsweise nicht standardmäßig POSIX-Verhalten. Es gibt einen Schalter, um es zu aktivieren: POSIXLY_CORRECT.

Denkanstoß zum Wert von EOL <newline>: http://www.rfc-editor.org/EOLstory.txt

Für alle praktischen Absichten bleiben wir auf dem Tooling-Track:

Lassen Sie uns mit einer Datei arbeiten, die kein EOL hat. Zum jetzigen Zeitpunkt ist die Datei in diesem Beispiel ein minimiertes JavaScript ohne EOL.

curl http://cdnjs.cloudflare.com/ajax/libs/AniJS/0.5.0/anijs-min.js -o x.js
curl http://cdnjs.cloudflare.com/ajax/libs/AniJS/0.5.0/anijs-min.js -o y.js

$ cat x.js y.js > z.js

-rw-r--r--  1 milanadamovsky   7905 Aug 14 23:17 x.js
-rw-r--r--  1 milanadamovsky   7905 Aug 14 23:17 y.js
-rw-r--r--  1 milanadamovsky  15810 Aug 14 23:18 z.js

Beachten Sie die cat Dateigröße ist genau die Summe ihrer einzelnen Teile. Wenn die Verkettung von JavaScript-Dateien ein Problem für JS-Dateien darstellt, wäre es sinnvoller, jede JavaScript-Datei mit einem Semikolon zu starten.

Wie jemand anderes in diesem Thread erwähnt: Was, wenn Sie wollen cat zwei Dateien, deren Ausgabe nur eine Zeile statt zwei wird? Mit anderen Worten, cat tut was es tun soll.

Das man von cat erwähnt nur Leseeingabe bis EOF, nicht <newline>. Notiere dass der -n Schalter von cat wird auch eine nicht <newline> terminierte Zeile ausgeben (oder unvollständige Linie) Als ein Linie - dass die Zählung beginnt bei 1 (entsprechend der man.)

-n Nummeriert die Ausgabezeilen, beginnend bei 1.

Jetzt verstehen wir, wie POSIX a definiert Linie Dieses Verhalten wird mehrdeutig oder gar nicht konform.

Wenn Sie den Zweck und die Kompatibilität eines bestimmten Tools verstehen, können Sie feststellen, wie wichtig es ist, Dateien mit einem EOL zu beenden. In C, C ++, Java (JARs), etc .. einige Standards diktieren eine Newline für die Gültigkeit - kein solcher Standard existiert für JS, HTML, CSS.

Zum Beispiel, anstatt zu verwenden wc -l filename man könnte es tun awk '{x++}END{ print x}' filename , und seien Sie versichert, dass der Erfolg der Aufgabe nicht durch eine Datei gefährdet wird, die wir möglicherweise bearbeiten möchten, die wir nicht geschrieben haben (z. B. eine Bibliothek von Dritten, wie zum Beispiel die verkleinerte JS-Datei) curld) - es sei denn, unsere Absicht war wirklich zu zählen Linien im POSIX-konformen Sinne.

Fazit

Es wird nur sehr wenige Anwendungsfälle geben, in denen das Überspringen von EOL bei EOF für bestimmte Textdateien wie JS, HTML und CSS negative Auswirkungen hat - wenn überhaupt. Wenn wir uns darauf verlassen, dass <newline> vorhanden ist, beschränken wir die Zuverlässigkeit unserer Werkzeuge nur auf die von uns erstellten Dateien und öffnen uns selbst für mögliche Fehler, die durch Dateien Dritter verursacht werden.

Moral der Geschichte: Ingenieurwerkzeuge, die nicht die Schwäche haben, bei EOF auf EOL zu setzen.

Fühlen Sie sich frei, Anwendungsfälle zu veröffentlichen, wie sie für JS, HTML und CSS gelten, wo wir untersuchen können, wie sich das Überspringen von EOL nachteilig auswirkt.


87
2017-08-15 06:31



Es kann mit dem verwandt sein Unterschied zwischen:

  • Textdatei (jede Zeile soll am Ende der Zeile enden)
  • Binärdatei (es gibt keine echten "Zeilen", von denen gesprochen werden muss, und die Länge der Datei muss erhalten bleiben)

Wenn jede Zeile in einem Zeilenende endet, vermeidet dies beispielsweise, dass das Verketten von zwei Textdateien die letzte Zeile des ersten Laufs in die erste Zeile des zweiten Laufs bringen würde.

Außerdem kann ein Editor beim Laden prüfen, ob die Datei in einem End-of-Line endet, speichert es in seiner lokalen Option 'eol' und verwendet diese beim Schreiben der Datei.

Vor ein paar Jahren (2005) haben viele Redakteure (ZDE, Eclipse, Scite, ...) dieses letzte EOL "vergessen", was nicht sehr geschätzt wurde.
Nicht nur das, aber sie interpretierten diesen endgültigen EOL fälschlicherweise als "starte eine neue Zeile" und beginnen tatsächlich, eine andere Zeile anzuzeigen, als ob sie bereits existierte.
Dies war sehr gut sichtbar mit einer 'richtigen' Textdatei mit einem gut benützten Texteditor wie vim, verglichen mit dem Öffnen in einem der obigen Editoren. Es zeigte eine zusätzliche Zeile unterhalb der tatsächlichen letzten Zeile der Datei an. Du siehst so etwas:

1 first line
2 middle line
3 last line
4

59
2018-04-08 12:29



Einige Tools erwarten dies. Beispielsweise, wc erwartet dies:

$ echo -n "Line not ending in a new line" | wc -l
0
$ echo "Line ending with a new line" | wc -l
1

37
2017-10-12 14:16



Grundsätzlich gibt es viele Programme, die Dateien nicht korrekt verarbeiten, wenn sie nicht den endgültigen EOL EOF erhalten.

GCC warnt Sie davor, weil es als Teil des C-Standards erwartet wird. (Abschnitt 5.1.1.2 anscheinend)

Compilerwarnung "Keine Zeilenschaltung am Ende der Datei"


18
2018-04-08 12:21



Dies stammt aus den frühen Tagen, als einfache Terminals verwendet wurden. Das Newline-Zeichen wurde verwendet, um ein "Flush" der übertragenen Daten auszulösen.

Heute wird der Newline-Char nicht mehr benötigt. Sicher, viele Apps haben immer noch Probleme, wenn der Newline nicht da ist, aber ich würde das als einen Fehler in diesen Apps betrachten.

Wenn Sie jedoch ein Textdateiformat haben, in dem Sie benötigen Die neue Zeile, Sie erhalten einfache Datenverifizierung sehr billig: Wenn die Datei mit einer Zeile endet, die am Ende keinen Zeilenumbruch hat, wissen Sie, dass die Datei defekt ist. Mit nur einem zusätzlichen Byte für jede Zeile können Sie defekte Dateien mit hoher Genauigkeit und nahezu ohne CPU-Zeit erkennen.


12
2018-04-08 12:41



Es gibt auch ein praktisches Programmierproblem mit Dateien ohne Zeilenumbrüche am Ende: Die read Bash eingebaut (ich weiß nicht über andere read Implementierungen) funktioniert nicht wie erwartet:

printf $'foo\nbar' | while read line
do
    echo $line
done

Dies druckt nur foo! Der Grund ist, dass wenn read trifft auf die letzte Zeile, schreibt den Inhalt nach $line gibt jedoch den Beendigungscode 1 zurück, weil EOF erreicht wurde. Dies bricht die while Schleife, so erreichen wir nie die echo $line Teil. Wenn Sie mit dieser Situation umgehen möchten, müssen Sie Folgendes tun:

while read line || [ -n "${line-}" ]
do
    echo $line
done < <(printf $'foo\nbar')

Das heißt, mach das echo wenn die read fehlgeschlagen wegen einer nicht leeren Zeile am Ende der Datei. Natürlich wird es in diesem Fall eine zusätzliche neue Zeile in der Ausgabe geben, die nicht in der Eingabe war.


10
2017-11-04 10:12



Ein separater Anwendungsfall: wenn Ihre Textdatei versionsgesteuert ist (in diesem Fall speziell unter Git, obwohl sie auch für andere gilt). Wenn am Ende der Datei Inhalt hinzugefügt wird, wurde die Zeile, die zuvor die letzte Zeile war, so bearbeitet, dass sie ein Zeilenvorschubzeichen enthält. Das bedeutet, dass blameWenn Sie die Datei suchen, um herauszufinden, wann diese Zeile zuletzt bearbeitet wurde, wird die Texteingabe angezeigt, nicht das Commit, das Sie eigentlich sehen wollten.


10
2017-09-05 13:17



Vermutlich einfach, dass ein Parsing-Code es erwartet hatte.

Ich bin mir nicht sicher, ob ich es als "Regel" betrachten würde, und es ist sicherlich nicht etwas, woran ich mich religiös halte. Der meiste vernünftige Code weiß, wie man Text (einschließlich Kodierungen) Zeile für Zeile (jede Wahl von Zeilenendungen) parst, mit oder ohne Zeilenumbruch in der letzten Zeile.

In der Tat - wenn Sie mit einer neuen Zeile enden: Gibt es (theoretisch) eine leere letzte Zeile zwischen EOL und EOF? Eine zum überlegen ...


9
2018-04-08 12:19