EOF ist kein Symbol

Kürzlich habe ich das Buch „Computersysteme: Architektur und Programmierung. Das Aussehen des Programmierers. " Dort erwähnten die Autoren im Kapitel über das Unix-E / A-System, dass am Ende der Datei kein Sonderzeichen steht EOF. Wenn Sie über das Unix / Linux-E / A-System gelesen oder damit experimentiert haben, wenn Sie C-Programme geschrieben haben, die Daten aus Dateien lesen, wird Ihnen diese Aussage wahrscheinlich völlig offensichtlich erscheinen. Aber schauen wir uns die folgenden zwei Aussagen genauer an, die sich auf das beziehen, was ich in dem Buch gefunden habe:





  1. EOF - Dies ist kein Symbol.
  2. Am Ende der Dateien befindet sich kein Sonderzeichen.

Was ist das EOF?

EOF ist kein Symbol


Warum sagt oder denkt jemand, dass EOFdies ein Symbol ist? Ich nehme an, dies kann so sein, weil Sie in einigen C-Programmen Code finden können, der explizite Überprüfungen für die EOFVerwendung von Funktionen getchar()und verwendet getc().

Es könnte so aussehen:

    #include <stdio.h>
    ...
    while ((c = getchar()) != EOF)
      putchar(c);

Oder so:

    FILE *fp;
    int c;
    ...
    while ((c = getc(fp)) != EOF)
      putc(c, stdout);

Wenn Sie sich die Hilfe für getchar()oder ansehen getc(), können Sie feststellen, dass beide Funktionen das nächste Zeichen aus dem Eingabestream lesen. Wahrscheinlich - genau das verursacht das Missverständnis über die Natur EOF. Dies sind jedoch nur meine Annahmen. Kehren wir zu der Idee zurück, dass EOFdies kein Symbol ist.

Und was ist ein Symbol im Allgemeinen? Ein Symbol ist die kleinste Textkomponente. "A", "a", "B", "b" - all dies sind verschiedene Symbole. Ein Zeichen hat einen numerischen Code, der im Unicode-Standard als Codepunkt bezeichnet wird . Der lateinische Buchstabe „A“ hat beispielsweise einen Dezimalcode von 65. Dies kann schnell über die Befehlszeile des Python-Interpreters überprüft werden:

$python
>>> ord('A')
65
>>> chr(65)
'A'

Oder schauen Sie sich die ASCII-Tabelle unter Unix / Linux an:

$ man ascii


Wir werden herausfinden, welcher Code entspricht, EOFindem wir ein kleines Programm in C schreiben. In ANSI C ist eine Konstante EOFdefiniert stdio.h, die Teil der Standardbibliothek ist. Normalerweise auf diese Konstante geschrieben -1. Sie können den folgenden Code in einer Datei speichern printeof.c, kompilieren und ausführen:

#include <stdio.h>

int main(int argc, char *argv[])
{
  printf("EOF value on my system: %d\n", EOF);
  return 0;
}

Kompilieren Sie das Programm und führen Sie es aus:

$ gcc -o printeof printeof.c

$ ./printeof
EOF value on my system: -1

Ich habe dieses Programm, getestet unter Mac OS und unter Ubuntu, berichtet, dass EOFgleich -1. Gibt es ein Zeichen mit diesem Code? Auch hier können Sie die Zeichencodes in der ASCII-Tabelle überprüfen, in der Unicode-Tabelle nachsehen, in welchem ​​Bereich die Zeichencodes liegen können. Wir werden anders handeln: Wir werden den Python-Interpreter starten und die Standardfunktion verwenden chr(), um uns das Symbol zu geben, das dem Code entspricht -1:

$ python
>>> chr(-1)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: chr() arg not in range(0x110000)

Wie erwartet existiert das Zeichen mit dem Code -1nicht. Also am Ende EOFund die Wahrheit ist kein Symbol. Wir wenden uns nun der zweiten betrachteten Erklärung zu.

Am Ende der Dateien befindet sich kein Sonderzeichen.


Vielleicht EOF- das ist ein Sonderzeichen, das am Ende der Datei zu finden ist? Ich nehme an, Sie kennen die Antwort bereits. Aber lassen Sie uns unsere Annahme sorgfältig prüfen.

Nehmen Sie eine einfache Textdatei, helloworld.txt , und zeigen Sie ihren Inhalt in hexadezimaler Darstellung an. Dazu können Sie den folgenden Befehl verwenden xxd:

$ cat helloworld.txt
Hello world!

$ xxd helloworld.txt
00000000: 4865 6c6c 6f20 776f 726c 6421 0a         Hello world!.

Wie Sie sehen können, hat das letzte Zeichen der Datei einen Code 0a. In der ASCII-Tabelle können Sie feststellen, dass dieser Code einem Zeichen entspricht nl, dh einem Zeilenumbruchzeichen. Sie können dies mit Python herausfinden:

$ python
>>> chr(0x0a)
'\n'

Damit. EOF- Dies ist kein Symbol, und am Ende der Dateien befindet sich kein spezielles Symbol. Was ist das EOF?

Was ist ein EOF?


EOF(Dateiende) ist ein Status, der von der Anwendung in einer Situation erkannt werden kann, in der der Dateilesevorgang sein Ende erreicht.

Lassen Sie uns einen Blick darauf werfen, wie es möglich ist, den Status EOFin verschiedenen Programmiersprachen beim Lesen einer Textdatei mithilfe der von diesen Sprachen bereitgestellten übergeordneten Eingabe- / Ausgabe-Tools zu erkennen. Dazu schreiben wir eine sehr einfache Version cat, die aufgerufen wird mcat. Es liest ASCII-Textbyte (Zeichen) und sucht explizit nach EOF. Wir werden das Programm in den folgenden Sprachen schreiben:

  • ANSI C.
  • Python 3
  • Gehen
  • JavaScript (Node.js)

Hier ist ein Repository mit Beispielcode. Wir fahren mit ihrer Analyse fort.

ANSI C.


Beginnen wir mit dem ehrwürdigen C. Das hier vorgestellte Programm ist eine modifizierte Version catdes Buches "C Programming Language".

/* mcat.c */
#include <stdio.h>

int main(int argc, char *argv[])
{
  FILE *fp;
  int c;

  if ((fp = fopen(*++argv, "r")) == NULL) {
    printf("mcat: can't open %s\n", *argv);
    return 1;
  }

  while ((c = getc(fp)) != EOF)
    putc(c, stdout);

  fclose(fp);

  return 0;
}

Zusammenstellung:

$ gcc -o mcat mcat.c

Starten:

$ ./mcat helloworld.txt
Hello world!

Hier einige Erklärungen zum obigen Code:

  • Das Programm öffnet die übergebene Datei als Befehlszeilenargument.
  • Die Schleife whilekopiert Daten aus der Datei in den Standardausgabestream. Die Daten werden byteweise kopiert, dies geschieht bis das Ende der Datei erreicht ist.
  • Wenn das Programm erreicht EOF, schließt es die Datei und wird beendet.

Python 3


In Python gibt es keinen Mechanismus zum expliziten Suchen EOF, ähnlich dem in ANSI C. Wenn Sie die Datei jedoch zeichenweise lesen, können Sie den Status EOFanzeigen, wenn die Variable, in der das nächste gelesene Zeichen gespeichert ist, leer ist:

# mcat.py
import sys

with open(sys.argv[1]) as fin:
    while True:
        c = fin.read(1) #   1 
        if c == '':     # EOF
            break
        print(c, end='')

Führen Sie das Programm aus und sehen Sie sich die zurückgegebenen Ergebnisse an:

$ python mcat.py helloworld.txt
Hello world!

Hier ist eine kürzere Version des gleichen Beispiels, das in Python 3.8+ geschrieben wurde. Hier wird der Operator verwendet : = (er wird als "Walross-Operator" oder "Walross-Operator" bezeichnet):

# mcat38.py
import sys

with open(sys.argv[1]) as fin:
    while (c := fin.read(1)) != '':  #   1    EOF
        print(c, end='')

Führen Sie diesen Code aus:

$ python3.8 mcat38.py helloworld.txt
Hello world!

Gehen


In Go können Sie den von Read () zurückgegebenen Fehler explizit überprüfen, um festzustellen , ob das Ende der Datei erreicht ist:

// mcat.go
package main

import (
    "fmt"
    "os"
    "io"
)

func main() {
    file, err := os.Open(os.Args[1])
    if err != nil {
        fmt.Fprintf(os.Stderr, "mcat: %v\n", err)
        os.Exit(1)
    }

    buffer := make([]byte, 1// 1-byte buffer
    for {
        bytesread, err := file.Read(buffer)
        if err == io.EOF {
            break
        }
        fmt.Print(string(buffer[:bytesread]))
    }
    file.Close()
}

Führen Sie das Programm aus:

$ go run mcat.go helloworld.txt
Hello world!

JavaScript (Node.js)


Node.js hat keinen Mechanismus zum expliziten Suchen nach EOF. Wenn jedoch am Ende der Datei versucht wird, etwas anderes zu lesen, wird das End- Stream-Ereignis ausgelöst .

/* mcat.js */
const fs = require('fs');
const process = require('process');

const fileName = process.argv[2];

var readable = fs.createReadStream(fileName, {
  encoding: 'utf8',
  fd: null,
});

readable.on('readable', function() {
  var chunk;
  while ((chunk = readable.read(1)) !== null) {
    process.stdout.write(chunk); /* chunk is one byte */
  }
});

readable.on('end', () => {
  console.log('\nEOF: There will be no more data.');
});

Führen Sie das Programm aus:

$ node mcat.js helloworld.txt
Hello world!

EOF: There will be no more data.

Low-Level-Systemmechanismen


Wie bestimmen die in den obigen Beispielen verwendeten übergeordneten E / A-Mechanismen das Ende der Datei? Unter Linux verwenden diese Mechanismen direkt oder indirekt den vom Kernel bereitgestellten Systemaufruf read () . Eine Funktion (oder ein Makro) getc()aus C verwendet beispielsweise einen Systemaufruf read()und gibt zurück, EOFwenn read()der Status angezeigt wird, in dem das Ende der Datei erreicht ist. In diesem Fall wird read()zurückgegeben 0. Wenn Sie dies alles in Form eines Diagramms darstellen, erhalten Sie Folgendes:


Es stellt sich heraus, dass die Funktion auf getc()basiert read().

Wir werden eine Version mit dem catNamen schreiben, syscatdie nur Unix-Systemaufrufe verwendet. Wir werden dies nicht nur aus Interesse tun, sondern auch, weil es uns durchaus Vorteile bringen kann.

Hier ist dieses Programm in C geschrieben:

/* syscat.c */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(int argc, char *argv[])
{
  int fd;
  char c;

  fd = open(argv[1], O_RDONLY, 0);

  while (read(fd, &c, 1) != 0)
    write(STDOUT_FILENO, &c, 1);

  return 0;
}

Starte es:

$ gcc -o syscat syscat.c

$ ./syscat helloworld.txt
Hello world!

Dieser Code verwendet die Tatsache, dass die Funktion read(), die angibt, dass das Ende der Datei erreicht ist, zurückgegeben wird 0.

Hier ist das gleiche Programm, das in Python 3 geschrieben wurde:

# syscat.py
import sys
import os

fd = os.open(sys.argv[1], os.O_RDONLY)

while True:
    c = os.read(fd, 1)
    if not c:  # EOF
        break
    os.write(sys.stdout.fileno(), c)

Starte es:

$ python syscat.py helloworld.txt
Hello world!

Hier ist dasselbe, was in Python 3.8+ geschrieben wurde:

# syscat38.py
import sys
import os

fd = os.open(sys.argv[1], os.O_RDONLY)

while c := os.read(fd, 1):
    os.write(sys.stdout.fileno(), c)

Führen Sie auch diesen Code aus:

$ python3.8 syscat38.py helloworld.txt
Hello world!

Zusammenfassung


  • EOF - Dies ist kein Symbol.
  • Am Ende der Dateien befindet sich kein Sonderzeichen.
  • EOF - Dies ist der vom Kernel gemeldete Status, der von der Anwendung erkannt werden kann, wenn der Datenlesevorgang das Ende der Datei erreicht.
  • In ANSI C EOFist dies wiederum kein Zeichen. Dies ist die definierte Konstante, stdio.hin die normalerweise der Wert -1 geschrieben wird.
  • Ein "Zeichen" EOFkann nicht in einer ASCII-Tabelle oder in Unicode gefunden werden.

Liebe Leser! Kennen Sie mehr oder weniger weit verbreitete Missverständnisse aus der Computerwelt?


All Articles