J’essaie de comprendre getchar ()! = EOF

Je lis le langage de programmation C et j’ai tout compris jusqu’à présent. Cependant, lorsque je suis tombé sur les getchar() et putchar() , je ne comprenais pas à quoi servent-elles, et plus précisément, à quoi sert le code suivant.

 main() { int c; while ((c = getchar()) != EOF) putchar(c); } 

Je comprends la fonction main() , la déclaration de l’entier c et la boucle while. Pourtant, je suis confus quant à la condition à l’intérieur de la boucle while. Quelle est l’entrée dans ce code C et quelle est la sortie.

Désolé si cette question est simple et stupide, mais je cherche simplement une explication simple avant de passer au livre et de devenir plus confus.

Ce code peut être écrit plus clairement comme:

 main() { int c; while (1) { c = getchar(); // Get one character from the input if (c == EOF) { break; } // Exit the loop if we receive EOF ("end of file") putchar(c); // Put the character to the output } } 

Le caractère EOF est reçu lorsqu’il n’y a plus d’entrée. Le nom a plus de sens dans le cas où l’entrée est lue à partir d’un fichier réel, plutôt que l’entrée utilisateur (qui est un cas particulier d’un fichier).


[En passant, généralement la fonction main devrait être écrite comme int main(void) .]

getchar() est une fonction qui lit un caractère depuis l’entrée standard . EOF est un caractère spécial utilisé en C pour indiquer que la fin du fichier a été atteinte.

En général, getchar() vous renvoie un caractère EOF lorsque votre entrée standard est autre que console (c’est-à-dire un fichier).

Si vous exécutez votre programme sous Unix comme ceci:

 $ cat somefile | ./your_program 

Ensuite, votre getchar() renverra chaque caractère d’un somefile et d’un EOF dès la fin d’un somefile .

Si vous exécutez votre programme comme ceci:

 $ ./your_program 

Et envoyez un EOF via la console (en appuyant sur CTRL+D sous Unix ou CTRL + Z sous Windows), puis getchar() retournera également EOF et l’exécution se terminera.

Peut-être avez-vous été dérouté par le fait qu’entrer -1 sur la ligne de commande ne met pas fin à votre programme? Étant donné que getchar() lit ceci sous la forme de deux caractères, – et 1. Dans l’affectation à c, le caractère est converti en valeur numérique ASCII. Cette valeur numérique est stockée dans un emplacement de mémoire auquel c.

putchar(c) récupère ensuite cette valeur, recherche la table ASCII et reconvertit le caractère imprimé.

Je suppose que trouver la valeur -1 décimale dans la table ASCII est impossible, car la table commence à 0. Donc, getchar() doit prendre en compte les différentes solutions sur différentes plates-formes. peut-être existe-t-il une version de getchar() pour chaque plate-forme?

Je trouve juste étrange que cet EOF ne soit pas dans le ascii ordinaire. Cela aurait pu être l’un des premiers caractères, qui ne sont pas imprimables. Par exemple, la fin de ligne est en ASCII.

Que se passe-t-il si vous transférez votre fichier de Windows à Linux? Le caractère du fichier EOF sera-t-il automatiquement mis à jour?

Le code écrit avec les normes C actuelles devrait être

 #include  int main(void) { int c; while ((c = getchar()) != EOF) putchar(c); } 

La boucle pourrait être réécrite comme

 int c; while (1) { c = getchar(); if (c != EOF) putchar(c); else break; } 

cela se lit comme

  • répète pour toujours
    • récupère le caractère suivant (“octet”) de l’entrée standard et le stocke dans c
    • si aucune condition exceptionnelle n’est apparue lors de la lecture dudit caractère
      • puis sort le caractère stocké dans c dans la sortie standard
    • autre
      • rompre la boucle

De nombreux langages de programmation traitent des conditions exceptionnelles en générant des exceptions qui interrompent le stream de programme normal. C ne fait pas une telle chose. Au lieu de cela, les fonctions qui peuvent échouer ont une valeur de retour et toutes les conditions exceptionnelles sont signalées par une valeur de retour spéciale, que vous devez vérifier à partir de la documentation de la fonction donnée. Dans le cas de getchar , la documentation de la norme C11 indique ( C11 7.21.7.6p3 ):

  1. La fonction getchar renvoie le caractère suivant du stream d’entrée désigné par stdin . Si le stream se trouve à la fin du fichier, l’indicateur de fin de fichier du stream est défini et getchar renvoie EOF . Si une erreur de lecture se produit, l’indicateur d’erreur du stream est défini et getchar renvoie EOF .

Il est indiqué ailleurs que EOF est une constante entière <0, et que toute valeur de retour ordinaire est> = 0 – le caractère unsigned char étendu de zéro à un int .

Le stream étant en fin de fichier, toutes les entrées ont été consommées. Pour une entrée standard, il est possible de le provoquer à partir du clavier en tapant Ctrl + D sur les terminaux Unix / Linux et Ctrl + Z dans les fenêtres de la console Windows. Une autre possibilité serait que le programme reçoive l’entrée d’un fichier ou d’un canal au lieu d’un clavier – la fin du fichier serait alors signalée chaque fois que cette entrée était complètement utilisée, c’est-à-dire

 cat file | ./myprogram 

ou

 ./myprogram < file 

Comme le dit le fragment ci-dessus, il existe deux conditions différentes qui peuvent amener getchar à renvoyer EOF : la fin de fichier a été atteinte ou une erreur réelle s'est produite. Cela ne peut pas être déduit de la valeur de retour seule. Au lieu de cela, vous devez utiliser les fonctions feof et ferror . feof(stdin) renverrait une valeur vraie si la fin de fichier était atteinte sur l'entrée standard. ferror(stdin) renverrait true en cas d'erreur.

En cas d'erreur réelle, la variable errno définie par contiendrait le code d'erreur; la fonction perror peut être utilisée pour afficher automatiquement un message d'erreur lisible par l'homme avec un préfixe. Ainsi, nous pourrions élargir l’exemple à

 #include  #include  // for the definition of errno #include  // for exit() int main(void) { int c; while ((c = getchar()) != EOF) putchar(c); if (feof(stdin)) { printf("end-of-file reached\n"); exit(0); } else if (ferror(stdin)) { printf("An error occurred. errno set to %d\n", errno); perror("Human readable explanation"); exit(1); } else { printf("This should never happen...\n"); exit('?'); } } 

Pour déclencher la fin du fichier, on utilisera Ctrl + D (affiché ici sous la forme ^D ) sur une nouvelle ligne sous Linux:

 % ./a.out Hello world Hello world ^D end-of-file reached 

(remarquez comment l' entrée est ici tamponnée en ligne, de sorte que l'entrée ne soit pas entrelacée dans la ligne avec la sortie).

De même, nous pouvons obtenir le même effet en utilisant un pipeline.

 % echo Hello world | ./a.out Hello world end-of-file reached 

Déclencher une erreur est un peu plus délicat. Dans les shells bash et zsh , l'entrée standard peut être fermée de sorte qu'elle ne vienne de nulle part, en ajoutant <&- à la ligne de commande:

 % ./a.out <&- An error occurred. errno set to 9 Human readable explanation: Bad file descriptor 

Mauvais descripteur de fichier ou EBADF signifie que le descripteur standard numéro de descripteur de fichier 0 était invalide, car il n'était pas ouvert du tout.

Une autre façon amusante de générer une erreur serait de lire l’entrée standard d’un répertoire - c’est-à-dire que errno aurait pour valeur EISDIR sous Linux:

 % ./a.out < / An error occurred. errno set to 21 Human readable explanation: Is a directory 

En fait, la valeur de retour de putchar doit également être vérifiée - elle renvoie également EOF en cas d'erreur, ou le caractère écrit:

 while ((c = getchar()) != EOF) { if (putchar(c) == EOF) { perror("putchar failed"); exit(1); } } 

Et nous pouvons maintenant tester cela en redirigeant la sortie standard vers /dev/full - il existe toutefois un piège - puisque la sortie standard est tamponnée, nous devons écrire suffisamment pour que le tampon se vide immédiatement et non à la fin du programme. Nous obtenons une infinité de zéro octets à partir de /dev/zero

  % ./a.out < /dev/zero > /dev/full putchar failed: No space left on device 

PS il est très important de toujours utiliser une variable de type int pour stocker la valeur de retour de getchar() . Même s'il lit un caractère , utiliser un caractère signed / unsigned / simple est toujours une erreur .

La fonction getchar () lit un caractère sur le clavier (c’est-à-dire, stdin )

Dans la condition dans la boucle getchar() donnée, getchar() est appelée avant chaque itération et la valeur reçue est affectée à l’entier c .

Maintenant, il faut comprendre qu’en C, l’entrée standard ( stdin ) est comme un fichier. c’est-à-dire que l’entrée est mise en mémoire tampon. L’entrée restra dans la mémoire tampon jusqu’à ce qu’elle soit réellement consommée. stdin est en fait le stream d’entrée standard .

getchar() renvoie la prochaine valeur disponible dans le tampon d’entrée.

Le programme affiche essentiellement tout ce qui a été lu à partir du clavier; y compris les espaces blancs comme \n (nouvelle ligne), espace, etc.

c’est-à-dire que l’entrée est l’entrée que l’utilisateur fournit via le clavier ( stdin signifie généralement clavier). Et la sortie est ce que nous fournissons en entrée.

L’entrée que nous fournissons est lue caractère par caractère et traitée comme un caractère même si nous le donnons sous forme de nombre.

getchar() ne retournera EOF que si la fin du fichier est atteinte. Le «fichier» qui nous intéresse ici est le stdin lui-même (entrée standard).

Imaginez un fichier contenant les entrées que nous fournissons via le clavier. C’est stdin . Ce “fichier” est comme un fichier infini . Donc pas d’ EOF .

Si nous fournissons plus d’entrées que ce que getchar() peut gérer à la fois (avant de les entrer comme entrées en appuyant sur entrée), les valeurs supplémentaires seront toujours stockées dans la mémoire tampon d’entrée non consommée. Le getchar() lira le premier caractère de l’entrée, le stockera dans c and print c with putchar (c) `.

Lors de la prochaine itération de la boucle while, les caractères supplémentaires fournis lors de la précédente itération qui sont toujours en stdin sont pris pendant while ((c = getchar()) != EOF) avec la partie c=getchar() . Le même processus est répété jusqu’à ce qu’il ne rest plus rien dans le tampon d’entrée.

Cela donne l’ putchar() que putchar() renvoie une chaîne au lieu d’un seul caractère à la fois si plus d’un caractère est donné en entrée lors d’une itération.

Ex: si l’entrée était
abcdefghijkl
la sortie aurait été la même
abcdefghijkl

Si vous ne voulez pas ce comportement, vous pouvez append fflush (stdin); juste après le putchar(c); . Ainsi, la boucle n’imprimera que le premier caractère de l’entrée fournie à chaque itération.

Ex: si l’entrée était
adgbad
seulement a sera imprimé.

L’entrée est envoyée à stdin uniquement après avoir stdin sur entrée.

putchar () est l’opposé de getchar() . Il écrit la sortie dans le stream de sortie standard ( stdout , généralement le moniteur).

EOF n’est pas un caractère présent dans le fichier. C’est quelque chose retourné par la fonction sous forme de code d’erreur.

Cependant, vous ne pourrez probablement pas sortir normalement de la boucle while. Le tampon d’entrée sera vidé (pour l’affichage sur la sortie) dès que quelque chose y entrera via le clavier et le stdin ne donnera pas EOF .

Pour quitter manuellement la boucle, EOF peut être envoyé à l’aide du clavier en appuyant sur ctrl + D sous Linux et
Ctrl + Z sous Windows

par exemple:

 while ((c = getchar()) != EOF) { putchar(c); fflush(stdin); } printf("\nGot past!"); 

Si vous appuyez sur la combinaison de touches pour donner EOF , le message Got past! sera affiché avant de quitter le programme.

Si stdin n’est pas déjà vide, vous devrez appuyer deux fois sur cette combinaison de touches. Une fois pour effacer ce tampon, puis pour simuler EOF .

EDIT: La paire de parenthèses supplémentaire autour de c = getchar() dans while ((c = getchar()) != EOF) de s’assurer que la valeur renvoyée par getchar() est d’abord atsortingbuée à c avant que cette valeur ne soit comparée à EOF .

Si cette parenthèse supplémentaire n’était pas présente, l’expression aurait été effectivement while (c = (getchar() != EOF) ) ce qui aurait signifié que c pouvait avoir une des deux valeurs suivantes: 1 (pour vrai) ou 0 (pour faux), ce qui n’est évidemment pas ce qui est prévu.

De manière similaire à la | Dans la commande pipe ci-dessus, vous pouvez utiliser la redirection sur votre système pour utiliser le code ci-dessus afin d’afficher tout le contenu en caractères d’un fichier, jusqu’à ce qu’il atteigne généralement la fin (EOF) représentée par CTRL-Z ou CTRL-D.

Dans la console: ProgramName < FileName1.txt

Et pour créer une copie de ce qui est lu à partir de FileName1, vous pouvez: ProgramName < FileName1.txt > CopyOfInput.txt

Cela démontre votre programme de multiples façons pour vous aider, espérons-le, à votre compréhension.

-J'espère que cela pourra aider.

 main(){ int c; while ((c = getchar()) != EOF) putchar(c); } 

En fait, c = getchar () fournit le caractère que l’utilisateur entre sur la console et cette valeur est vérifiée avec EOF qui représente la fin du fichier. EOF est rencontré au dernier du fichier. (c = getchar ())! = EOF équivaut à c! = EOF. Maintenant, je pense que c’est beaucoup plus facile. Si vous avez d’autres questions, faites le moi savoir.

  getchar() 

obtient un caractère d’entrée.

  c = getchar() 

La valeur de cette affectation est la valeur du côté gauche après l’affectation ou la valeur du caractère qui a été lu. La valeur de EOF est -1 par défaut.

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

Tant que la valeur rest autre chose que EOF ou, en d’autres termes, tant que la condition rest vraie, la boucle continue d’itérer. Une fois que la valeur devient EOF la valeur de la condition entière sera 0 et la boucle sera interrompue.

Les parenthèses supplémentaires autour de c = getchar() sont destinées au compilateur, soulignant que nous voulions vraiment effectuer une affectation dans la condition, car cela suppose généralement que vous souhaitiez taper == et vous avertir.

  main() { int c; while ((c = getchar()) != EOF) putchar(c); } 

Donc tout le code renvoie ce que vous avez entré. Il atsortingbue la valeur des caractères à c à l’intérieur de la condition, puis la renvoie dans le corps de la boucle, ne se terminant que lorsque la fin du fichier est détectée.