Partage
  • Partager sur Facebook
  • Partager sur Twitter

Problème avec strcmp

    30 juin 2019 à 13:09:26

    Bonjour,

    j'aimerais  : a) que l'utilisateur entre un mot, b) que celui-ci soit enregistré dans une chaîne de caractères et c) vérifier si ce mot est valide.

    Lorsque tous les mots à comparer font la même longueur et sont entrés dans une chaîne qui est pile à leur taille, je ne rencontre aucun problème :

    int main(void)
    
    {
    
    	char criAnimal[5];
    
    
    	puts("Vous êtes plutôt WOUF, MIOU ou PIOU ?");
    	fgets(criAnimal, 5, stdin);
    	viderBuffer();
    
    	while(strcmp(criAnimal, "WOUF") != 0 && strcmp(criAnimal, "MIOU") != 0 && strcmp(criAnimal, "PIOU") !=0)
    		{
    			puts("Saisie invalide. Essayez à nouveau.");
    			fgets(criAnimal, 5, stdin);
    			viderBuffer();
    		}
    
    	puts("SAISIE CORRECTE");
    return 0;
    }

    En revanche, lorsque je dois prévoir une chaîne suffisamment grande pour contenir des mots de différentes longueurs, strcmp ne reconnaîtra jamais comme corrects les mots qui ne remplissent pas complètement le tableau (caractère de fin inclus). Ainsi, dans le programme suivant, CHIENS et CHATS ne seront jamais reconnus comme corrects, alors que OISEAUX le sera :

    int main(void)
    
    {
    
        char nomAnimal[8];        
    
        puts("Que préférez-vous : les chiens, les chats ou les oiseaux ?\n"
            "Tapez CHIENS, CHATS ou OISEAUX pour répondre.");
        fgets(nomAnimal, 8, stdin);
        viderBuffer();
    
        while(strcmp(nomAnimal, "CHIENS") != 0 && strcmp(nomAnimal, "CHATS") != 0 && strcmp(nomAnimal, "OISEAUX") !=0)
            {
                puts("Saisie invalide. Essayez à nouveau.");
                fgets(nomAnimal, 8, stdin);
                viderBuffer();
            }
    
        puts("SAISIE CORRECTE");
    
    return 0;
    
    }
    

    Est-ce qu'il existe une solution pour régler ce problème ?
    Merci d'avance pour votre aide.

    • Partager sur Facebook
    • Partager sur Twitter
      30 juin 2019 à 14:09:49

      fgets mets le retour de chariot '\n' dans la chaîne de caractère.
      • Partager sur Facebook
      • Partager sur Twitter
        30 juin 2019 à 15:00:41

        Mais il le fait aussi quand la chaîne de caractère fait pile la bonne dimension pour contenir la saisie de l'utilisateur et le caractère spécial, non ? (Dans mon premier exemple,  fgets devrait rendre "MIOU\n", "PIOU\n" ou "WOUF", puisque j'ai prévu 5 emplacements de mémoire pour ces mots qui font 4 lettres), ou je me trompe ?) Du coup, je ne vois pas en quoi une chaîne plus longue change quoi que ce soit,..
        • Partager sur Facebook
        • Partager sur Twitter
          30 juin 2019 à 15:49:13

          Hello,

          fgets(....,N,....) lit au plus N-1 caractères, et met un \0 après le dernier caractère lu. Si un retour chariot est lu avant que N-1 caractères aient été lus, alors il est mis dans la chaine lue, et l'input se termine.

          Dans le cas de ce petit exemple:

          	char str[5];
          	
          	fgets(str,sizeof(str),stdin);
          	printf("|%s|",str);
          
          

          123[ENTER] mettra 123\n\0 dans str

          |123
          |

          1234[ENTER] mettra 1234\0 dans str

          |1234|

          Edit (petit oubli): et laissera le \n dans le buffer clavier

          12345[ENTER] mettra 1234\0 dans str

          |1234|

          et laissera le 5 et le \n dans le buffer clavier.

          -
          Edité par edgarjacobs 30 juin 2019 à 17:27:25

          • Partager sur Facebook
          • Partager sur Twitter

          On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

            30 juin 2019 à 16:04:37

            Grenade90 a écrit:

            Mais il le fait aussi quand la chaîne de caractère fait pile la bonne dimension pour contenir la saisie de l'utilisateur et le caractère spécial, non ? 

            non puisque tu as limité le nombre de caractères à pile poil le nombre de lettre à saisir.

            par contre dans ce cas MIOUMIOU est une saisie correcte. (il suffit que les quatre première lettre soit correctes.

            -
            Edité par rouloude 30 juin 2019 à 16:15:37

            • Partager sur Facebook
            • Partager sur Twitter
              30 juin 2019 à 21:34:19

              (Tiens, c'est curieux ! je croyais avoir déjà répondu tout à l'heure... j'espère que je n'ai pas posté ma réponse dans un mauvais fil.)

              Ah d'accord, je pensais en fait que fgets enlevait le \0 pour mettre le \n à la place ; je n'avais pas compris que cette fonctio mettait le \n après le \0.

              Du coup, une question : est-ce que c'est une erreur, avec fgets, de ne réserver que 5 emplacements pour des mots de 4 lettres ? (Je veux dire, même si les 5 emplacements m'arrangent dans mon exemple, est-ce que ce n'est pas une mauvaise gestion de la mémoire ?)

              Sinon, je vais tenter d'enlever \n à la fin de fgets (j'ai vu que c'était possible) et je reviens vers vous pour vous dire si ça a marché ou non.

              Merci !

              • Partager sur Facebook
              • Partager sur Twitter
                30 juin 2019 à 23:29:45

                C est une erreur d'utiliser fgets pour des saisies interactives qui sont dans un dialogue ligne par ligne.

                Utilisez getline  pour lire une ligne complète.

                • Partager sur Facebook
                • Partager sur Twitter
                  30 juin 2019 à 23:31:40

                  Re-bonjour !

                  J'ai réussi à enlever le '\n' du fgets et maintenant le code fonctionne.

                  Il me reste une question : même si le code fonctionne parce que (en principe) j'ai enlevé le retour à la ligne, lorsque je choisis un mots pour le stocker dans nomAnimal, je dois encore appuyer deux fois sur entrée dans la console (alors que je n'ai pas le problème avec criAnimal). Est-ce que vous savez pourquoi ?

                  PS : en lisant le manuel (ou plutôt  les explications de "Tutorial Points"), j'ai répondu toute seule à ma question dans le dernier message : si la chaîne de caractères prévoit 4 emplacements pour des lettres + 1 pour le caractère de fin de chaîne, alors fgets doit aussi prévoir 5 emplacements. Pas besoin d'en prévoir un sixième pour le retour à la ligne en plus (à moins que vous ne me disiez le contraire...)

                  Voici mon code :

                  void nettoieFgets(char chaine[])              //Enlève le retour à la ligne de fgets
                  {
                  	char *chercheN = strchr(chaine, '\n');
                  	if (chercheN != NULL)
                  		{
                  		       *chercheN = 0;
                  		}
                  
                  }
                  
                  
                  int main(void)
                  
                  {
                  
                  	char criAnimal[5]; // WOUF, MIOU ou PIOU
                  	char nomAnimal[8]; //CHIENS, CHATS ou OISEAUX
                  	int nouvelleSaisie = 0; // 1 = la saisie précédente de l'utilisateur était erronée.
                  
                  
                  	while(strcmp(criAnimal, "WOUF") != 0 && strcmp(criAnimal, "MIOU") != 0 && strcmp(criAnimal, "PIOU") !=0)
                  		{
                  
                  			if (nouvelleSaisie == 0)
                  				{
                  					puts("Vous êtes plutôt WOUF, MIOU ou PIOU ?");
                  				}
                  			else
                  				{
                  					puts("Saisie invalide. Essayez à nouveau.");
                  				}
                  		fgets(criAnimal, 5, stdin);
                  		viderBuffer();
                  		nettoieFgets(criAnimal);
                  		nouvelleSaisie = 1;
                  		}
                  
                  
                  	puts("SAISIE CORRECTE");
                  	nouvelleSaisie = 0;
                  
                  
                  	while(strcmp(nomAnimal, "CHIENS") != 0 && strcmp(nomAnimal, "CHATS") != 0 && strcmp(nomAnimal, "OISEAUX") !=0)
                  		{
                  
                  			if (nouvelleSaisie == 0)
                  				{
                  					puts("Vous êtes plutôt CHIENS, CHATS ou OISEAUX ?");
                  				}
                  			else
                  				{
                  					puts("Saisie invalide. Essayez à nouveau.");
                  				}
                  		fgets(nomAnimal, 8, stdin);
                  		viderBuffer();
                  		nettoieFgets(nomAnimal);
                  		nouvelleSaisie = 1;
                  		}
                  
                  	puts("SAISIE CORRECTE");
                  	nouvelleSaisie = 0;
                  
                  return 0;
                  }

                  [EDIT : Michel, je n'avais pas vu ta (votre ?) réponse car nous avons posté presque en même temps.]

                  1) Il me semble que getline n'est pas standard, mais juste une extension de GNU : http://manpagesfr.free.fr/man/man3/getline.3.html.

                  2) Que serait l'intérêt d'utiliser getline plutôt que fgets ? Je n'ai absolument pas compris les explications du manuel...

                  -
                  Edité par Grenade90 1 juillet 2019 à 0:08:37

                  • Partager sur Facebook
                  • Partager sur Twitter
                    1 juillet 2019 à 7:21:31

                    Avec une source de documentation plus à jour, getline est dans la norme POSIX depuis 10 ans.

                    Getline utilise un tampon extensible pour y loger la ligne lue dans un fichier texte où la console, et réalloue ce tampon au besoin.

                    Intérêt : lit une ligne sans avoir à fixer une limite de taille arbitraire.

                    On lui passe donc 

                    • L'adresse du pointeur vers le début de ligne
                    • L'adresse de l'entier qui contient la taille du tampon
                    • Et bien sûr le FILE* sur lequel on lit

                    Au début, mettre NULL pour le tampon, et l'adresse d'un entier contenant 0. C'est simple. Utiliser, réutiliser. À la fin, faire un free pour libérer le tampon.

                    Exemple :

                    #define  _POSIX_C_SOURCE  200809L
                    
                    #include <stdlib.h>
                    #include <stdio.h>
                    #include <stdbool.h>
                    #include <string.h>
                    
                    int main()
                    {
                    	char * ligne = NULL;
                    	size_t taille = 0;
                    	int numero = 0;
                    	printf("tapez du texte, STOP pour arreter\n");
                    	
                    	do {
                    		getline(& ligne, & taille, stdin);
                    		numero ++;
                    		printf("%d -> %s", numero, ligne);
                    	}
                        while (strncmp(ligne, "STOP\n", 5) != 0);
                        free(ligne);
                        
                    	return 0;
                    }
                    
                    



                    -
                    Edité par michelbillaud 1 juillet 2019 à 10:31:22

                    • Partager sur Facebook
                    • Partager sur Twitter
                      1 juillet 2019 à 16:24:31

                      Grenade90 a écrit:

                      Il me reste une question : même si le code fonctionne parce que (en principe) j'ai enlevé le retour à la ligne, lorsque je choisis un mots pour le stocker dans nomAnimal, je dois encore appuyer deux fois sur entrée dans la console (alors que je n'ai pas le problème avec criAnimal). Est-ce que vous savez pourquoi ?

                      Probablement ta fonction viderBuffer qui bloque sur getchar car le buffer clavier est déjà vide !

                      • Partager sur Facebook
                      • Partager sur Twitter
                        1 juillet 2019 à 23:29:56

                        Merci Rouloude, je ne voyais pas du tout ce que ça pouvait être.

                        Michel : la fonction getline est beaucoup plus simple à comprendre avec tes explications. Seulement, je me demande : est-ce que cette norme POSIX est portable sur Windows et Mac ? (D'après Wikipedia, la norme POSIX de 1990 est portable sur Windows, mais je ne sais pas ce qu'il en est pour getline, de 20 ans plus récent. Est-ce qu'il y a un site sur lequel je peux me renseigner ? Le seul article un peu récent sur getline que j'aie trouvé (https://blog.udemy.com/c-getline/) n'en parle pas.) En tout cas, un grand merci, car tu es souvent là pour répondre à mes questions. :)

                        -
                        Edité par Grenade90 1 juillet 2019 à 23:30:30

                        • Partager sur Facebook
                        • Partager sur Twitter
                          1 juillet 2019 à 23:45:10

                          je n'utilise ni l'un ni l'autre, mais je veux bien essayer si on m'offre un ordinateur pour chaque.
                          • Partager sur Facebook
                          • Partager sur Twitter

                          Problème avec strcmp

                          × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                          × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                          • Editeur
                          • Markdown