web-dev-qa-db-fra.com

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 fonctions getchar() et putchar(), je ne comprenais pas à quoi servent-elles, et plus précisément ce que fait 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 de 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.

30
Paolo Bernasconi

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, la fonction main devrait généralement être écrite sous la forme int main(void).]

27

getchar() est une fonction qui lit un caractère de entrée standard. EOF est un caractère spécial utilisé dans C pour indiquer que le END OF FILE a été atteint.

Vous obtiendrez généralement un caractère EOF renvoyant de getchar() 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 dans somefile et EOF dès que somefile se termine.

Si vous exécutez votre programme comme ceci:

$ ./your_program

Et envoyez une EOF via la console (en tapant CTRL+D sous Unix ou CTRL + Z sous Windows), alors getchar() renverra également EOF et l'exécution sera terminée.

15
Pablo Santa Cruz

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 accède c.

putchar(c) récupère ensuite cette valeur, recherche la table ASCII et reconvertit en caractère, lequel est 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 getchar() pour chaque plate-forme?

Je trouve juste étrange que ce EOF ne soit pas dans l’asci 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 vers Linux? Le caractère de fichier EOF sera-t-il automatiquement mis à jour?

4
Pedro Norwego

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

#include <stdio.h>

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éter pour toujours
    • récupère le caractère suivant ("octet") de l'entrée de entrée standard et le stocke dans c
    • si aucune condition exceptionnelle n’est apparue lors de la lecture dudit caractère
      • puis affichez le caractère stocké dans c dans sortie standard
    • autre
      • rompre la boucle

De nombreux langages de programmation traitent des conditions exceptionnelles via levant des exceptions qui interrompent le flux 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.6p ):

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

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

Le flux étant en fin de fichier, toutes les entrées ont été consommées. Pour une entrée standard, il est possible de le provoquer depuis le 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 fragment ci-dessus l'indique, il existe deux conditions différentes qui peuvent entraîner le retour de getchar à EOF: soit le fin du fichier a été atteint, o une erreur réelle s'est produite. Cela ne peut ê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.

Si une erreur réelle se produisait, la variable errno définie par <errno.h> 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 <stdio.h>
#include <errno.h> // for the definition of errno
#include <stdlib.h> // 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 par ^D) sur une nouvelle ligne sous Linux:

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

(remarquez comment entrée ici est mis en ligne, de sorte que l'entrée n'est 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 shell bash et zsh, l'entrée standard peut être closed afin 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

Descripteur de fichier incorrect, ou EBADF signifie que le numéro de descripteur de fichier entrée standard - 0 était incorrect, 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 à partir d'un répertoire - ceci provoque la définition de errno avec 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 maintenant, nous pouvons 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

P.S. 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, tiliser signed/unsigned/plain char est toujours faux .

3
Antti Haapala
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 est équivalent à c! = EOF. Maintenant, je pense que c'est beaucoup plus facile. Si vous avez d'autres questions, faites le moi savoir. 

0
Shivam Goel
 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 par défaut -1.

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

Tant que la valeur reste autre chose que EOF ou, en d'autres termes, tant que la condition reste 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() concernent le compilateur, ce qui montre bien que nous voulions vraiment effectuer une affectation dans la condition, car cela suppose généralement que vous souhaitiez saisir == et vous avertir.

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

Ainsi, tout le code renvoie en réalité ce que vous avez entré. Il attribue la valeur des caractères à c à l'intérieur de la condition, puis la renvoie dans le corps de la boucle pour ne se terminer que lorsque la fin du fichier est détectée.

0
Darinka Zobenica

D'une 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. 

En 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.