Qu'est-ce que l'EOF et comment le déclencher? [fermé]

12

Ceci est mon code source C.

Quand je le construis dans Ubuntu, il commence à obtenir des caractères mais je ne sais pas comment terminer le programme, car il ne se termine pas par une entrée ENTERou un retour chariot.

Que signifie EOF? Comment puis-je le déclencher?

Cette source figure également dans un livre de 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);
}
stackprogramer
la source
4
en langage C -1est équivalent à EOF. Il est défini /usr/include/stdio.hcomme une constante macro
Edward Torvalds
1
Lecture pertinente: stackoverflow.com/q/12389518/3701431
Sergiy Kolodyazhnyy
@edwardtorvalds entrer -1en entrée ne fonctionne pas :)
Sergiy Kolodyazhnyy
Je pense que le même livre de Dennis Ritchie explique cela.
andy256
Également pertinent: unix.stackexchange.com/questions/110240/… (Aucune des réponses postées à cette question n'est entièrement correcte.)
fkraiem

Réponses:

22

Tl; dr

Vous pouvez généralement "déclencher EOF" dans un programme exécuté dans un terminal avec une touche CTRL+ Djuste après le dernier rinçage d'entrée.


Que signifie EOF? Comment puis-je le déclencher?

EOF signifie Fin de fichier.

"Déclencher EOF" dans ce cas signifie à peu près "faire savoir au programme qu'aucune entrée ne sera envoyée".

Dans ce cas, puisque getchar()renverra un nombre négatif si aucun caractère n'est lu, l'exécution est terminée.

Mais cela ne s'applique pas seulement à votre programme spécifique, il s'applique à de nombreux outils différents.

En général, le "déclenchement EOF" peut être effectué avec une touche CTRL+ Djuste après le dernier rinçage d'entrée (c'est-à-dire en envoyant une entrée vide).

Par exemple avec cat:

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

Ce qui se passe sous le capot lorsque vous appuyez sur CTRL+, Dc'est que l'entrée saisie depuis le dernier rinçage d'entrée est vidée; lorsque cela se produit pour être une entrée vide de la read()syscall appelée sur les rendements STDIN du programme 0, getchar()renvoie un nombre négatif ( -1dans la bibliothèque GNU C), ce qui est à son tour interprété comme EOF 1 .


1 - /programming//a/1516177/4316166

kos
la source
2
La compilation fonctionne, car la délimitation par des virgules n'est pas liée par le fait d'être sur la même ligne. A part ça, grande explication sur EOF :)
Paulius Šukys
@ PauliusŠukys Huh, vous avez raison. Mon C est un peu rouillé. :)
kos
1
iirc EOF n'est pas défini comme étant -1 selon la norme. C'est exactement ce qu'il se trouve dans la glibc par exemple.
larkey
1
EOF ne consiste pas «à envoyer une« entrée vide »» et la réponse SO que vous citez ne dit pas le contraire. Il s'agit d'un signal hors bande. Dans le cas d'un terminal, il est envoyé en tapant Ctrl / d.
user207421
4

TL; DR : EOF n'est pas un caractère, c'est une macro utilisée pour évaluer le retour négatif d'une fonction de lecture d'entrée. On peut utiliser Ctrl+ Dpour envoyer un EOTcaractère qui forcera le retour de la fonction-1

Chaque programmeur doit RTFM

Reportons-nous au "CA Reference Manual", de Harbison et Steele, 4e éd. à partir de 1995, page 317:

L'entier négatif EOF est une valeur qui n'est pas un codage d'un "caractère réel". . . Par exemple, fget (section 15.6) renvoie EOF à la fin du fichier, car il n'y a pas de "vrai caractère" à lire.

Ce EOFn'est essentiellement pas un caractère, mais plutôt une valeur entière implémentée stdio.hpour représenter -1. Ainsi, la réponse de kos est correcte dans la mesure où cela va, mais il ne s'agit pas de recevoir une entrée "vide". Il est important de noter qu'EOF sert ici de comparaison de valeur de retourgetchar() , pas pour signifier un caractère réel. Le man getcharsoutient que:

VALEUR DE RETOUR

fgetc (), getc () et getchar () renvoient le caractère lu sous forme de transtypage non signé à un int ou EOF à la fin du fichier ou d'une erreur.

gets () et fgets () renvoient s en cas de succès, et NULL en cas d'erreur ou lorsque la fin du fichier se produit alors qu'aucun caractère n'a été lu.

ungetc () renvoie c en cas de succès, ou EOF en cas d'erreur.

Considérez la whileboucle - son objectif principal est de répéter l'action si la condition entre crochets est vraie . Regarde encore:

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

Il dit essentiellement de continuer à faire des choses si c = getchar()renvoie un code réussi ( 0ou supérieur; c'est une chose courante d'ailleurs, essayez d'exécuter une commande réussie, echo $?puis échouez echo $?et voyez les nombres qu'ils renvoient). Donc, si nous obtenons avec succès le caractère et l'assing à C, le code d'état renvoyé est 0, l'échec est -1. EOFest défini comme -1. Par conséquent, lorsque la condition -1 == -1se produit, les boucles s'arrêtent. Et quand cela arrivera-t-il? Quand il n'y a plus de personnage à obtenir, quand c = getchar()échoue. Vous pourriez écrire while ((c = getchar ()) != -1)et ça marcherait toujours

Revenons également au code actuel, voici un extrait de stdio.h

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

Codes ASCII et EOT

Bien que le caractère EOF ne soit pas un caractère réel, cependant, il existe un caractère EOT(Fin de transmission), qui a une valeur décimale ASCII de 04; il est lié au raccourci Ctrl+ D(représenté également par un méta-caractère ^D). Le caractère de fin de transmission signifiait la fermeture d'un flux de données remontant à l'époque où les ordinateurs étaient utilisés pour contrôler les connexions téléphoniques, d'où le nom de «fin de transmission».

Il est donc possible d'envoyer cette valeur ascii au programme comme ceci, notez $'\04'qui est l'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

Ainsi, nous pouvons dire qu'il existe, mais il n'est pas imprimable

Note latérale

Nous oublions souvent que dans le passé, les ordinateurs n'étaient pas aussi polyvalents - les concepteurs doivent utiliser toutes les touches du clavier disponibles. Ainsi, l'envoi de EOTcaractère avec CtrlD est toujours "l'envoi d'un caractère", contrairement à la saisie de majuscules A, ShiftA, vous faites toujours donner à l'ordinateur une entrée avec les touches disponibles. Ainsi EOT est un vrai personnage dans le sens où il vient de l'utilisateur, il est lisible par ordinateur (bien que non imprimable, non visible par l'homme), il existe dans la mémoire de l'ordinateur

Commentaire de Byte Commander

Si vous essayez de lire à partir de / dev / null, cela devrait également renvoyer un EOF, non? Ou qu'est-ce que j'y arrive?

Oui, exactement, car /dev/nullil n'y a pas de caractère réel à lire, donc il c = getchar()renverra du -1code et le programme se fermera immédiatement. Encore une fois, la commande ne renvoie pas EOF. EOF est juste une variable constante égale à -1, que nous utilisons pour comparer le code retour de la fonction getchar . EOFn'existe pas en tant que personnage, c'est juste une valeur statique à l'intérieur stdio.h.

Démo:

# 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

Un autre clou dans le cercueil

Parfois, on tente de prouver que EOF est un caractère avec un code comme celui-ci:

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

Le problème est que le type de données char peut être une valeur signée ou non signée. De plus, ce sont les plus petits types de données adressables, ce qui les rend très utiles dans les microcontrôleurs, où la mémoire est limitée. Ainsi, au lieu de déclarer, int foo = 25;il est courant de voir dans les microcontrôleurs à petite mémoire char foo = 25;ou quelque chose de similaire. De plus, les caractères peuvent être signés ou non signés .

On pourrait vérifier que la taille en octets avec un programme comme celui-ci:

#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

Quel est le but ? Le fait est que EOF est défini comme -1, mais le type de données char peut imprimer des valeurs entières .

D'ACCORD . . Et si nous essayons d'imprimer char en chaîne?

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

Évidemment, une erreur, mais néanmoins, l'erreur nous dira quelque chose d'intéressant:

skolodya @ ubuntu: $ gcc EOF.c -o EOF
EOF.c: dans la fonction 'main': EOF.c: 4: 5: avertissement: le format '% s' attend l'argument de type 'char *', mais l'argument 2 a tapez 'int' [-Wformat =] printf ("% s", EOF);

Valeurs hexadécimales

L'impression EOF sous forme de valeur hexadécimale donne FFFFFFFF, une valeur de 16 bits (8 octets), compliment à deux de a -1.

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

Production:

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

Une autre chose curieuse se produit avec le code suivant:

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

Si l'on appuie sur Shift+ A, nous obtenons la valeur hexadécimale 41, évidemment la même que dans le tableau ASCII. Mais pour Ctrl+ D, nous avons ffffffff, encore une fois - la valeur de retour de getchar()stocké dans 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

Se référer à d'autres langues

Notez que d'autres langages évitent cette confusion, car ils opèrent sur l'évaluation d'un état de sortie de fonction, et non sur la comparaison avec une macro. Comment lit-on un fichier en Java?

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

Et le python?

with open("/etc/passwd") as file:
     for line in file:
          print line
Sergiy Kolodyazhnyy
la source
Grand point, en effet un personnage est envoyé d'une manière ou d'une autre à un moment donné.
kos
Je pense que le caractère EOF est quelque chose qui a été perdu en traduction, car ce n'est pas un caractère réel, mais EOT est un caractère ascii réel. Allez comprendre !
Sergiy Kolodyazhnyy
1
Si vous essayez de lire /dev/null, cela devrait également renvoyer un EOF, non? Ou qu'est-ce que j'y arrive?
Byte Commander
@ByteCommander permet de le découvrir. Est-ce que cat / dev / null | chat -A.
Sergiy Kolodyazhnyy
@ByteCommander a ajouté une section traitant de votre commentaire
Sergiy Kolodyazhnyy
2

EOF signifie fin de fichier . Bien que je ne sache pas comment déclencher le symbole suivant, vous pouvez exécuter le programme suivant en canalisant un fichier, qui envoie le signal EOF à la fin:

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

a.outest votre source compilée

Paulius Šukys
la source
1
Cela a déjà été voté, mais sur une note latérale, EOF n'est pas un caractère, je pense que l'idée fausse provient du fait qu'il est signalé par une frappe CTRL, qui est généralement le moyen d'entrer des caractères non imprimables. Si je comprends bien, tout ce qui se passe est que toutes les entrées sont vidées et que l'entrée à vider vide read()(syscall) reviendra 0, ce qui est interprété comme EOF: stackoverflow.com/a/1516177/4316166
kos
@kos, tu as raison, c'est un signal après tout.
Paulius Šukys