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 :

Stockage du contenu d'un fichier dans un tableau


Sujet :

C

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2018
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2018
    Messages : 6
    Par défaut Stockage du contenu d'un fichier dans un tableau
    Bonjour à tous et à toutes,

    Je rencontre un problème dans la subsistance de valeurs dans mon tableau.
    Voici le code:
    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
    19
    20
    21
    22
    void tabFichier( char *Fichier, char ***chaine, int nbColonne, int nbLigne ){
     
        FILE *f;
        f = fopen(Fichier,"rt");
        char *c = malloc(1000 * sizeof(char));
        char **T = malloc(nbColonne * sizeof(char *));
        if(f !=NULL){
            int Col = nbColonne;
            int Lig = nbLigne;
            fscanf(f,"%s",c);
            fscanf(f,"%s",c);
            for(int i=0; i<Lig; i++){
                fscanf(f,"%s",c);
                decoupeChaine(c,T,Col);
                chaine[i] = T;
            }
        }
        else {
            perror("ErroR\n");
        }
        fclose(f);
    }
    Ma fonction qui stocke dans mon tableau le contenu de mon fichier. Pour vous donnez plus d'informations mon fichier est un fichier.txt dans lequel est stocké des chaines de caractères comme cela:
    A;B;C;D
    test;TEST;O;P

    Code de la fonction decoupeChaine:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void decoupeChaine(char *entree, char **sortie, int nbCol){
        sortie[0] = strtok (entree,";");
        for (int j = 1; j < nbCol; j++)
        {
            sortie[j] = strtok (NULL, ";");
        }
    }
    Quand je printf dans la boucle for le contenu de mon tableau est bon et correspond bien au contenu de mon fichier dans la forme souhaité. Mon problème c'est qu'une fois sortis de cette boucle quand je printf le contenu du tableau j'obtiens pour les premières ligne de mon tableau n'importe quoi seul ma dernière ligne est juste. J'ai déjà essayé de solutionner le problème sans succès avant toutes mes lignes étaient identiques à la dernière. J'ai lu beaucoup d'informations sur internet mais soit je en comprenais pas soit ça ne s'appliquer pas dans mon cas.
    Si vous pouviez m'aider s'il vous plaît
    Je vous en remercie par avance !
    Cordialement,
    Seltux

  2. #2
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Bonjour,

    La fonction découpe chaîne est sensée retourner un tableau de chaînes.
    Mais ce qui est mis dans le tableau sont des pointeurs dans la chaîne reçue en paramètre qui disparaît à chaque itération et donc la dernière utilisation doit être a peu près correcte (sauf que strtok ne fait que modifier et remodifier l'original), et les autres sont toutes des "undefined behavior".

  3. #3
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Tu stockes dans T des adresses de sous-chaînes appartenant au buffer pointé par c qui ne correspondent à rien une fois que tu as écrasé le contenu du buffer en lisant la ligne suivante. Si tu veux procéder de la sorte (avec une table de références), il te faut y stocker l'intégralité des données.

    L'allocation dynamique de la zone mémoire pointée par c ne se justifie a priori pas.

    Personnellement j'emploierais fgets quitte à parcourir le flux ligne par ligne.

    Ton code n'est pas propre. Utilise des enregistrements (struct), nomme correctement tes variables et tu y verras sans doute déjà plus clair.

  4. #4
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2018
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2018
    Messages : 6
    Par défaut
    Bonjour,

    Tout d'abord merci pour vos réponses, j'ai essayé beaucoup de choses pour solutionner le problème j'ai notamment passé par adresse les variables lors de l'appel de la fonction et toujours le même problème.

    @dalfab
    Pour ce qui est de strok je suis d'accord mais en ayant essayant seul la fonction découpe chaîne sur une chaîne simple le résultat est correcte donc j'en ai déduis que le problème était dans la fonction tabFichier au niveau des pointeurs.
    Quel alternative existe t'il pour remplacer strok, j'avais choisis strok car c'était rapide et simple d'utilisation pour ce que je voulais faire. Car l'utilisation de strcpy etc est lourde pour le programme que je réalise.

    @Matt_Houston
    Oui mon code est loin d'être propre je m'en excuse, je vois ce que vous voulez dire sur ce qui est à l'origine du problème. Quand vous me dites d'utiliser un tableau de référence je pense que vous faite allusion à cela : char tableau[]. Cela reviens à ce que je souhaite faire, c'est à dire stocker dans un tableau les données de mon fichier. Je m'excuse mais je ne comprends pas bien pourquoi utiliser un tableau de référence ni comment procéder.

    Pour ce qui est de l'utilisation du fgets, remplacer mes fscanf ne solutionne pas le problème j'avais déjà essayé mais sans obtenir un résultat probant.

    Je ne comprends par très bien ce que vous me proposez, pourriez-vous peut-être détailler le processus sans me donner la solution.

    Je vous en remercie par avance.

    Cordialement,
    Seltux

  5. #5
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Chaque chaine trouvée doit être placée dans un buffer qui a été dynamiquement réservé, donc finalement on peut tout à fait utiliser strtok.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void decoupeChaine(char *entree, char **sortie, int nbCol){
        const char  *str =  strtok( entree , ";" );
        sortie[0] = malloc( strlen(str) + 1 );        // réserver de la place, et mémoriser le pointeur
        strcpy( sortie[0] , str );                    // mettre dans la zone réservée la chaîne
        for (int j = 1; j < nbCol; j++)
        {
            str = strtok( NULL , ";" );
            sortie[j] = malloc( strlen(str) + 1 );    // réserver de la place, et mémoriser le pointeur
            strcpy( sortie[j] , str );                // mettre dans la zone réservée la chaîne
        }
    }

  6. #6
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2018
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2018
    Messages : 6
    Par défaut
    Bonsoir,

    @dalfab
    Merci pour votre réponse, je comprends mieux ce qu'il faut faire et visualise mieux la chose, je vais essayer cela dès que je rentrerai chez moi et je vous tiendrai au courant du résultat.
    Merci encore et bonne soirée.

    Cordialement,
    Seltux

  7. #7
    Membre très actif
    Avatar de sambia39
    Homme Profil pro
    No Comment
    Inscrit en
    Mai 2010
    Messages
    548
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : No Comment
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Mai 2010
    Messages : 548
    Par défaut
    Bonsoir,
    Citation Envoyé par dalfab Voir le message
    Chaque chaine trouvée doit être placée dans un buffer qui a été dynamiquement réservé, donc finalement on peut tout à fait utiliser strtok.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    void decoupeChaine(char *entree, char **sortie, int nbCol){
        const char  *str =  strtok( entree , ";" );
        sortie[0] = malloc( strlen(str) + 1 );        // réserver de la place, et mémoriser le pointeur
        strcpy( sortie[0] , str );                    // mettre dans la zone réservée la chaîne
        for (int j = 1; j < nbCol; j++)
        {
            str = strtok( NULL , ";" );
            sortie[j] = malloc( strlen(str) + 1 );    // réserver de la place, et mémoriser le pointeur
            strcpy( sortie[j] , str );                // mettre dans la zone réservée la chaîne
        }
    }
    Oui, mais il est tout de même préférable d'avoir un garde-fou qui accompagne la fonction. Certes, on peut garantir en amont que le nécessaire soit fait de façon à passer à la fonction les éléments adéquat, il n'en reste pas moins que la fonction est dépendante des données qu'on lui transmet.

    Ligne 3 : la fonction malloc(..) admet comme paramètre la fonction strlen(...) qui renvoie le nombre de caractères. Sauf que strlen(...) ne peut pas traiter une variable pointeur qui admet une adresse NULL et de ce fait, dès lors que strtok(...) renverra NULL ( et donc str = NULL), la fonction strlen(...), crashera.


    Ligne 5 : à ce que j'ai compris, le traitement des autres sous chaînes dépend de la limite fixée par la variable entière nbCol. Or ici, nbCol n'est pas véritablement le nombre de colonnes, mais bien le nombre de groupes de mots et si l'on se trompe sur le nombre max de groupes de mots, ne serait ce que d'une incrémentation, on a le même scénario que la ligne 3 (qui se produira à la ligne 8) à savoir un crash du programme.

    Certes, c'est à celui qui développe l'application de prendre les mesures nécessaires, mais il n'en reste pas moins qu'il faut faire attention à ce genre de choses et dans une première approche, j'écrirais la fonction comme ci-dessous de façon à ce qu'elle admette uniquement des données valides et qu'elle puisse faire le découpage des chaînes, non pas en fonction des colonnes, mais en fonction des données et donc elle s'arrêtera : soit parce qu'elle a fini de traiter les données, soit parce qu'il y a une erreur d'allocation mémoire ou autre.

    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
    19
    20
    21
    22
    23
    24
    25
    26
     
    #define _POSIX_C_SOURCE 200809L
     
    bool decoupeChaine_other(char *entree, char **sortie ){
     
        size_t i = 0;
        char  *str =  strtok( entree , ";" );
     
        if( NULL == str )
            /*    --snip-- */
            return (false);
        else if( NULL == sortie )
            /*    --snip-- */
            return (false);
        if( NULL == ( sortie[i] = strdup(str) ) )
            /*    --snip-- */
            return (false);
     
        while( NULL != (str = strtok(NULL, ";" ) ) ){
            if( NULL == (sortie[++i] = strdup(str)) )
                /*    --snip-- */
                return (false);         
        }
     
        return (true);
    }
    À bientôt.

  8. #8
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2018
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2018
    Messages : 6
    Par défaut
    Bonsoir,

    @sambia39
    Merci pour votre intervention qui est très intéressante, je n'avais en effet pas pensé la fonction de cette manière qui est bien plus logique et sûr que ma version.
    J'étais bien conscient que les erreurs que vous énoncez sont effectivement des erreurs possibles et qui ferai crash le programme mais je n'étais pas dans l'optique de les empêcher puisque ce programme n'a pas pour but de devenir publique.
    Je sais bien que cette façon de penser est idiote et votre intervention ma fait réfléchir. Il est bien mieux que dès maintenant je code de façon "propre". Je vous remercie pour cela.

    Merci par avance.
    Cordialement,
    Seltux

  9. #9
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Citation Envoyé par Seltux Voir le message
    Je ne comprends par très bien ce que vous me proposez, pourriez-vous peut-être détailler le processus sans me donner la solution.
    Bien entendu. Il faut avant tout que tu comprennes bien ce qu'il se passe.

    Citation Envoyé par Seltux Voir le message
    Quand vous me dites d'utiliser un tableau de référence je pense que vous faite allusion à cela : char tableau[]. Cela reviens à ce que je souhaite faire, c'est à dire stocker dans un tableau les données de mon fichier. Je m'excuse mais je ne comprends pas bien pourquoi utiliser un tableau de référence ni comment procéder.
    Note que j'ai pris garde d'employer le terme table de références car le mot tableau pourrait prêter à confusion étant donnée sa signification particulière dans le contexte d'une implémentation en langage C. Tu en utilises déjà une : il s'agit de la zone mémoire pointée par T au sein de laquelle tu stockes les adresses (les références) des différentes chaînes considérées.

    Le défaut principal de ton programme est la non-validité de la plupart de ces adresses. En fait toutes sauf celles de la dernière ligne, puisque tu mets chaque ligne du flux dans le même buffer et que tu écrases donc à la ligne n + 1 les données de la ligne n.

    Donc : si tu veux procéder de la sorte, à savoir organiser les données sous la forme d'une table de références, alors il te faut bien deux buffers comme tu l'as fait :

    1. celui qui contient les données proprement dites, la séquence de caractères complète ;
    2. celui qui contient les adresses du premier caractère de toutes les chaînes présentes au sein de cette séquence : la fameuse table de références.

    Si le contenu du premier buffer est modifié alors que tu as déjà commencé à renseigner les références, celles-ci sont invalidées et inutilisables. C'est ce qu'il se passe actuellement.


    Il y a d'innombrables manières d'y remédier, plus ou moins simples. En voici deux :

    • lire l'intégralité du flux d'une traite dans le buffer de données (dans une boucle avec fread, par exemple), fermer le flux, puis découper toutes les chaînes ;
    • lire une ligne à la fin du buffer de données (sans écraser les précédentes !) en incrémentant le pointeur de destination, réaliser la découpe de chaînes pour cette ligne, puis passer à la suivante.

    Ton implémentation ressemble à la seconde méthode mais elle est erronée.


    Citation Envoyé par Seltux Voir le message
    Pour ce qui est de l'utilisation du fgets, remplacer mes fscanf ne solutionne pas le problème j'avais déjà essayé mais sans obtenir un résultat probant.
    Ok, ce n'est pas important pour l'instant puisque ce n'est pas ce qui empêche le fonctionnement correct de ton programme. Laissons cela de côté pour le moment.

  10. #10
    Nouveau membre du Club
    Homme Profil pro
    Étudiant
    Inscrit en
    Décembre 2018
    Messages
    6
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2018
    Messages : 6
    Par défaut
    Bonjour,

    @dalfab
    J'ai essayé plusieurs choses (certaines qui n'avaient pas de sens aussi ^^) en rapport avec votre explication. J'ai réussis à solutionner le problème sans pour autant utiliser un buffer dynamique dans decoupeChaine. L'utilisation d'un buffer dynamique dans decoupeChaine permettrai d'optimiser mon code alors ou bien son utilisation était un moyen de solutionner le problème autrement ?

    @Matt_Houston
    Tout d'abord un grand merci pour votre explication qui m'a permis de mieux comprendre et je vois maintenant le problème. J'ai réussis à résoudre le problème, le contenu de mon tableau est maintenant juste et correspond bien à ce qu'il y a dans mon fichier.

    Voici le code fonctionnel (sans structure, je vais directement en créer pour rendre le code plus propre):

    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
    19
    20
    21
    22
    23
    24
    void tabFichier( char *Fichier, char ***Tab, int nbColonne, int nbLigne ){
     
        FILE *f;
        f = fopen(Fichier,"rt");
        char *chaine = malloc(1000 * sizeof(char));
        char **T = malloc(nbColonne * sizeof(char *));
        if(f !=NULL){
            int Col = nbColonne;
            int Lig = nbLigne;
            fscanf(f,"%s",chaine);
            fscanf(f,"%s",chaine);
            for(int i=0; i<Lig; i++){
                fscanf(f,"%s",chaine);
                decoupeChaine(chaine,T,Col);
                for (int j = 0; j < Col; ++j) {
                    Tab[i][j] = T[j];
                }
            }
        }
        else {
            perror("ErroR\n");
        }
        fclose(f);
    }
    Est-ce que la correction correspond bien à ce que vous m'expliquiez ou bien ai-je simplement contourné le problème? Je pense que ce que j'ai fais correspond à ce que vous m'aviez expliqué mais j'aimerai une confirmation pour en être sûr.

    Je vous remercie encore une fois pour votre aide !
    Merci par avance.

    Cordialement,
    Seltux

  11. #11
    Membre Expert
    Inscrit en
    Mars 2005
    Messages
    1 431
    Détails du profil
    Informations forums :
    Inscription : Mars 2005
    Messages : 1 431
    Par défaut
    Tu as contourné le problème puisque le contenu de la fonction tabFichier n'a pratiquement pas changé. Accessoirement, la zone mémoire pointée par chaine n'est toujours pas libérée en fin de traitement.

    Si tu obtiens bien la sortie attendue, c'est parce que tu emploies très probablement la version de decoupeChaine de dalfab, qui recopie les sous-chaînes dans de petits buffers dédiés. Tu n'écrases donc plus les données car tu les dupliques. Il n'y a rien de mal à cela, mais il s'agit d'une méthode bien différente : on ne stocke plus de références (au sens général) mais directement les données brutes.

    Afin que tu visualises un peu mieux ce dont on cause, voici à quoi ressemble l'organisation des données en mémoire en recopiant chaque chaîne dans un buffer alloué indépendamment (le buffer du haut représente notre index / table de références) :



    À comparer avec la méthode que j'avais évoqué :



    Nulle méthode n'est meilleure que l'autre dans l'absolu et comme souvent il faut bien étudier le cas d'utilisation. La première est bien plus flexible si tu as besoin de modifier la longueur des chaînes de caractères ultérieurement, alors que pour des données statiques la complexité sera moindre et les performances sans doute bien meilleures avec la seconde méthode.

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

Discussions similaires

  1. ajouter le contenu d'un fichier dans un tableau
    Par simplyc dans le forum Débuter
    Réponses: 13
    Dernier message: 29/09/2011, 12h38
  2. Stockage de caractères d'un fichier dans un tableau
    Par harry le ravi dans le forum VBScript
    Réponses: 1
    Dernier message: 21/06/2009, 17h26
  3. Réponses: 8
    Dernier message: 03/09/2007, 21h11
  4. injecter le contenu d'un fichier dans un tableau
    Par rogerio dans le forum C++
    Réponses: 5
    Dernier message: 03/05/2006, 16h37
  5. Réponses: 4
    Dernier message: 26/01/2006, 14h37

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