Was ist EOF und wie wird es ausgelöst? [geschlossen]

12

Dies ist mein C-Quellcode.

Wenn ich es in Ubuntu baue, werden Zeichen angezeigt, aber ich weiß nicht, wie ich das Programm beenden soll, da es nicht durch Eingabe ENTERoder einen Wagenrücklauf endet .

Was bedeutet EOF? Wie kann ich es auslösen?

Diese Quelle ist auch auf einem Buch von Dennis Ritchie:

#include <stdio.h>
    /* count digits, white space, others */
main ()
{
  int c, i, nwhite, nother;
  int ndigit[10];
  nwhite = nother = 0;
  for (i = 0; i < 10; ++i)
    ndigit[i] = 0;
  while ((c = getchar ()) != EOF)
    if (c >= '0' && c <= '9')
      ++ndigit[c - '0'];
    else if (c == ' ' || c == '\n' || c == '\t')
      ++nwhite;
    else
      ++nother;
  printf ("digits =");
  for (i = 0; i < 10; ++i)
    printf (" %d", ndigit[i]);
  printf (", white space = %d, other = %d\n", nwhite, nother);
}
Stapelprogrammierer
quelle
4
in C-Sprache -1entspricht EOF. Es ist /usr/include/stdio.hals Makrokonstante definiert
Edward Torvalds
1
Relevante Lektüre: stackoverflow.com/q/12389518/3701431
Sergiy Kolodyazhnyy
@edwardtorvalds Eingabe -1als Eingabe funktioniert nicht :)
Sergiy Kolodyazhnyy
Ich denke, dasselbe Dennis Ritchie-Buch erklärt dies.
andy256
Ebenfalls relevant: unix.stackexchange.com/questions/110240/… (Keine der Antworten auf diese Frage ist völlig korrekt.)
fkraiem

Antworten:

22

Tl; dr

Sie können EOF im Allgemeinen in einem Programm "auslösen", das in einem Terminal mit einem CTRL+ DTastendruck direkt nach der letzten Eingabespülung ausgeführt wird.


Was bedeutet EOF? Wie kann ich es auslösen?

EOF bedeutet Dateiende.

"EOF auslösen" bedeutet in diesem Fall ungefähr "das Programm darauf aufmerksam machen, dass keine Eingabe mehr gesendet wird".

In diesem Fall getchar()wird die Ausführung beendet , da eine negative Zahl zurückgegeben wird, wenn kein Zeichen gelesen wird.

Dies gilt jedoch nicht nur für Ihr spezifisches Programm, sondern auch für viele verschiedene Tools.

Im Allgemeinen kann das "Auslösen von EOF" mit einem CTRL+ DTastendruck direkt nach dem letzten Eingangs-Flush erfolgen (dh durch Senden eines leeren Eingangs).

Zum Beispiel mit cat:

% cat >file # Hit ENTER
foo # Hit ENTER and CTRL+D
% 

Was unter der Haube passiert, wenn CTRL+ gedrückt Dwird, ist, dass die Eingabe, die seit dem letzten Eingabe-Flush eingegeben wurde, geleert wird. wenn dies geschieht , die eine leere eingegeben werden read()genannt Syscall auf der STDIN kehrt das Programm 0, getchar()gibt eine negative Zahl ( -1in dem GNU C - Bibliothek) , und dies ist wiederum als EOF interpretiert 1 .


1 - /programming//a/1516177/4316166

kos
quelle
2
Die Kompilierung funktioniert, da die Kommagrenze nicht an dieselbe Zeile gebunden ist.
Davon abgesehen eine
@ PauliusŠukys Huh, du hast recht. Mein C ist etwas rostig. :)
Kos
1
iirc EOF ist nicht als -1 gemäß Standard definiert. Es ist genau das, was es zum Beispiel in glibc ist.
Larkey
1
EOF besteht nicht darin, eine "leere Eingabe" zu senden, und die von Ihnen zitierte SO-Antwort sagt nichts anderes aus. Es ist ein Außerbandsignal. Im Falle eines Terminals wird es durch Eingabe von Strg / d gesendet.
user207421
4

TL; DR : EOF ist kein Zeichen, sondern ein Makro zur Bewertung der negativen Rückgabe einer Eingabe-Lesefunktion. Man kann Ctrl+ verwenden D, um EOTZeichen zu senden , die die Funktionsrückgabe erzwingen-1

Jeder Programmierer muss RTFM

Wir verweisen auf "CA Reference Manual" von Harbison und Steele, 4. Aufl. ab 1995, Seite 317:

Die negative Ganzzahl EOF ist ein Wert, der keine Codierung eines "echten Zeichens" ist. . . Zum Beispiel gibt fget (Abschnitt 15.6) am Ende der Datei EOF zurück , da kein "echtes Zeichen" zu lesen ist.

Im Wesentlichen EOFhandelt es sich nicht um ein Zeichen, sondern um einen ganzzahligen Wert , der stdio.hzur Darstellung implementiert ist -1. Somit ist die Antwort von kos soweit richtig, aber es geht nicht darum, "leere" Eingaben zu erhalten. Wichtiger Hinweis ist, dass hier EOF als Rückgabewert (des getchar()) Vergleichs dient, nicht um ein tatsächliches Zeichen zu kennzeichnen. Das man getcharunterstützt das:

RÜCKGABEWERT

fgetc (), getc () und getchar () geben das als vorzeichenloses Zeichen gelesene Zeichen am Ende der Datei oder des Fehlers an einen int oder EOF zurück.

get () und fgets () geben s bei Erfolg zurück und NULL bei Fehler oder wenn das Dateiende eintritt, während keine Zeichen gelesen wurden.

ungetc () gibt bei Erfolg c oder bei Fehler EOF zurück.

Betrachten Sie die whileSchleife - ihr Hauptzweck besteht darin, die Aktion zu wiederholen, wenn die Bedingung in den Klammern erfüllt ist . Schau nochmal:

while ((c = getchar ()) != EOF)

Grundsätzlich heißt es, mach weiter, wenn c = getchar()erfolgreicher Code zurückgegeben wird ( 0oder höher; es ist übrigens üblich, einen erfolgreichen Befehl auszuführen , dann echo $?und dann fehlgeschlagen echo $?und die zurückgegebenen Zahlen zu sehen). Wenn wir also erfolgreich Zeichen erhalten und C zuordnen, ist der zurückgegebene Statuscode 0, fehlgeschlagen -1. EOFist definiert als -1. Wenn ein Zustand -1 == -1eintritt, werden die Schleifen daher gestoppt. Und wann wird das passieren? Wenn kein Charakter mehr zu bekommen ist, wenn er c = getchar()fehlschlägt. Sie könnten schreiben while ((c = getchar ()) != -1)und es würde immer noch funktionieren

Kehren wir auch zum eigentlichen Code zurück. Hier ist ein Auszug aus stdio.h

/* End of file character.
   Some things throughout the library rely on this being -1.  */
#ifndef EOF
# define EOF (-1)
#endif

ASCII-Codes und EOT

Obwohl das EOF-Zeichen kein tatsächliches Zeichen ist, gibt es ein EOT(Ende der Übertragung) Zeichen mit dem ASCII-Dezimalwert 04; Es ist mit der Verknüpfung Ctrl+ verknüpft D(auch als Metazeichen dargestellt ^D). Das Ende der Übertragung wird verwendet, um das Schließen eines Datenstroms vor langer Zeit anzuzeigen, als Computer zur Steuerung von Telefonverbindungen verwendet wurden, daher die Benennung "Ende der Übertragung".

Es ist also möglich, diesen ASCII-Wert wie $'\04'folgt an das Programm zu senden. Beachten Sie Folgendes: EOT:

skolodya@ubuntu:$ ./a.out  <<< "a,b,c $'\04'"                                  
digits = 1 0 0 0 1 0 0 0 0 0, white space = 2, other = 9

Wir können also sagen, dass es existiert, aber es ist nicht druckbar

Randnotiz

Wir vergessen oft, dass Computer in der Vergangenheit nicht so vielseitig waren - Designer müssen jede verfügbare Tastaturtaste verwenden. Das Senden eines EOTZeichens mit StrgD bedeutet also immer noch "Senden eines Zeichens", ähnlich wie bei der Eingabe von Großbuchstaben A und ShiftA. Sie geben dem Computer dennoch eine Eingabe mit verfügbaren Schlüsseln. Somit ist EOT ein realer Charakter in dem Sinne, dass es vom Benutzer stammt, vom Computer lesbar ist (obwohl es nicht druckbar ist, für Menschen nicht sichtbar), es existiert im Computerspeicher

Kommentar von Byte Commander

Wenn Sie versuchen, aus / dev / null zu lesen, sollte dies auch einen EOF zurückgeben, oder? Oder was bekomme ich dort?

Ja, genau richtig, da /dev/nulldarin kein tatsächlich zu lesendes Zeichen vorhanden ist. Daher c = getchar()wird -1Code zurückgegeben und das Programm wird sofort beendet. Wieder gibt der Befehl EOF nicht zurück. EOF ist nur eine konstante Variable gleich -1, mit der wir den Rückkehrcode der Funktion getchar vergleichen . EOFexistiert nicht als Zeichen, es ist nur ein statischer Wert im Inneren stdio.h.

Demo:

# cat /dev/null shows there's no readable chars
DIR:/xieerqi
skolodya@ubuntu:$ cat /dev/null | cat -A        

# Bellow is simple program that will open /dev/null for reading. Note the use of literal -1                                   
   DIR:/xieerqi
skolodya@ubuntu:$ cat readNull.c                                               
#include<stdio.h>

void main()
{
   char c;
    FILE *file;
    file = fopen("/dev/null", "r");

    if (file) 
    {
    printf ("Before while loop\n");
        while ((c = getc(file)) != -1)
            putchar(c);
    printf("After while loop\n"); 
    fclose(file);
    }
}

DIR:/xieerqi
skolodya@ubuntu:$ gcc readNull.c -o readNull                                   

DIR:/xieerqi
skolodya@ubuntu:$ ./readNull
Before while loop
After while loop

Ein weiterer Nagel im Sarg

Manchmal wird versucht zu beweisen, dass EOF ein Zeichen mit einem Code wie diesem ist:

#include <stdio.h>
int main(void)
{
    printf("%c", EOF);
    return 0;
}

Das Problem dabei ist, dass der Datentyp char ein vorzeichenbehafteter oder vorzeichenloser Wert sein kann. Darüber hinaus sind sie der kleinste adressierbare Datentyp, was sie in Mikrocontrollern mit begrenztem Speicher sehr nützlich macht. Anstatt zu deklarieren int foo = 25;, ist es üblich, in Mikrocontrollern mit kleinem Speicher char foo = 25;oder ähnlichem zu sehen. Außerdem können Zeichen signiert oder nicht signiert sein .

Mit einem Programm wie diesem könnte man die Größe in Bytes überprüfen:

#include <stdio.h>
int main(void)
{
    printf("Size of int: %lu\n",sizeof(int));
    printf("Sieze of char: %lu\n",sizeof(char));
    //printf("%s", EOF);
    return 0;
}

skolodya@ubuntu:$ ./EOF                                                        
Size of int: 4
Sieze of char: 1

Was genau ist der Punkt? Der Punkt ist, dass EOF als -1 definiert ist, der Datentyp char jedoch ganzzahlige Werte drucken kann .

IN ORDNUNG . . Was ist, wenn wir versuchen, char als Zeichenfolge zu drucken?

#include <stdio.h>
int main(void)
{
    printf("%s", EOF);
    return 0;
}

Offensichtlich ein Fehler, aber dennoch wird uns ein Fehler etwas Interessantes sagen:

skolodya @ ubuntu: $ gcc EOF.c -o EOF
EOF.c: In Funktion 'main': EOF.c: 4: 5: Warnung: Format '% s' erwartet Argument vom Typ 'char *', aber Argument 2 hat Typ 'int' [-Wformat =] printf ("% s", EOF);

Hex-Werte

Das Drucken von EOF als Hex-Wert ergibt FFFFFFFFeinen 16-Bit-Wert (8 Byte), zwei Komplimente von a -1.

#include <stdio.h>
int main(void)
{
    printf("This is EOF: %X\n", EOF);
    printf("This is Z: %X\n",'Z');
    return 0;
}

Ausgabe:

DIR:/xieerqi
skolodya@ubuntu:$ ./EOF                                                        
This is EOF: FFFFFFFF
This is Z: 5A

Eine andere merkwürdige Sache tritt mit dem folgenden Code auf:

#include <stdio.h>
int main(void)
{
   char c;
   if (c = getchar())
    printf ("%x",c);
    return 0;
}

Wenn man Shift+ drückt A, erhält man den Hex-Wert 41, offensichtlich wie in der ASCII-Tabelle. Aber für Ctrl+ Dhaben wir ffffffffwieder - den Rückgabewert von getchar()gespeichert in c.

DIR:/xieerqi
skolodya@ubuntu:$ gcc  EOF.c -o ASDF.asdf                                      

DIR:/xieerqi
skolodya@ubuntu:$ ./ASDF.asdf                                                  
A
41
DIR:/xieerqi
skolodya@ubuntu:$ ./ASDF.asdf                                                  
ffffffff

Beziehen Sie sich auf andere Sprachen

Beachten Sie, dass andere Sprachen diese Verwirrung vermeiden, da sie einen Funktions-Exit-Status auswerten und nicht mit einem Makro vergleichen. Wie liest man eine Datei in Java?

    File inputFile  = new File (filename);
    Scanner readFile = new Scanner(inputFile);
    while (readFile.hasNext())
        { //more code bellow  }

Wie wäre es mit Python?

with open("/etc/passwd") as file:
     for line in file:
          print line
Sergiy Kolodyazhnyy
quelle
Toller Punkt, tatsächlich wird irgendwann irgendwie ein Charakter gesendet.
Kos
Ich denke, das EOF-Zeichen ist etwas, das bei der Übersetzung verloren gegangen ist, da es kein tatsächliches Zeichen ist, aber EOT ist ein tatsächliches ASCII-Zeichen. Stelle dir das vor !
Sergiy Kolodyazhnyy
1
Wenn Sie versuchen, daraus zu lesen /dev/null, sollte dies auch einen EOF zurückgeben, oder? Oder was bekomme ich dort?
Byte Commander
@ByteCommander lässt es herausfinden. Mach cat / dev / null | Katze -A.
Sergiy Kolodyazhnyy
@ ByteCommander hinzugefügt Abschnitt Adressierung Ihres Kommentars
Sergiy Kolodyazhnyy
2

EOF steht für File End . Obwohl ich nicht weiß, wie das folgende Symbol ausgelöst werden soll, können Sie das folgende Programm ausführen, indem Sie eine Datei weiterleiten , die am Ende das EOF- Signal sendet :

echo "Some sample text" | ./a.out

Wo a.outist deine kompilierte Quelle?

Paulius Šukys
quelle
1
Dies wurde bereits positiv bewertet, aber in einer Randnotiz ist EOF kein Zeichen. Ich denke, das Missverständnis ergibt sich aus der Tatsache, dass es durch einen STRG-Tastendruck signalisiert wird, der normalerweise die Art ist, nicht druckbare Zeichen einzugeben. Soweit ich weiß, ist alles, was tatsächlich passiert, dass alle Eingaben geleert werden und die zu leerende Eingaben read()(syscall) zurückgegeben werden 0, was als EOF interpretiert wird: stackoverflow.com/a/1516177/4316166
kos
@kos, du hast recht, es ist immerhin ein Signal.
Paulius Šukys