IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C Discussion :

vérifier les données entrées


Sujet :

C

  1. #1
    Membre du Club Avatar de Redgard
    Homme Profil pro
    x
    Inscrit en
    Décembre 2014
    Messages
    90
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : x

    Informations forums :
    Inscription : Décembre 2014
    Messages : 90
    Points : 60
    Points
    60
    Par défaut vérifier les données entrées
    Bonjour,

    M'amusant à coder, je me suis lancé dans un projet de "calculatrice" appconsole. J'aimerais vérifier les entrées, ce qui fait qu'en tournant sur internet, je suis tombé sur ça:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    int status, input;
    status = scanf("%d", &input);
    while(status!=1){
    	printf("Incorrect number... please try again: ");
    	status = scanf("%d", &input);
    }
    J'en avais déjà entendu parlé, mais le problème est que tu tombes sur une boucle infinie, si tu n'entres pas la bonne valeur (ex: "a"). Une personne a corrigé ce problème de boucle infinie dans le code suivant, mais je ne comprend pas bien la deuxième boucle "while".
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #include<stdio.h>
    int main(void){
    	// input	user input -- hopefully a number
    	// temp		used to collect garbage characters
    	// status	did the user enter a number?
    	int input, temp, status;
     
    	printf("Please enter a number: ");
    	status = scanf("%d", &input);
    	while(status!=1){
    		while((temp=getchar()) != EOF && temp != '\n');
    		printf("Invalid input... please enter a number: ");
    		status = scanf("%d", &input);
    	}
     
    	printf("Your number is %d\n",input);
    	return 0;
    }
    Si je ne trompe pas, la deuxième boucle "while" va s'exécuter tant que caractère contenu dans "temp" est différent de "end of file" et d'un "retour à la ligne". Cette boucle "While" ne comprend que "printf", si je me trompe pas? Mais en quoi ça va changer/résoudre le problème? Est-ce lié à une différence dans la manière les entiers et les caractères sont stockés dans la mémoire?

    Cas théorique:
    - je rentre "1" et appuie sur "entré". Étant une variable "entier", je présume quelle ne stocke pas "\n" quand j'appuie sur "entré", ce qui fait que la fonction "scanf" renvoie "1' qui est stocké dans "status". De là, les deux boucles suivantes ne s'exécutent pas et le programme s'arrête.
    - je rentre "10" et appuie sur "entré". Etant une variable "entier", je présume quelle ne stocke pas "\n" quand j'appuie sur "entré", ce qui fait que la fonction "scanf" renvoie "2" (??) qui est stocké dans "status". De là, mes deux boucles "while" ne sont-elles pas censé s'exécuter pour me renvoyer un message d'erreur? Comment ça se fait que je ne reçois pas de message d'erreur? l'entier "10" est traité comme un seul caractère?
    - je rentre "a" et appuie sur "entré". Etant une variable "character", je présume quelle stocke "\n" quand j'appuie sur "entré", ce qui fait que la fonction "scanf" renvoie "2" (??) qui est stocké dans "status". De là, mes deux boucles "while" s'active et me renvoient le message d'erreur. Mais comment ça se fait que je tombe pas dans une boucle infinie?


    Merci d'avance pour votre aide,
    Red'

  2. #2
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Redgard Voir le message
    ce qui fait que la fonction "scanf" renvoie "2" (??) qui est stocké dans "status"

    Bonjour

    Tu as bien deviné. Le souci vient du '\n'. Tu t'es juste trompé sur l'analyse de scanf() car cette fonction renvoie le nombre d'éléments correctement récupérés. Si tu demandes "un" nombre, alors la fonction renverra 1 si elle a bien récupéré un nombre (quel que soit son nombre de chiffres) et 0 dans le cas contraire. Donc si tu entres 'a', la fonction n'arrive pas à récupérer de nombre donc elle renvoie 0 (et pas 2).

    Le soucis du scanf(), c'est qu'il ne prend que ce qui correspond pile-poil à ce qui est demandé.
    Or, quand tu entres un nombre (ex 123), tu appuies sur "1", "2", "3" et "<return>" pour valider. Ben ce "<return>" n'étant pas du nombre n'est alors pas récupéré et reste dans stdin (le clavier).

    Cette boucle interne n'a d'autre but que de supprimer du clavier tout ce qui n'a pas été récupéré et qui peut foutre la grouille à la saisie suivante.

    Personnellement, je n'aime pas trop "purger" stdin pour plusieurs raisons dont la première est que stdin c'est souvent le clavier mais pas toujours. Dans le cas de programmes chainés par des pipes (monde Unix/Linux), la sortie d'un programme "p1" va alimenter l'entrée du programe "p2" et pour "p2", stdin ce n'est alors plus le clavier mais ce qui vient de "p1". C'est alors dommage de "considérer" que ce qui vient de "p1" n'est pas digne d'intérêt et doit alors être supprimé.
    Une autre raison est que si on maitrise son clavier, on 'a alors pas besoin de le purger.

    Donc pour éviter d'avoir à purger stdin, j'utilise une autre méthode: je commence par récupérer toute la ligne (tout le texte situé entre le début et le "<return>") en tant que "texte brut". Ainsi, le "<return>" est récupéré lui aussi donc n'a plus besoin d'être purgé.
    Ensuite, ne reste qu'à analyser le texte brut récupéré pour vérifier si ce texte correspond à ce que je veux. Et pour ça, il y a sscanf() , qui, comme sa soeur, renvoie le nombre d'éléments correctement récupérés. Ne reste donc qu'à tester ce nombre.

    Exemple
    Code c : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    char zone[1000 + 1];
    int nb;
    while (1) {
        fputs("Entrez un nombre :", stdout);
        fgets(zone, 1000 + 1, stdin);
        /* Je veux un nombre donc je dois vérifier si scanf() récupère bien un élément */
        if (sscanf(zone, "%d", nb) == 1) break;
        printf("Entrée incorrecte - Ce n'est pas un nombre - Recommencez !!!\n");
    }
    printf("Le nombre saisi est %d\n", nb);
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  3. #3
    Expert éminent sénior
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 369
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 369
    Points : 41 519
    Points
    41 519
    Par défaut
    Citation Envoyé par Sve@r Voir le message
    Personnellement, je n'aime pas trop "purger" stdin pour plusieurs raisons dont la première est que stdin c'est souvent le clavier mais pas toujours. Dans le cas de programmes chainés par des pipes (monde Unix/Linux), la sortie d'un programme "p1" va alimenter l'entrée du programe "p2" et pour "p2", stdin ce n'est alors plus le clavier mais ce qui vient de "p1". C'est alors dommage de "considérer" que ce qui vient de "p1" n'est pas digne d'intérêt et doit alors être supprimé.
    Une autre raison est que si on maitrise son clavier, on 'a alors pas besoin de le purger.

    Donc pour éviter d'avoir à purger stdin, j'utilise une autre méthode: je commence par récupérer toute la ligne (tout le texte situé entre le début et le "<return>") en tant que "texte brut". Ainsi, le "<return>" est récupéré lui aussi donc n'a plus besoin d'être purgé.
    Ensuite, ne reste qu'à analyser le texte brut récupéré pour vérifier si ce texte correspond à ce que je veux. Et pour ça, il y a sscanf() , qui, comme sa soeur, renvoie le nombre d'éléments correctement récupérés. Ne reste donc qu'à tester ce nombre.
    C'est exactement la même chose.
    Dans tous les cas, tu lis une ligne et ne parses que le début de celle-ci avant de jeter le reste.
    dommage de "considérer" que ce qui vient de "p1" n'est pas digne d'intérêt
    C'est n'est pas tout ce qui vient de p1, mais seulement le reste de la ligne. Si p2 attend un seul entier par ligne, alors avec les deux codes le reste de la ligne est ignoré.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  4. #4
    Membre du Club Avatar de Redgard
    Homme Profil pro
    x
    Inscrit en
    Décembre 2014
    Messages
    90
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : x

    Informations forums :
    Inscription : Décembre 2014
    Messages : 90
    Points : 60
    Points
    60
    Par défaut
    Pour Scanf():

    Donc en bref, Scanf() fait plus de la gestion d'erreur, je pensais quelle dénombrait les entrées. J'ai du mal-lire/comprendre.

    "On success, the function returns the number of items of the argument list successfully filled. This count can match the expected number of items or be less (even zero) due to a matching failure, a reading error, or the reach of the end-of-file."
    source: http://www.cplusplus.com/reference/c...canf/?kw=scanf

    Je comprends pas cette phrase, alors... quand il parle d'objets, il parle d'une "ligne de charactères"?

    C'est intéressant de comprendre un peu mieux le fonctionnement d'une fonction, car ce genre de détail n'est pas nécessairement rapporté sur les sites de référence.


    Quand tu dis que ça peut foutre la grouille, tu veux dire un peu comme un débordement de mémoire dans le cas d'un tableau? Donc dans l'idée, on entre de nouvelles données et on se retrouve avec des caractères de la saisie précédente?




    En réfléchissant de mon côté, j'avais hésité à récupérer une ligne de caractères, pour ensuite la traiter et la convertire en entier (atoi()), mais j'avais un peu l'impression de compliquer la chose pour rien.




    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    char zone[1000 + 1];
    int nb;
    while (1) {
        fputs("Entrez un nombre :", stdout);
        fgets(zone, 1000 + 1, stdin);
        /* Je veux un nombre donc je dois vérifier si scanf() récupère bien un élément */
        if (sscanf(zone, "%d", nb) == 1) break;
        printf("Entrée incorrecte - Ce n'est pas un nombre - Recommencez !!!\n);
    }
    printf("Le nombre saisi est %d\n", nb);
    Si je ne me trompe pas dans ma lecture:
    1. tu crées une boucle while infinie.
    2. tu récupères les données entrées au clavier dans un tableau "zone" de 1000+1 caractères.
    3. tu copies le contenu de ce tableau dans un variable entier "nb", et testes directement si la copie c'est bien passé. Si ça s'est bien passé, tu brises/casses la boucle infinie, sortant de celle-ci et n'exécutant pas printf().


    Quelle intérêt de préférer fputs() (source) à printf()(source)?
    Le premier étant utilisé pour envoyer des données dans un fichier/vers l'écran/autre et le deuxième se limitant à l'affichage de donnée sur l'écran

  5. #5
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Redgard Voir le message
    Donc en bref, Scanf() fait plus de la gestion d'erreur, je pensais quelle dénombrait les entrées.
    Elle dénombre les entrées correctement récupérées !!! C'est une nuance importante...

    Citation Envoyé par Redgard Voir le message
    "On success, the function returns the number of items of the argument list successfully filled. This count can match the expected number of items or be less (even zero) due to a matching failure, a reading error, or the reach of the end-of-file."
    source: http://www.cplusplus.com/reference/c...canf/?kw=scanf

    Je comprends pas cette phrase, alors... quand il parle d'objets, il parle d'une "ligne de charactères"?
    Il parle pas d'objets mais d'"items" (éléments). Expression générique pour citer les int, float, strings etc que peut récupérer scanf.

    Citation Envoyé par Redgard Voir le message
    Quand tu dis que ça peut foutre la grouille, tu veux dire un peu comme un débordement de mémoire dans le cas d'un tableau? Donc dans l'idée, on entre de nouvelles données et on se retrouve avec des caractères de la saisie précédente?
    Exactement. Le pire c'est une saisie de string juste après une saisie de nombre. La fonction récupère le "\n" sans s'arrêter et l'utilisateur reste planté comme un rosier devant son programme qui part en torche...

    Citation Envoyé par Redgard Voir le message
    Si je ne me trompe pas dans ma lecture:
    1. tu crées une boucle while infinie.
    2. tu récupères les données entrées au clavier dans un tableau "zone" de 1000+1 caractères.
    3. tu copies le contenu de ce tableau dans un variable entier "nb", et testes directement si la copie c'est bien passé. Si ça s'est bien passé, tu brises/casses la boucle infinie, sortant de celle-ci et n'exécutant pas printf().
    Exact. Tant qu'on n'entre pas un vrai nombre, le truc repose la question. Et quoi qu'il arrive, au final stdin est clean.

    Citation Envoyé par Redgard Voir le message
    Quelle intérêt de préférer fputs() (source) à printf()(source)?
    Le premier étant utilisé pour envoyer des données dans un fichier/vers l'écran/autre et le deuxième se limitant à l'affichage de donnée sur l'écran
    Dans le monde du C, l'écran, le clavier, la mémoire, le disque dur ou autre sont tous considérés comme des fichiers. J'utilise donc fputs() pour envoyer une chaine dans le fichier "stdout" (l'écran) tout comme j'aurais pu utiliser printf() ou même fprintf(stdout, ...).
    Sauf que printf() et ses soeurs effectuent un travail d'analyse de la chaine (remplacer les "%x" "%y" etc par leurs valeurs) dont je n'ai pas besoin ici donc autant utiliser une fonction qui ne fait pas ce travail d'analyse et qui, par voie de conséquence, sera plus rapide...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Recouper les données entre deux listes
    Par greg4646 dans le forum Macros et VBA Excel
    Réponses: 7
    Dernier message: 15/08/2007, 10h27
  2. exporter les données entre deux tables différentes ?
    Par tizilfin dans le forum Oracle
    Réponses: 3
    Dernier message: 12/03/2007, 15h39
  3. Vérifier les données
    Par christi dans le forum ASP.NET
    Réponses: 3
    Dernier message: 08/02/2007, 09h23
  4. Réponses: 3
    Dernier message: 12/01/2007, 17h43
  5. comment transférer les données entre 2 pc?.
    Par unix27 dans le forum Administration
    Réponses: 12
    Dernier message: 10/04/2006, 07h48

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo