Forum Programmation.c Mettre un fichier dans une matrice

Posté par  .
Étiquettes : aucune
0
16
mai
2009
Bonjour les moules !
J'aimerai lire le contenu de ./fichier, qui contient des entiers organisé en tableau, et faire correspondre chacun de ces entiers à une case dans une matrice de type int grille[5][5]

J'ai donc le code suivant :

machin.c

int main(int argc, char *argv[])
{
int caractereActuel, grille[11][11],i,j;

for (i=0;i<=10;i++) //Remise à zéro de la grille
{
for (j=0;j<=10;j++)
{
grille[i][j]=0;
}
}

FILE* fichier = NULL;

fichier = fopen("fichieraouvrir", "r+");

if (fichier != NULL)
{
do
{
for(i=1;i<=5;i++)
{
for(j=1;j<=5;j++)
{
caractereActuel= fgetc(fichier); // On lit le caractère
printf("%c ", caractereActuel);
grille[i][j]=caractereActuel;
}
}
} while (caractereActuel != EOF); // On continue tant que fgetc n'a pas retourné EOF (fin de fichier)

fclose(fichier); // On ferme le fichier qui a été ouvert
}

else
{
// On affiche un message d'erreur si on veut
printf("Impossible d'ouvrir le fichier test.txt");
}

printf("\nAffichage de la matrice ... \n");
for (i=0;i<=5;i++)
{
for (j=0;j<=5;j++)
{
printf("%d ",grille[i][j]);
}
printf("\n");
}
printf("\n");

return 0;
}


fichier :

00200
01000
00000
10030
00050


Evidemment, ca ne marche pas ....
en console ca me donne :

gcc machin.c && ./a.out
0 0 2 0 0
0 1 0 0 0
0 0 0 0 0
1 0 0 3 0
0 0 0 5 0
� � � � � �
Affichage de la matrice ...
48 48 50 48 48 10
48 49 48 48 48 10
48 48 48 48 48 10
49 48 48 51 48 10
48 48 48 53 48 10
-1 -1 -1 -1 -1 -1


Donc je voudrais savoir qu'est ce qui va pas :D Un probleme de pointeur, ou de type de donnée, ou une mauvaise utilisation de fgetc ? fscanf est-elle plus appropriée ici ?
C'est dans le cadre d'un petit projet pour ma premiere année d'IUT, donc j'ai pas forcement toutes les notions necessaires ... ca doit donc etre un peu crade comme code ?

Merci d'avance :)
  • # Nombre d'itérations trop important

    Posté par  . Évalué à 3.

    Bonjour,

    En fait ton problème vient du fait que dans tes boucles for tu teste inférieur ou égal, ce qui signifie que tu va boucler pour i = 0,1,2,3,4 et 5, soit 6 itérations.

    En l'occurrence cela fonctionne car dans ton fichier texte, les 5 premiers champs sont tes nombres, et le sixième champs correspondant à i=5 est le saut de ligne, d'où son affichage comme 10 quand tu demande son passage en entier (10 est le nombre associé à \n en ascii, cf man ascii)

    Quand à la sixième ligne composée de -1, je suppose qu'il s'agit de la conversion de NULL vers entier.

    Essaye ton code en remplaçant ton inférieur ou égal par un inférieur strict, cela devrait régler le problème.
    • [^] # Re: Nombre d'itérations trop important

      Posté par  . Évalué à 2.

      Bonjour, merci de ton aide :)

      J'ai donc remplacé mes 'inferieurs ou égal' par des 'strict inferieur' dans mes doubles boucle ... hélas, ca ne fait qu'empirer :

      Affichage de la matrice ...
      48 48 53 48 10
      -1 -1 -1 -1 -1
      -1 -1 -1 -1 -1
      -1 -1 -1 -1 -1
      -1 -1 -1 -1 -1


      Je suppose aussi que les 48, 53 est autres correspondent aux valeurs ASCII de 0,1,2 ...
      Comment je peux avoir directement 0,1 plutot que leur valeurs ASCII ? Ma variable caractereActuel est pourtant bien un int, je comprend pas quelle change de valeur entre
      printf(%c , caractereActuel); (qui me donne bien le nombre contenu dans le fichier)
      et
      grille[i][j]=caractereActuel;

      • [^] # Re: Nombre d'itérations trop important

        Posté par  . Évalué à 4.

        Je te conseille de reprendre chaque boucle proprement. Fait les toutes commencer à 0, car actuellement tu commence parfois à 0, parfois à 1, ce qui est troublant. En C il vaut mieux savoir ce que l'on fait, et quelles données on manipule.

        Par ailleurs, comme indiqué plus bas, attention à la différence entre un chiffre affiché par ton terminal, et la valeur du code qui le représente en mémoire. Le C est très permissif, donc si tu demande quelque chose de bizarre (afficher un entier comme un caractère par exemple), il te fait confiance. Un grand pouvoir implique de grandes responsabilités de lire pas mal de documentation.

        Je te conseille de te renseigner sur les options de gcc, il est capable de générer des avertissements sur les erreurs courantes. Au minimum -Wall qui active tous les warnings.
  • # confusion entre cast et conversions

    Posté par  . Évalué à 4.

    Salut,

    Je rajouterais que si tu t'attends à retrouver la valeur des entiers de ton fichier dans ta matrice, il faut convertir tes données lues (des char) en entier (int) et pas faire un simple cast (comme c'est fait implicitement dans ton code).

    Pour rappel (si on veut vraiment revenir à la racine), en ASCII :
    valeur d'un chiffre entier = codes ASCII du caractère représentant le chiffre - '0'

    Si tu débutes, je te laisse chercher les détails sur le net, c'est comme ça que tu apprendras vraiment (enfin c'est mon avis, surtout pour des trucs de bases comme ça, la doc foisonne)
  • # Code bon :)

    Posté par  . Évalué à 5.

    J'ai suivi vos conseils, et est donc le code suivant :


    int main(int argc, char *argv[])
    {
    int grille[11][11],i=0,j=0,m=0;
    char caractereActuel;
    for (i=0;i<=10;i++)
    {
    for (j=0;j<=10;j++)
    {
    grille[i][j]=0;
    }
    }

    FILE* fichier = NULL;
    //Réinitialisation des variables
    i=0;
    j=0;
    fichier = fopen("fichieraouvrir", "r+");

    if (fichier != NULL)
    {
    do
    {
    for(j=0;j<=5;j++)
    {
    caractereActuel= fgetc(fichier); // On lit le caractère
    m=caractereActuel-'0';
    printf("i = %d, j = %d, m = %d \n",i,j,m);
    //sprintf(intermediaire,"%c",caractereActuel);
    if (m==-38) //Si l'entier est un retour à la ligne, on change de ligne
    {
    i++;
    }
    else //Si non, on l'écrit a sa place dans la matrice
    {
    grille[i][j]=m;
    }
    }
    } while (caractereActuel != EOF); // On continue tant que fgetc n'a pas retourné EOF (fin de fichier)

    fclose(fichier); // On ferme le fichier qui a été ouvert
    }

    else
    {
    // On affiche un message d'erreur si on veut
    printf("Impossible d'ouvrir le fichier test.txt");
    }
    printf("\nAffichage de la matrice ... \n");
    for (i=0;i<5;i++)
    {
    for (j=0;j<5;j++)
    {
    printf("%d ",grille[i][j]);
    }
    printf("\n");
    }
    printf("\n");

    return 0;
    }



    Qui fontionne bien comme il faut :) Je me suis pris la tete avec atoi(), mais je n'en ai visiblement pas besoin :)
    Aussi, je n'avais pas réaliser que je liser les caracteres colones par colones, et non ligne par ligne. Je règle ce problème en bouclant sur j, et non sur i.
    Est ce que ce code est potable, ou alors je peut l'améliorer ? Je pense notamment au changement de ligne ... vous auriez fait comme moi ? ou autrement ?

    Merci de votre aide en tout cas :)
    • [^] # Re: Code bon :)

      Posté par  . Évalué à 7.

      Bravo ..... JE n'ai pas regardé ton code dans le détail, mais tu fais partie des gens qu'on a envie d'aider. Pourquoi ?
      - tu ne viens pas en nous demandant de faire ton travail : tu as déjà commencé à travailler et tu nous indiques précisément ce qui te gène.
      - tu as exploré les pistes que les intervenants t'ont données pour corriger ton code
      - et sutout tu as diffusé le résultat (pour les curieux) et remercié les gens qui t'ont aidés.

      Ca devient de plus en plus rare les gens comme toi ;)
    • [^] # Re: Code bon :)

      Posté par  . Évalué à 2.

      Alors, en ce qui concerne la "qualité" de ton code (chacun a sa propre vision de ce qui est "bien", donc prend la suite avec des pincettes) :
      - atoi() aurait pu servir, si tu avais des nombres qui tiennent sur plus d'un caractère (en fait, ce ne seraient plus des "chiffres"), mais comme il travaille sur des chaînes C, il aurait fallu lui fournir soit des chaînes terminées par \0. Bref, dans ton cas, c'est cool ça marche bien avec l'astuce c-'0'.
      - Pour les boucles, avec l'expérience, j'ai appris à toujours commencer par 0 et à utiliser quasi uniquement des strictement inférieur. Pour moi c'est beaucoup plus lisible.
      - fgetc() est un peu "bancal" dans le signalement d'EOF, et une utilisation correcte voudrait que tu récupères d'abord un int, le teste voir si ce n'est pas un EOF, et _après_ tu cast vers un _unsigned_ char, qui est la manière la plus "naturelle" de bosser sur des caractères ASCII (ton -38 pour le retour à la ligne je trouve ça moche par rapport à un '\n' ... d'ailleurs pourquoi -38 ? ça a l'air de correspondre à rien de très logique ...) et te permettera surtout de différencier un caractère 0xff dans ton fichier par rapport à la vraie fin du fichier.
      - D'un autre coté, si tu sais que tu auras _exactement_ 6 caractères sur une ligne (5 chiffres + un LF), t'as pas besoin de faire de test du tout, juste un getc() dans le vide.
      - Mais bon, on voit bien que t'essayes de te soucier un peu des cas d'erreurs possibles, mais il reste encore plein de "failles" si tu voulais faire un truc robuste. D'un côté, ce n'est peut-être pas ton objectif ...
      • [^] # Re: Code bon :)

        Posté par  . Évalué à 2.

        Salut !
        Justement, maintenant j'ai des entiers de plus de deux caractères :D
        Du coup, j'attend que fgetc me renvoit 32 pour changer la colone où écrire ...
        J'ai suivi ton conseil sur le -38 et le '\n' aussi, je n'avais pas pensé à ca. C'est effectivement plus lisible et compréhensible.
        En revanche, j'ai un peu de mal a saisir la récupération de l'int, le test et après le cast ...

        le code parlera peut etre plus :) (j'ai enlevé le "pas necessaire")


        #include <stdio.h> /* Librairie d'entrée sortie */
        #include <stdlib.h> /* Librairie de fonctions standard */

        #define SP 32
        int main(int argc, char *argv[])
        {
        int grille[11][11],i=0,j=0,entier=0,findefichier=0;
        char caractereActuel;
        //Réinitialisation de la matrice
        //Réinitialisation des variables
        i=0;
        j=0;
        fichier = fopen("fichieraouvrir", "r");

        if (fichier != NULL)
        {
        while (findefichier==0)
        {
        caractereActuel= fgetc(fichier);
        if (caractereActuel != EOF)
        {
        entier=caractereActuel-'0';
        printf("i = %d, j = %d, m = %d \n",i,j,entier);
        //sprintf(intermediaire,"%c",caractereActuel);
        if (caractereActuel=='\n') //Si l'entier est un retour à la ligne, on change de ligne
        {
        i++;
        j=0;
        }
        else if (caractereActuel == SP) //Si l'entier est un espace, on change de colone
        {
        j++;
        }
        else //Si non, on l'écrit a sa place dans la matrice
        {
        grille[i][j]=(10*grille[i][j])+entier;
        }
        }
        else if (caractereActuel == EOF)
        {
        fclose(fichier); // On ferme le fichier qui a été ouvert
        findefichier=1;
        }
        }
        }

        else
        {
        // On affiche un message d'erreur si on veut
        printf("Impossible d'ouvrir le fichier test.txt");
        }

        //Affichage de la matrice
        return 0;


        Ainsi, pour le fichier

        0 0 2 0 0
        0 1 0 0 0
        0 0 25 0 0
        1 0 0 3 0
        0 0 0 5 0



        ca me donne bien


        bash-3.2$ gcc manipulationfichier.c && ./a.out

        Affichage de la matrice ...
        0 0 2 0 0
        0 1 0 0 0
        0 0 25 0 0
        1 0 0 3 0
        0 0 0 5 0




        Mais en effet, le but n'est pas de faire un truc "super robuste", il s'agit d'un petit projet de pacman dans le cadre des cours, et ce bout de code est pour le chargement du niveau, qui est contenu dans un fichier ...

        Bonne soirée
        • [^] # Re: Code bon :)

          Posté par  . Évalué à 2.

          Pas mal ton code. Quelques remarques (encore :-)) :
          - Tu peux écrire la plupart des caractères de manière "lisible", pas besoin de mettre directement leur code ASCII : pour espace par exemple, met if (caractereActuel == ' ')
          - Pour l'histoire du fgetc() et de l'int : dans le man de fgetc, tu vois que son type de retour est 'int'. Quand tu lis un peu plus loin, il est dit que cette fonction renvoie soit "un unsigned char transformé en int", soit "EOF". C'est parce qu'un caractère peut prendre la valeur 0 à 255 (ou -128 à 127 pour un non signé, mais c'est pareil), et que là tu n'as pas la "place" de caser une valeur pour EOF. Donc, fgetc() renvoit un int, qui vaudra soit -1 (pour EOF, mais t'es pas censé le savoir ; oublie le, mais en pratique ça dépanne des fois) soit de 0 à 255 (pour un caractère normal). Dans ton code, tu cast directement vers un char, donc tu ne "verras" pas la différence entre le caractère 0xff (ou 255)(ou -1) et EOF. Bon, tu vas me dire, au final, tu t'en fous, tu ne gère pas ce caractère. Mais pour une prochaine fois où tu liras des flux "binaires", fais-y attention. Dans ton code, il suffirait à priori de changer la déclaration de caractereActuel en int.
          - Enfin, dans ta gestion de fin de fichier, je bouclerais simplement sur un while (caractereActuel != EOF) et je fermerait le fichier après. Bon, ça c'est vraiment pour le style, genre j'ai envie de raccourcir le code ; ton code reste tout à fait fonctionnel (juste que l'alternative à ton test sur EOF qui reteste sur != de EOF, ça fait un peu "moche").

Suivre le flux des commentaires

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