Forum Programmation.c Lecture d'un fichier texte par colonne (débutant)

Posté par  (site Web personnel) .
Étiquettes : aucune
0
6
mar.
2005
Bonjour à tous,

Je débute dans le C, aussi ai-je une question basique mais qui me pose beaucoup de problèmes.
Je dispose d'un fichier de données comportant deux colonnes de chiffres (entier ou flottants), séparés par un séparateur qui peut être une espace, un tab ou une virgule.

Je voudrais lire ce fichier pour stocker les valeurs de chaque colonne dans un tableau.
Pour l'instant, je n'arrive qu'à lire ligne par ligne...

Voici ce que j'ai :


int main()
{
FILE *fp;
fp = open("data.dat","r");
char ligne[81];
while (fgets(ligne,81,fp) != NULL)
{
printf("%s",ligne);
}
}


Maintenant, il faut que je j'identifie mes colonnes, mais je ne sais pas comment faire...

Quelqu'un aurait-il un début de solution ?
Ou un début de doc sur ce problème ?

Merci,


Darckense
  • # re

    Posté par  . Évalué à 2.

    Jette un oeil à la page de man de la fonction fscanf.
    Par contre, prend garde au buffer overflow ...
    • [^] # Re: re

      Posté par  (site Web personnel) . Évalué à 2.

      Salut, je vais tenter cela pour toi

      je n'ai pas essayé si ça compilais ni même si ça marchais mais cela devrait te permettre de t'approcher d'une solution fonctionnelle.


      #include <stdio.h>

      int main()
      {
      FILE *fp;
      int i = 0;
      float nombre1, nombre2;
      float tab1[200];
      float tab2[200];

      fp = open("data.dat","r");
      if (f != NULL)
      {
      while(fscanf(fp, "%f %f", &nombre1, &nombre2) != EOF)
      {
      tab1[i] = nombre1;
      tab2[i] = nombre2;
      i += 1;
      }
      }

      return 0;
      }
      • [^] # Re: re

        Posté par  . Évalué à 3.

        Pour utiliser les fonctions *scanf, il faut être certain du format de données entrantes, s'il y a la moindre erreur de saisie, ça fait boum!

        Donc, on ne doit utiliser les fonctions *scanf que pour interpréter des données écrites par les fonctions *printf !

        Autrement, on utilise fgets, puis, au choix, strtok (si l'on est sûr est certain de ne pas avoir besoin de réentrance), ou strtok_r, ou alencore on interprète "à la main" ...
        • [^] # Re: re

          Posté par  (site Web personnel) . Évalué à 2.

          Les données que je veux rentrer dans mon programme ont été acquise par une manip et sauvées dans un fichier texte en deux colonnes.

          Je suis quasiment certain qu'il n'y a pas d"erreur de saisie... :-)

          Pour fgets, d'accords, mais encore faut-il pouvoir découper la chaine "à la main" comme tu dis, et ce n'est pas spécialement évident pour un novice comme moi.


          Darckense
      • [^] # Re: re

        Posté par  (site Web personnel) . Évalué à 3.

        comme dit l autre, fscanf marche tres bien si les fichiers ont ete generes automatiquement par une autre apli. Sinon ... surprise :)
        ( quoi que avec un 2.6.10, la suprise se limite a un segfault, sur le 2.6.8, c etait tout l affichage qui partait en vrille ... genre des lignes rouges sur mon image de bureau, mais pas sur les fenetres )

        si tu veux t assurer du format du fichier, tu peut le preparser en perl/awk/sed/cut/tr/uniq ...

        J utilise rarement le C seul dans un projet ... j ai toujours d autres scripts en d autres langages a cote.
      • [^] # Re: re

        Posté par  . Évalué à 3.

        Ton programme risque fort de planter si le fichier contient plus de 200 lignes (buffer overflow justement), et tu utilises la fonction open avec les paramètres de fopen, ce qui risque fort de ne pas marcher (dans le meilleur des cas, ça ne compilera pas). De plus, open() n'est pas dans stdio.h.

        Bon, ok, à partir de maintenant je suppose que tu pensais plutôt à fopen(), qui prend bien ce genre de paramètres, et est bien déclarée dans stdio.h.

        Sans parler de l'utilisation de fscanf sans aucune précaution. fscanf retourne le nombre de valeurs assignées. Tu devrais vérifier cette valeur de retour en plus de EOF. Et, comme dit dans mon autre message, fscanf ne devrait être utilisé que pour lire des données formattées par les fonctions de la série des *printf ...

        Bref, beaucoup de choses à revoir :)
        • [^] # Re: re

          Posté par  (site Web personnel) . Évalué à 2.

          Justement, est-il possible de déclarer la taille des tableaux en cours d'éxécution du programme ?

          Du genre je scanne deux fois le fichier texte. Une première fois pour lire le nombre de lignes disponibles. Ensuite, je créé les tableaux de taille ad-hoc, et je relis une seconde fois pour sauver les données dans mon tableau.

          Ou encore, si la première ligne du fichier indique le nombre de ligne de données, il me suffit de lire cette ligne pour créer mes tableaux, et ensuite sauver mes données ??


          Darckense
          • [^] # Re: re

            Posté par  . Évalué à 3.

            Tout à fait, mais pour cela il faut en apprendre un peu sur les pointeurs. Bon, voilà ce que je te propose :
            #include <stdio.h>
            
            /* retourne le nombre de lignes d'un fichier texte deja ouvert */
            int get_nb_lines(FILE* fp)
            {
              unsigned int nb = 0;
              
              while (fgets(fp) != NULL) {
                nb++;
              }
            
              return nb;
            }
            
            /* recupere les valeurs dans v1 et v2 (passage par reference) */
            void get_values(char* ligne, float *v1, float *v2)
            {
               char* tmp:
               char* result;
                /* strtok modifie son premier argument, 
                    on travaille donc sur une variable temporaire */
               strcpy(tmp, ligne);
            
               result = strtok(tmp, "\t, ");
               if(result != NULL) {
                 sscanf(result, "%f", v1);
                 
                 result = strtok(NULL, "\t ");
                 if(result != NULL) {
                    sscanf(result, "%f", v2);
                 }
               }  
            }
            
            
            int main(int argc, char* argv[])
            {
              FILE* fp = NULL;
              unsigned int nb_lines = 0;
              float *value1 = NULL,
                      *value2 = NULL;
              int i = 0;
              char line[256] = "";
              
              
              fp = fopen("test.dat", "r");
              if (fp == NULL) return -1;
            
              nb_lines = get_nb_lines(fp);
            
              value1 = malloc(nb_lines * sizeof *value1);
              value2 = malloc(nb_lines * sizeof *value2);
            
              while((fgets(ligne, 256, fp) != NULL) 
                      && (i<nb_lines)) {
                get_values(ligne, &(value1[i]), &(value2[i]));
                i++;
              }  
            
              /* et bien sur on libere la memoire */
              free(value1);
              value1 = NULL;
              free(value2);
              value2 = NULL;
              fclose(fp);
              fp = NULL;
            
              return  0;
            }
            
            Je ne l'ai pas testé ni même compilé, j'espère que ça marchera, en tout cas l'idée y est ...
            • [^] # Re: re

              Posté par  . Évalué à 2.

              Zut, il y a un GROS LEAK (voire même un segfault) dans la fonction get_nb_lines, mais bon, je te le laisse comme exercice :)
              • [^] # Re: re

                Posté par  (site Web personnel) . Évalué à 1.

                Ouah !

                Impressionnant.

                Bon, j'ai déjà ce que je voulais faire, même si c'est perfectible.
                Demain, je file m'acheter un bouquin sur le C, et je regarde ta fonction get_nb_lines de plus prés.

                Ahlala, que ne faut-il pas faire pour fitter ses données sous linux ! Heureusement qu'il y a les Numerical Recipes ! :-)


                Darckense
                • [^] # Re: re

                  Posté par  . Évalué à 1.

                  mince, y'a encore un bug à la fin de get_nb_lines : il faut se repositionner au début du fichier, sinon la suite ne fonctionnera pas. Cherche donc du côté de la fonction fseek ...
      • [^] # Re: re

        Posté par  (site Web personnel) . Évalué à 1.

        Bonjour,

        Merci beaucoup pour le code. Ca compile presque. En fait, il faut utiliser fopen() au lieu de open(), et tout marche nickel ! ;-)

        Encore une fois merci,

        Amicalement,


        Darckense
    • [^] # Re: re

      Posté par  (site Web personnel) . Évalué à 1.

      Je ne savais pas que les fonctions du C étaient dans les pages man.
      Trés instructif.

      Merci.


      Darckense

Suivre le flux des commentaires

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