Forum Programmation.c Question pour C gourou !

Posté par  .
Étiquettes : aucune
0
31
déc.
2006
Bonjour,

J'ai un problème un peu compliqué a résoudre, ca fait quelques semaines que je suis dessus.

Voilà, j'ai dans les eaux de 1000 fichiers .csv dans un repertoire qu'il faut que je charge en mémoire pour pouvoir faire des traitements dessus.

Les données sont conservees dans chaque fichier sous la forme
char[10] char[10] int float float float float
char[10] char[10] int float float float float
char[10] char[10] int float float float float, etc.

La solution que j'ai trouvé était de créer une structure pour pouvoir faire des traitements dessus. Mais j'ai beaucoup de problèmes pour arriver à effectuer des traitements dessus depuis une fonction. Voici un bout de code. Il ne résoud que très partiellement le problème :




#include <stdio.h>

struct entree {

char date[10], time[10];
int v;
float a,b,c,d;

}

main () {

struct entree data[1000] ; # 1000 lignes pour le fichier

int i=0;
FILE *fp;
fp = fopen("1.csv", "r");

while(!(feof(fp))) {

fscanf(fp, "%s %s %d %f %f %f %f", &date, &time, &v, &a, &b, &c, &d);

strcpy(data[i].date, date);
strcpy(data[i].time, time);
data[i].v = v);
data[i].a = a);
data[i].b = b);

i++;
}

}



J'ai 3 problèmes :
- j'arrive pas à creer une fonction distincte de main, qui mette les données dans la structure
- je sais pas comment gérer plusieurs fichiers
- je sais pas comment gérer un fichier de x lignes (x étant susceptible d'être supérieur à 200.000).

Je sais, ca fait beaucoup de mémoire vive qu'il va falloir acheter, mais c'est pas un problème ;-)
  • # base de la programmation

    Posté par  . Évalué à 1.


    #include <stdio.h>

    struct entree {
    char date[10], time[10];
    int v;
    float a,b,c,d;
    }

    void ma_fonction(ma_variable) {

    struct entree data[1000] ; # 1000 lignes pour le fichier

    int i=0;
    FILE *fp;
    fp = fopen("ma_variable", "r");

    while(!(feof(fp))) {

    fscanf(fp, "%s %s %d %f %f %f %f", &date, &time, &v, &a, &b, &c, &d);

    strcpy(data[i].date, date);
    strcpy(data[i].time, time);
    data[i].v = v);
    data[i].a = a);
    data[i].b = b);

    i++;
    }

    return data

    }

    void main(void) {

    ma_fonction("1.csv");
    }



    ca fait longtemps que j'ai pas codé en C, il pourrait donc y avoir des erreurs...

    apres pour gerer plusieurs fichiers, ca va dependre si tu veux les mettre dans ta structure separement ou dans une seule (concatenation)

    et pour les fichiers de taille T, il faut surement recupere d'abord la longueur du fichier et la passer en parametre à ma_fonction(mon_fichier, sa_taille), ou tester dans ma_fonction la taille du fichier.
    • [^] # Re: base de la programmation

      Posté par  . Évalué à 1.

      L'idéal serait de tout mettre dans une seule grosse structure, sinon je vois pas comment faire facilement des traitements sur toutes les données.

      Dans ta solution par contre, est-ce qu'on peut accéder à la structure depuis main ?

      Merci !
      • [^] # Re: base de la programmation

        Posté par  . Évalué à 1.

        dans l'idée je penses que oui mais il faut peut-etre creer data[] en dehors de la fonction, et la fonction se contentant de la remplir.

        et pour concatenation,
        il faudrait simplement faire un data[X+Y+Z]
        X, Y et Z etant les tailles des trois fichiers que tu veux traiter.

        une solution avant de coder, consiste à decrire en francais ce que tu veux faire (algorythmie),
        et ensuite transposer dans le langage en question.
    • [^] # Re: base de la programmation

      Posté par  . Évalué à 2.

      En reprenant le code precedant, et en modifiant deux trois petites choses (notamment un typedef en plus car je suis plus a l'aise avec) et des corrections de syntaxe:
      
      #include <stdio.h>
      
      typedef struct {
         char date[10], time[10];
         int v;
         float a,b,c,d;
      }ENTREE;
      
      /*on retourne data, donc il faut l'indiquer*/
      ENTREE* ma_fonction(char *ma_variable) {
      
         ENTREE data[1000] ; # 1000 lignes pour le fichier
         /* Faut les declarer si on veut les utiliser dans le fscanf)*/
         char date[10];
         char time[10]
         int v;
         float a,b,c,d;
         /**********/
      
         int i=0;
         FILE *fp;
         fp = fopen("ma_variable", "r");
      
         while(!(feof(fp))) {
            fscanf(fp, "%s %s %d %f %f %f %f", &date, &time, &v, &a, &b, &c,    &d);
      
            strcpy(data[i].date, date);
            strcpy(data[i].time, time);
            data[i].v = v);
            data[i].a = a);
            data[i].b = b);
            i++;
         }
         return data; /*important le ; ^ ^ */
      }
      
      
      int main(void) {
         ENTREE* ma_structure=NULL;
         ma_strucutre=ma_fonction("1.csv");
         return EXIT_SUCCES;
      }
      
      Mais a titre personnel, j'utiliserai plutot un tableau dynamique (histoire d'utiliser juste ce qu'il faut en memoire ^^ ). En fait, une question me taraude : es tu familiarisé avec la notion de pointeurs, de pointeurs sur void, de tableau dynamique ? Car sinon, tu va vite etre bloqué sur plein de truc qui sont somme toute pas compliqué a faire lorsqu'on a bien assimilé les pointeurs.
      • [^] # Aaaaie, mes yeux !

        Posté par  . Évalué à 5.

        C'est quoi ce return d'un pointeur dans la stack ? Oui oui, le "ENTREE data[1000]" est dans la stack ; donc ton return a la fin de la fonction pointe au (tres) mauvais endroit. Et ca, c'est sans compter le fait que vu que le tableau est dans la stack, ca risque fortement d'etre reecrit au prochain appel d'une fonction. Bref, je n'ai regarde que ca, mais ca me semble tres tres mal, il faut plutot faire un malloc().
    • [^] # Re: base de la programmation

      Posté par  . Évalué à 2.

      Bon, j'avais pas vu que c'etait de ca post que ca venait, donc voir ce que j'ai repondu a lunique a propos du return d'un pointeur dans la stack. Pas beau du tout.

      Sur ce, apres l'apero, un verre de vin et pas loin d'une demi bouteille de champagne, au lit. L'air de rien, je bosse demain... Enfin dans 8 heures. En principe.

      Et pour zedis :
      -passer le tableau en parametre en tant que pointeur a la fonction de remplissage, ou que la fonction en question alloue de la memoire a coup de malloc().
      -si tu travailles avec malloc, realloc() est ton ami.
      -a part la memoire qui risque d'exploser, il y a plein de methodes possible plus ou (surtout) moins efficaces.

      En fait, ca depend vraiment de ce que tu veux faire comme traitement sur tes donnees ; mais *tout* charger en memoire me semble un peu lourd...

      Bon, au dodo !
      • [^] # Re: base de la programmation

        Posté par  . Évalué à 1.

        Merci pour tout ces conseils, mais toujours impossible de résoudre le problème...

        La solution proposée par imalip, j'ai essayé aussi, mais sans résultat.
        L'idée était de passer en argument à la fonction qui récupère les données un pointeur sur la structure de sorte que la fonction la remplisse. Etant relativement débutant en C, je me suis cassé la tete (notamment ;-) a essayer de faire en sorte que ça fonctionne ; impossible. Voici le code...

        Si quelqu'un peut me sortir de ce mauvais pas je lui paye une biere !



        #include <stdio.h>


        struct enreg {

        char date[20], heure[20];
        int v;
        float a,b,c,d;
        };


        /*
        * Lit le fichier et enregistre les elements dans une structure data accessible globalement
        *
        */

        void fct(struct enreg *s) {

        char chaine[256], date[20], heure[20];
        int v;
        float a,b,c,d;
        int i=0;

        FILE *fp = fopen("1.txt", "r");

        while(fgets(chaine, sizeof(chaine), fp)) {

        sscanf(chaine, "%s %s %d %f %f", date, heure, &v, &a, &b );
        (s+i)->v = v;
        printf("%d \n", i);
        i++;

        }

        fclose(fp);

        }


        /*
        * Main
        *
        */

        main() {

        struct enreg x[1000000]; // cree la structure x

        // ? ? ? void fct(struct enreg *y);

        fct(&x[1000000]);

        }
        • [^] # Re: base de la programmation

          Posté par  . Évalué à 2.

          Effectivement, c'est débutant de chez débutant, la...

          Remplacer "(s+i)->v = v;" par "s[i].v = v;"

          s+i ajoute l'entier i a l'adresse s, donc pas du tout ce que tu veux faire. Ca m'étonne presque que le compilo ne te jette pas...

          et "fct(&x[1000000]);" par "fct(x);"

          &x[1000000] est l'adresse du 1000001eme élément de ton tableau qui en fait 1000000, donc tu tappes en-dehors. il faut que tu passes l'adresse du premier, donc &x[0] (qui est equivalent a x).
          • [^] # Re: base de la programmation

            Posté par  . Évalué à 1.

            Ca marche déjà un peu mieux !

            Il me reste 2 erreurs que j'essaye de résoudre :
            - sous Linux, impossible de compiler en apportant les modifications conseillées, gcc me retourne "tow or more data types in declaration of fct"... l'erreur étant à la ligne de la déclaration de la fonction fct

            - Je passe sous visual C++ et là ca compile, mais ca plante lorsque le tableau dépasse 10000 lignes... j'augmentes logiquement le nombre de lignes 30000 et ca plante sans raison valable.

            Grrrr..

            Pour le "(s+i)->v = v " j'avais lu quelque part qu'il fallait utiliser cette notation pour travailler sur des pointeurs de trableaux de structures. En utilisant s[x].heure je croyais qu'on travaillait sur les valeurs directement et non sur l'adresse de la variable.

            Il me reste encore du taf pour maitriser les pointeurs (ne me jettez pas la pierre j'ai lu pas mal de bouquins sur le passage de pointeurs de tablaux de structures dans une fonction, et c'est dur de trouver des infos a ce sujet).
            • [^] # Re: base de la programmation

              Posté par  . Évalué à 2.

              - Je passe sous visual C++ et là ca compile, mais ca plante lorsque le tableau dépasse 10000 lignes... j'augmentes logiquement le nombre de lignes 30000 et ca plante sans raison valable.

              Comme je l'ai dit plus bas, la raison valable est que 30000 lignes, ça fait environ 1,5 Mo (en supposant une cinquantaine d'octets par structure), et que tu alloues tes scructures sur la pile. Une implémentation est libre de refuser un tel code (de tête, la limite minimale imposée par la norme doit être autour de 65 Ko, à moins que je mélange avec autre chose). Passe par malloc quand tu as besoin d'autant de mémoire.
          • [^] # Re: base de la programmation

            Posté par  . Évalué à 4.

            s+i ajoute l'entier i a l'adresse s


            Euh non, si je ne m'abuse c'est de l'arithmétique des pointeurs. En particulier quand tu ajoutes 1 à un pointeur ça passe à l'adresse suivante (donnée par la taille du type en mémoire)

            @zedis : il te manque plein de base pour coder en C on dirait. C'est un langage relativement bas niveau avec pleins de pièges niveau gestion mémoire, pointeur tout ça. Tu devrais lire une bon tuto un peu complet ou mieux un bon bouquin de C avant d'essayer de coder, sinon tu vas te jeter dans tous les pitèges un par un ... Ou passer à un langage un peu plus haut niveau qui gère tout seul la mémoire.
            • [^] # Re: base de la programmation

              Posté par  . Évalué à 3.

              Pour compléter, ta question n'est pas vraiment une question pour gourou, puisqu'en réalité elle concerne le minimum vital pour écrire un programme en C un peu plus complexe qu'un hello world et qui marche correctement.
        • [^] # Re: base de la programmation

          Posté par  . Évalué à 2.

          #include <stdio.h>
          
          struct enreg { /* ... */ };
          
          void fct(struct enreg *s) {
          char chaine[256], date[20], heure[20];
          int v;
          float a,b,c,d;
          int i=0;
          
          FILE *fp = fopen("1.txt", "r");
          N'oublie pas de vérifier si fopen a bien pu ouvrir le fichier (if (fp == NULL) { /* erreur */ }).
          while(fgets(chaine, sizeof(chaine), fp)) {
          Faire un while sur fgets est mieux que sur feof, mais ce n'est pas encore ça. Si une ligne du fichier fait plus de 254 caractères, tu auras des surprises... (voir http://linuxfr.org/comments/784745,1.html où j'en ai déjà parlé)
          sscanf(chaine, "%s %s %d %f %f", date, heure, &v, &a, &b );
          Il faut toujours tester les valeurs de retour des fonctions *scanf. Elles retournent le nombre de conversions effectuées avec succès. dans ton cas, si elle renvoie un nombre différent de 5, alors il y a une erreur. Par ailleurs, si une des deux premières chaînes fait plus de 19 caractères, tu obtiens un débordement de tampon, et donc un comportement indéfini. Vérifie dans la doc, mais il me semble que le bon format est %[20]s.
          (s+i)->v = v;
          C'est correct mais, pour des raisons de lisibilité, je préfère la notation s[i].v.
          main() {
          Non. int main(void). gcc autorise un type absent pour la fonction main, car il se base sur le C90, mais il faut préciser le type en mode C99.
          struct enreg x[1000000]; // cree la structure x
          Ouch. 1000000 de fois environ 50 octets (en suposant une implémentation classique), ça fait 50 Mo sur la pile. Un peu gros. Ca compilera très probablement (peut-être avec un warning si le compilo n'est pas trop mauvais), mais ce n'est pas garanti que ça marche à l'exécution. Utilise plutôt une allocation dynamique avec malloc.
          fct(&x[1000000]);
          Comme déjà dit, fct(x);.
          • [^] # Re: base de la programmation

            Posté par  . Évalué à 1.

            Mon avis:

            - fais le traitement sur une ligne avec une fonction qui lit une ligne et remplit la structure adéquate
            - apprend à gérer les listes chainées, ou utilise une lib qui les implémente (glib, me semble-t-il, a tout ce qu'il faut pour le faire).

            - ensuite, une fois que tu as la fonction qui te remplit une structure avec une ligne, tu passe a la lecture de tout le fichier.
            • [^] # Re: base de la programmation

              Posté par  . Évalué à 1.

              Merci à tous ceux qui ont répondu, c'est quand meme cool de voir qu'on peut encore demander de l'aide !!!

              Bon, j'ai résolu en partie mon problème : pour la mémoire, l'allocation d'un tableau de 100000 lignes est effectivement beaucoup trop gros. En passant par malloc, ca marche nettement mieux.

              Par contre pour la structure personne n'a vraiment répondu à la question : comment on fait pour passer un pointeur sur une structure à une fonction, de sorte qu'on puisse l'utiliser au sein de la fonction (comme ça idéalement je pourrais remplir ma structure depuis une fonction ce qui simplifirai pas mal mon code...).

              Sinon quelqu'un a une idée de comment on crée un tableau dans lequel je pourrai mettre mes structures (rappel : pour l'instant une structure = 1 fichier, et il me reste 998 fichiers à lire et a charger en mémoire)...

              Merci !
              Zed
          • [^] # Re: base de la programmation

            Posté par  . Évalué à 1.

            Merci pour ton post, c'est super constructif, j'ai appris plein de trucs !

            Dans mes fichiers, les données sont toujours exactement les memes. Donc pour fgets ca ne doit pas poser de problemes pratiques normalement, idem pour sscanf (ceci dit merci d'avoir soulevé le probleme potentiel).

            Merci également d'avoir d'expliqué pourquoi
            struct enreg x[1000000]
            posait problème ; j'ai trouvé la solution hier soir, mais j'ai pas vraiment compris pourquoi ; ton explication éclaire tout maintenant.

            fct(&x[100000]) est effectivement faux, mais si je fais fct(x), j'envoie une copie de la structure et non un pointeur non ?

            Il y a quelque chose qui m'échappe là (sur comment faire pour envoyer un pointeur dans une fonction et travailler avec).

            Merci !
            Zed.
            • [^] # Re: base de la programmation

              Posté par  . Évalué à 2.

              Dans mes fichiers, les données sont toujours exactement les memes. Donc pour fgets ca ne doit pas poser de problemes pratiques normalement, idem pour sscanf (ceci dit merci d'avoir soulevé le probleme potentiel).
              Ca ne pose pas de problèmes, jusqu'à ce que ça en pose... Loi de Murhpy, tu connais ?
              fct(&x[100000]) est effectivement faux, mais si je fais fct(x), j'envoie une copie de la structure et non un pointeur non ?
              Avec :
              #define N 1000
              struct T {
                  int n;
              }
              
              void struct g(struct T *p) { /* .... */ }
              
              void f(void)
              {
                  struct T mavar[N];
                  g(mavar); /* correct */
                  g(&mavar[0]); /* correct, équivalent à la première ligne */
                  g(&mavar[N/2]): /* correct aussi */
              }
              mavar est un tableau de N structures. En C, lorsqu'on passe un tableau en paramètre à une fonction, il est automatiquement converti en un pointeur sur le type de base du tableau, pointant sur le premier élément. Donc, dans le premier appel à g, mavar est converti en struct T*, de valeur &mavar[0]. Les deux premiers appels à g sont donc équivalents. Le troisième est là pour te montrer que tu peux passer un pointeur vers n'importe quel élément du tableau (il faut juste faire attention à bien rester entre les bornes 0 -> N-1). Dans cet exemple, p sera un pointeur vers le 501e élément de mavar.
                Note que, dans g, tu peux toujours utiliser la notation tableau sur le pointeur p, pour accéder à d'autres éléments :
                #include <stdlib.h>
                void struct g(struct T *p) 
                {
                    if (p != NULL)
                    {
                        /* les trois lignes suivantes sont équivalentes */
                        printf("a: %d\n", (*p).n);
                        printf("b: %d\n", p->n);
                        printf("c: %d\n", p[0].n);
                
                        /* la suite suivante ne peut marcher que 
                         * si p pointe dans un tableau, et 
                         * sur autre chose que le dernier élément 
                         */
                        printf("d: %d\n", p[1].n);
                    }
                }
                Comme on ne peut passer vraiment un tableau en paramètre à une fonction (à cause de la conversion implicite en pointeur), il faut passer à la fonction g la taille du tableau, i.e. le nombre d'éléments accessibles à partir de *p.
                void struct g(struct T *p, size_t s) 
                {
                    if (p != NULL && s > 0)
                    {
                        for (int i = 0; i < s; i++)
                        {
                            printf("p[%u].n = %u\n", i, p[i].n);
                        }
                    }
                }
                Ainsi g sait jusqu'où elle peut aller (dans mon exemple, le test "s > 0" est inutile, vue la construction de la boucle, mais dans le cas général il est bon de faire le test).
                  Pour ton problème, si tu veux avoir un seul tableau pour toutes les données de tous les fichiers, je pense que tu peux faire quelque chose comme ça :
                  1. tu créés ton tableau à N éléments par malloc ;
                  2. tu passes à ta fonction un pointeur vers le premier élément, la taille N, et le nom du fichier (que tu détermines comme tu veux) ;
                  3. la fonction lit ce qui se trouve dans le fichier, à hauteur de N lignes, et les place dans le tableau ;
                  4. la fonction renvoie le nombre de lignes lues (avec peut-être un code d'erreur négatif pour indiquer si elle s'est arrêtée à N, et qu'il y a peut-être d'autres lignes encore dans le fichier) ;
                  5. la fonction principale reçoit ce nombre de lignes (disons n), puis rappelle la fonction de lecture sur le n+1è élément du tableau, en lui passant N-n comme limite (les erreurs "à un près" sont classiques dans ce genre de travail sur les tableaux, vérifie mes calculs sur le papier avant de coder (et puis c'est toujours un bon exercice)).
                  Les étapes 2 à 5 sont à faire en boucle, jusqu'à ce que tu ais lu tous tes fichiers.
                    Je te passe les contrôles d'erreur habituels, c'est juste une idée d'algo que tu peux utiliser, à ajuster en fonction de tes besoins.
        • # question con :

          Posté par  . Évalué à 2.

          T'es obligé de faire ça en C ou tu as choisi le C parce que c'est le langage avec lequel tu es le plus à l'aise ?

          Je demande ça parce que les langages de script sont particulièrement adaptés au traitement de données dans des fichiers (c'est limite pour ça qu'ils ont été conçus). Je pense à Perl, Python, ... voire même avec les commandes de shell usuelles ...

        Suivre le flux des commentaires

        Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.