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.
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).
main
devrait généralement être écrite sous la forme int main(void)
.]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.
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?
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
c
c
dans sortie standardDe 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 ):
- La fonction
getchar
renvoie le prochain caractère du flux d'entrée pointé parstdin
. Si le flux se trouve à la fin du fichier, l'indicateur de fin de fichier du flux est défini etgetchar
renvoieEOF
. Si une erreur de lecture se produit, le code d'erreur du flux est défini etgetchar
renvoieEOF
.
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 .
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.
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.
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.