Partage
  • Partager sur Facebook
  • Partager sur Twitter

Problème d'affichage de tableau suite à fclose()

    3 décembre 2019 à 11:58:36

    Bonjour,

    Tout d'abord, n'hésitez pas à me transférer vers un sujet résolu similaire, si vous en connaissez un. Mon problème est si particulier que je n'ai pas réussi à le décrire correctement avec des mots-clefs pour en trouver un équivalent.

    Aussi, ce problème est double mais je suspecte une origine commune, ce pourquoi je ne fais qu'un seul poste.

    Je développe donc le pendu du cours de C.

    Pour tester mon code, j'ai un fichier de 4 mots comme ceci :

    Fichier de mots pour tester le pendu

    PROBLEME 1 :

    a) J'utilise donc la fonction choisirMot() de mon Pendu.h pour choisir un mot aléatoirement dans mon fichier de mots.

    Une table "mot_choisi" est donc mise à jour avec le mot en question.

    b) Puis, avec la fonction creerMotMasque(), je crée un autre tableau, qui ne contient pour le moment que des '*' (autant que de caractères dans mot_choisi).

    c) Enfin, j'affiche ce mot_masque à chaque tout de jeu (pour suivre l'évolution du mot à deviner).

    ET LA, est mon problème !

    Le mot masqué, s'il est autre que "APERO" (S'il n'est donc pas le mot de la ligne 0) est affiché ... avec des caractères incompréensibles à la suite (Est-ce la vie n'essaierait pas de me faire un signe ?! ... ;) ).

    Tels que :

    Affichage console du problème

    J'ai fais de nombreux tests et je me suis perçu que le problème disparaissait si je supprimais la ligne 54 de Pendu.h : fclose(fichier).

    Or il est important de fermer le fichier après utilisation !

    Sauriez-vous donc quel est le problème ?

    PROBLEME 2 :


    J'en viens donc à mon second problème.

    Mettons que j'ai supprimé cette fameuse ligne 54 du Pendu.h : fclose(fichier).

    Le premier tout se passe très bien mais à parti du deuxième tour, rebelote. Le 2nd mot_masque est affiché avec des caractères inutiles, SAUF SI CE SECOND MOT EST APERO ! (Décidément, je vais finir par y croire !)


    J'ai plusieurs hypothèses que je n'ai pas réussi à mener à bien (probablement par manque de connaissance des fonctions utilisées) :

    - La première serait un problème dans la capture du mot_choisi. Si "APERO" est épargné, c'est peut être parce qu'il est sur la première ligne ?

    - Ou alors, serait-ce un problème d'initialisation/déclaration du pointeur/tableau mot_masque ? Ce qui pourraient expliquer le problème du 1er tour et celui du 2nd ? Ou un problème lorsdu free(mot_masque) ligne 99 de main() ?


    Merci à toi, si tu as un le courage de me lire jusqu'ici !

    Si le post est trop long, n'hésitez pas à me le dire et je pourrais le découper en deux posts différents.


    Merci et bonne journée,

    Main :

    Ci-dessous, le code du main() et du Pendu.h

    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <string.h>
    #include <ctype.h>
    
    #include"Pendu.h"
    
    void afficherRegles();
    void jouerSolo(char* mot_masque, char mot_choisi[]);
    int lireChoixMenu();
    
    int main()
    {
    
        srand(time(NULL));              // Initialisation variable pour choix aléatoire
        int choixMenu = 0;
        char mot_choisi [50] = "";      // Tableau contenant le mot à deviner (= mot choisi aléatoirement par le programme)
        char* mot_masque = NULL;        // Pointeur vers le futur tableau qui contiendra le mot avec les lettres devinées
    
    while(choixMenu != 4){
    
        printf("\nBonjour et bienvenue dans le jeu de pendu !\n\n");
        printf("\nTapez 1 :\tPour consultez les règles du jeu.\n");
        printf("\nTapez 2 :\tPour faire une partie en solo.\n");
        printf("\nTapez 3 :\tPour faire une partie en duo\n");
        printf("\nTapez 4 :\tPour quitter\n\n");
        choixMenu = lireChoixMenu();
    
        switch (choixMenu)
        {
        case 1:
            afficherRegles();
            break;
        case 2:
            jouerSolo(mot_masque, mot_choisi);
            break;
        case 3:
            break;
        }
    }
    
        return 0;
    }
    
    void afficherRegles(){
        printf("\nLE JEU DU PENDU:\n\n");
        printf("Les règles sont simples:\n");
        printf("\t- Un mot sera choisi automatiquement par le programme\n");
        printf("\t- Vous aurez autant de chances que de lettres dans ce mot\n");
        printf("\t- Votre score sera le nombre de chances restantes au moment de deviner le mot\n");
    }
    
    void jouerSolo(char* mot_masque, char mot_choisi[]){
    
        int longueur_mot = 0;           // Variable contenant le nombre de caractère du mot à deviner
        char lettre_choisie = 'a';      // Variable contenant la lettre choisie par l'utilisateur
        int nbChances = 0;              // Le joueur possède autant de chances de jouer qu'il y a de lettres dans le mot
        int victoire = 0;               // Cette variable vaut 0 si l'utilisateur a réussi à deviner le mot. Sinon, elle vaut 1.
    
        // Choix aléatoire du mot à deviner dans le fichier de mot
        choisirMot(mot_choisi);
    
        // Définition du nombre de chances
        nbChances = strlen(mot_choisi);
        printf("\nNombre de chances : %d", nbChances);
        printf("\n\n%s\n\n", mot_choisi);       // Simplement utile pour le test, à supprimer pour l'exec final
    
        // Création du mot masqué qui contiendra le mot avec les lettres devinées
        longueur_mot = strlen(mot_choisi);
        printf("%d\n\n", longueur_mot);
        mot_masque = malloc(longueur_mot*sizeof(char));
    
        // Création du mot qui sera complété au fur et à mesure des lettres devinées correctement.
        creerMotMasque(mot_masque, mot_choisi, longueur_mot);
    
        printf("\n\n%s", mot_masque);
    
        while (nbChances > 0 && victoire == 0){
    
            printf("\nIl vous reste %d chances.\n", nbChances);
    
            lettre_choisie = lireCaractere();
    
            analyseMot(mot_masque, mot_choisi, lettre_choisie, longueur_mot);
    
            if (strcmp(mot_choisi, mot_masque) == 0){
            victoire = 1;
            printf("\nVotre mot est complet, vous remporte la partie !\n\n");
            }
    
            printf("\n\n%s", mot_masque);
    
            nbChances --;
        }
    
        printf("\nNombre de chances : %d", nbChances);
        // On libère la partie de la mémoire contenant le mot à deviner
        free(mot_masque);
    }
    
    int lireChoixMenu(){
        char caractere = 0;
        int caractere_num = 0;
    
    
        printf("\n\nVeuillez saisir votre choix.\n");
        caractere = getchar(); // On lit le premier caractère
        // On lit les autres caractères mémorisés un à un jusqu'au \n (pour les effacer)
        while (getchar() != '\n');
    
        caractere_num = caractere - 48;
    
        if (caractere_num < 1 || caractere_num > 4){
            printf("\nSaisie incorrecte !\n\n");
        }
    
        return caractere_num; // On retourne le premier caractère qu'on a lu
    }
    


    Pendu.h :

    #ifndef PENDU_H_INCLUDED
    #define PENDU_H_INCLUDED
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    #include <ctype.h>
    
    #define TAILLE_MAX 100
    
    // Prototypes des fonctions
    void choisirMot(char mot_choisi[]);
    void creerMotMasque(char* mot_masque, char mot_choisi[], int longueur);
    void analyseMot(char mot_masque[], char mot_choisi[], char lettre, int longueur);
    char lireCaractere();
    
    
    // Variables commune sà plusieur fonctions
    FILE* fichier = NULL;
    
    
    void choisirMot(char mot_choisi[])
    {
        int ligne = 0;
        char chaine [TAILLE_MAX] = "";
    
        // Ouverture du fichier et calcul du nombre de lignes
        fichier = fopen("mots.txt", "r");
        if (fichier != NULL)
        {
            while(fgets(chaine, TAILLE_MAX, fichier) != NULL){
                ligne ++;
            }
            printf("\nIl y a %d lignes.\n\n", ligne);
            fclose(fichier);
        }
        else
        {
            // On affiche un message d'erreur
            printf("Impossible d'ouvrir le fichier de mots au moment du calcul de ligne.\n");
        }
    
        // Choix aléatoire de la ligne (donc du mot) pour le pendu
        int choix = rand()%(ligne);
        printf("La ligne choisie pour le mot est : %d.\n\n", choix);
    
        // Ouverture du fichier et récupération du mot choisi
        fichier = fopen("mots.txt", "r");
        if (fichier != NULL)
        {
            for (int i = 0; i<choix; i++) {
                fgets(mot_choisi, TAILLE_MAX, fichier);
            }
            fscanf(fichier, "%s", mot_choisi);
            //fclose(fichier);
        }
        else
        {
            // On affiche un message d'erreur
            printf("Impossible d'ouvrir le fichier de mots au moment de la récupération du mot choisi.\n");
        }
    
    }
    
    
    void creerMotMasque(char* mot_masque, char mot_choisi[], int longueur)
    {
    
        if (mot_masque == NULL) // On vérifie si l'allocation a marché ou non
        {
            printf("\nL'allocation n'a pas fonctionné !\n\n");
            exit(0); // On arrête tout
        }
        for (int i=0; i<longueur; i++){
            mot_masque[i] = '*';
        }
    }
    
    // Ici, mot_masque est un tableau car il l'est devenu suite à l'appel de creerMotMasque
    void analyseMot(char mot_masque[], char mot_choisi[], char lettre, int longueur)
    {
        char *resultat = NULL;
    
        // On construit tout d'abord le nouveau mot avec la lettre saisie par l'utilisateur
        for (int i=0; i<longueur; i++){
            if (toupper(lettre) == mot_choisi[i]){
                mot_masque[i] = toupper(lettre);
            }
        }
    
        // Puis, on regarde si cette lettre appartenait au mot choisi et on averti l'utilisateur du résultat
        int ascii_lettre = lettre;
        resultat = strchr(mot_choisi, ascii_lettre);
        if (resultat != NULL) // Si on a trouvé quelque chose
        {
            printf("\nBravo vous avez trouvr le caractere : %c.\n\n", lettre);
        }
        else{
            printf("\nDesole, cette lettre n'appartient pas au mot a deviner.\n\n");
        }
    }
    
    char lireCaractere()
    {
        char caractere = 0;
    
        do {
        printf("\n\nVeuillez saisir un caractere alphabetique.\n");
        caractere = getchar(); // On lit le premier caractère
        caractere = toupper(caractere); // On met la lettre en majuscule si elle ne l'est pas déjà
        // On lit les autres caractères mémorisés un à un jusqu'au \n (pour les effacer)
        while (getchar() != '\n');
        }while (isalpha(caractere)==0);
    
        return caractere; // On retourne le premier caractère qu'on a lu
    }
    #endif // PENDU_H_INCLUDED
    


    Florian


    • Partager sur Facebook
    • Partager sur Twitter
      3 décembre 2019 à 13:31:37

      Salut,

      Une chaine de caractère doit se terminer par '\0'. 

      Quand un printf affiche une chaine de caractère, il continuer d'afficher la mémoire jusqu'à trouver le \0 ... 

      Ici tu as des caractères en plus : donc tu as oublié le \0, et donc le printf continue et t'affiche.... ce qu'il y a en mémoire après ta chaine. ça peut être n'importe quoi. Et ça peut aussi planter si tu sors du segment mémoire.

      Quand tu vires une ligne de ton programme qui n'a rien à voir, ça décale tout, donc ce qu'il y a dans la mémoire après ta chaine peut être différent. ça peut commencer par un \0 ... ou pas.

      C'est un peu comme si tu avais une feuille de papier que tu lis, et si tu arrives au bout, ben tu lis ce qu'il y a sur la table... et si ton programme a changé, ta feuille de papier n'est pas au même endroit sur la table et donc tu liras autre chose.

      Regarde "mot_masque", tu le malloc, puis ensuite tu la passes à créermotmasque qui remplit les lettres, mais pas le \0.

      • Partager sur Facebook
      • Partager sur Twitter

      Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

        3 décembre 2019 à 15:50:13

        c'est pour ça que c'est utile de voir les monoïdes pour comprendre que \0 agit comme élément neutre dans la concaténation des mots (chaînes de caractères)

        mais ça tombe bien ça sert aussi à indiquer que la chaîne est terminée

        il y a un sujet wiki sur le langage formel si ça intéresse(taper langage formel)  

        • Partager sur Facebook
        • Partager sur Twitter

        Problème d'affichage de tableau suite à fclose()

        × 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