Partage
  • Partager sur Facebook
  • Partager sur Twitter

BIzarrerie dans la lecture d'un fichier (texte)

Le nombre 26 me fait des misères

    28 mars 2020 à 11:59:22

    Bonjour,

        Voulant enregistrer un tableau de nombres compris entre 0 et 100, j'ai utilisé un fichier texte avec l'option "r" pour lire et "w" pour écrire. Tout semblait fonctionner jusqu'au jour où le nombre 26 a fait partie du tableau. A la lecture, 26 a été remplacé par -1 (comme si 26 était la valeur de EOF) et le reste du tableau est remplacé par 0.

        J'ai résolu le problème en remplaçant les options "r" et "w" par "rb" et "wb" (fichier binaire), et pour le moment, ça a l'air de fonctionner, même avec 26!

        Est ce qu'il y a une explication?

        Voici le texte de mon programme, avec les deux résultats selon que le tableau contient ou non le nombre 26.

    #include <stdio.h>
    #include <stdlib.h>

    FILE* fichier=NULL;
    int i;

    char liste_initiale[45] =  {7,1,2,3,26,5,68,29};     // première liste contenant  26
    //char liste_initiale[45]={7,1,2,3,37,5,68,29};  // deuxieme liste ne contenant pas 26
    char liste_lue[45];

    /// ////////////////////////////////////////////

    void ecriture(char liste[45])
    {
        fichier=fopen("ecriture_de_nombres.zut","w");
        for (i=0;i<=liste[0];i++)
        {
            fputc(liste[i],fichier);
        }
        fclose(fichier);
    }

    /// ////////////////////////////////////////////

    void lecture2(char liste[45])
    {
        char x;

        fichier=fopen("ecriture_de_nombres.zut","r");
        x=fgetc(fichier);
        liste[0]=x;                  // x est indispensable
        i=0;                           // si je fait directement "liste[0]=fgetc(fichier)"
        do                             // ça ne marche pas
        {
            i++;
            x=fgetc(fichier);    // idem
            liste[i]=x;
        }
        while (x!=EOF);
        fclose(fichier);
    }


    /// //////////////////////////////////////////////


    int main()
    {
        ecriture(liste_initiale);        // ecriture de la liste initiale dans un fichier
        lecture2(liste_lue);        // lecture du fichier
        for (i=0;i<=liste_lue[0];i++)    // affichage de la liste lue
        {
            printf("%d,",liste_lue[i]);
        }
        return 0;
    }

    Résultat obtenu avec la liste ne contenant pas 26:  7,1,2,3,37,5,68,29,
    Résultat obtenu avec la liste contenant 26:            7,1,2,3,-1,0,0,0,

    • Partager sur Facebook
    • Partager sur Twitter
    papy essaie de programmer!
      28 mars 2020 à 12:11:17

      Bonjour,

      Merci de colorer votre code à l'aide du bouton Code

      Les forums d'Openclassrooms disposent d'une fonctionnalité permettant de colorer et mettre en forme les codes source afin de les rendre plus lisibles et faciles à manipuler par les intervenants. Pour cela, il faut utiliser le bouton Code de l'éditeur, choisir un des langages proposés et coller votre code dans la zone prévue. Si vous utilisez l'éditeur de messages en mode Markdown, il faut utiliser les balises <pre class="brush: cpp;">Votre code ici</pre>.

      Merci de modifier votre message d'origine en fonction.

      Liens conseillés

      • Partager sur Facebook
      • Partager sur Twitter
        28 mars 2020 à 13:34:49

        Bonjour ! Je viens de tester le programme (sous Linux) et je n'ai pas ce souci.

        − Avec le 26 :

        robun@Ordi:~ > ./zzz
        7,1,2,3,26,5,68,29,robun@Ordi:~ > 
        

        − Sans le 26 :

        robun@Ordi:~ > ./zzz
        7,1,2,3,37,5,68,29,robun@Ordi:~ > 
        

        Ça suggère que ce n'est pas un problème de programmation...

        -
        Edité par robun 28 mars 2020 à 15:56:26

        • Partager sur Facebook
        • Partager sur Twitter
          28 mars 2020 à 14:10:28

          Hello,

          Sous windows (en tout cas jusquà win7 inclus, et sous msdos avant lui), le caractère 0x1a (déc. 26) est considéré comme indicateur de fin de fichier si le fichier est ouvert en mode texte ("r" --- ou parfois "rt"). Sous linux, je ne sais pas, mais le test est simple à faire.

          -
          Edité par edgarjacobs 28 mars 2020 à 14:15:01

          • Partager sur Facebook
          • Partager sur Twitter

          On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

            28 mars 2020 à 15:44:58

            Merci à tous les deux pour vos réponses

            Je travaille avec windows 10. Cela confirme ce que je pense: 26 doit être un code d'indication de fin de fichier, du moins pour les fichiers textes.

            Je vais remettre mon programme en essayant d'utiliser ce qui est prévu pour ça:

            #include <stdio.h>
            #include <stdlib.h>
            
            FILE* fichier=NULL;
            int i;
            
            char liste_initiale[45]={7,1,2,3,26,5,68,29};  // première liste ne contenant pas 26
            //char liste_initiale[45]={7,1,2,3,37,24,98};  // deuxieme liste, contenant 26
            char liste_lue[45];
            
            /// ////////////////////////////////////////////
            
            void ecriture(char liste[45])
            {
                fichier=fopen("ecriture_de_nombres.zut","w");
                for (i=0;i<=liste[0];i++)
                {
                    fputc(liste[i],fichier);
                }
                fclose(fichier);
            }
            
            /// ////////////////////////////////////////////
            
            void lecture2(char liste[45])
            {
                char x;
            
                fichier=fopen("ecriture_de_nombres.zut","r");
                x=fgetc(fichier);
                liste[0]=x;              	// x est indispensable
                i=0;			// si je fait directement "liste[0]=fgetc(fichier)"
                do				// ça ne marche pas
                {
                    i++;
                    x=fgetc(fichier);
                    liste[i]=x;
                }
                while (x!=EOF);
                fclose(fichier);
            }
            
            
            /// //////////////////////////////////////////////
            
            
            int main()
            {
                ecriture(liste_initiale);		// ecriture de la liste initiale dans un fichier
                lecture2(liste_lue);		// lecture du fichier
                for (i=0;i<=liste_lue[0];i++)	// affichage de la liste lue
                {
                    printf("%d,",liste_lue[i]);
                }
                return 0;
            }
            
            Résultat obtenu avec la liste ne contenant pas 26:  7,1,2,3,37,5,68,29,
            Résultat obtenu avec la liste contenant 26:         7,1,2,3,-1,0,0,0,



            • Partager sur Facebook
            • Partager sur Twitter
            papy essaie de programmer!
              28 mars 2020 à 16:33:04

              Salut,

              Pour commencer, tu mélanges allégrement la gestion d'un fichier en mode texte et en mode binaire.

              Il y a une différence entre écrire des nombres et écrire des caractères. Le caractère 26 correspond à la commande SUB, le plus souvent interprétée comme une alternative de la touche "entrée".

              Ton fichier ne contient quasiment que des commandes, ce qui est assez inutile pour un fichier en format texte :

              Le jeu de fonctions fputc, fgetc sont à utiliser avec des caractères. Si tu veux stocker des nombres en mode texte, il y a fprintf() / fscanf().

              Il y a une justification à traiter des fichiers en mode texte lorsque ceux-ci sont destinés à être lus ou édités par un utilisateur humain, ou pas mais qui sache lire ledit fichier. Si tu veux juste stocker des valeurs sur un octet, utilise le mode binaire.

              Bonne continuation.

              Edit : ta lecture va fonctionner juste en ouvrant en "rb" plutôt qu'en "r". Je n'en suis pas certain mais il me semble que ça supprime l'effet d'interprétation du caractère lu.

              -
              Edité par drx 28 mars 2020 à 16:38:42

              • Partager sur Facebook
              • Partager sur Twitter

              Bonhomme !! | Jeu de plateforme : Prototype.

                28 mars 2020 à 16:52:26

                PierreGrenard a écrit:

                Résultat obtenu avec la liste contenant 26:         7,1,2,3,-1,0,0,0,



                Normal, le fichier en ouvert en mode texte. Le 0x1a est donc intereprété comme indicateur de fin de fichier. Le seul moyen de "contourner" cela, c'est d'ouvrir le fichier en mode "rb":
                #include <stdbool.h>
                
                bool readValues(char *filename,char values[]) {
                	FILE *stin;
                	
                	stin=fopen(filename,"rb");
                	if(!stin)
                		return(false);
                
                	int c,i;
                	i=0;
                	while((c=fgetc(stin))!=EOF)
                		values[i++]=c;
                	fclose(stin);
                
                	return(true);
                }
                
                
                


                • Partager sur Facebook
                • Partager sur Twitter

                On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

                  28 mars 2020 à 17:05:00

                  Pour vérifier qu'un fichier est bien écrit en mode texte, il suffit de faire un type <nom-de-fichier>, dans un fenêtre de commande, on voit tout de suite ce qui se passe

                  D'autre part "fprintf() / fscanf()." ne paraît pas très sur, il vaut mieux un "fprintf() / fgets()." c'est plus sûr.

                  -
                  Edité par joel76 28 mars 2020 à 17:06:19

                  • Partager sur Facebook
                  • Partager sur Twitter

                  Le crayon la gomme et le papier sont les meilleurs outils du programmeur !

                    28 mars 2020 à 17:39:13

                    Concernant fprintf() / fscanf(), dans le cas présent, je me serais contenté d'écrire
                    // Ecrire
                    st=fopen("....","wb");
                    fwrite(values,sizeof(values[0])*(values[0]+1),1,st);
                    fclose(st);
                    
                    
                    // Lire
                    st=fopen("....","rb");
                    fread(&values[0],sizeof(values[0]),1,st);
                    fread(values+1,sizeof(values[0])*values[0],1,st);
                    fclose(st);
                    


                    Ça fonctionne pour les types char et entier.

                    -
                    Edité par edgarjacobs 28 mars 2020 à 17:45:42

                    • Partager sur Facebook
                    • Partager sur Twitter

                    On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

                      28 mars 2020 à 18:11:47

                      joel76 a écrit:

                      D'autre part "fprintf() / fscanf()." ne paraît pas très sur, il vaut mieux un "fprintf() / fgets()." c'est plus sûr.

                      Pas Sûr ? C'est à dire ?

                      fscanf() et fgets() ne font rien d'équivalent. à la rigueur, on peut remplacer fgets() par fscanf() mais l'inverse n'est pas forcément vrai.

                      Effectivement, si le fichier n'est pas formaté selon les attentes du fscanf(), ça pose problème, mais le problème sera le même quand il s'agira de parser la chaîne de char extraite par fgets(), sans parler d'avoir à programmer le parseur avec des risques d'erreurs quand une fonction qui a fait ses preuves peut le faire à moindre coût.

                      N'importe qui peut modifier n'importe quel fichier, si le fichier est en binaire, il est encore possible de le modifier. Pour la petite histoire, j'encapsule mes ressources et ayant connaissance de la construction de la capsule, je peux remplacer une image par une autre dans le jeu en tapant au bon endroit. Je peux même me mettre 200 vies si je tape au bon endroit dans l'exécutable.

                      Bref, il n'y a pas de fichier "sûr", il doit être formaté selon les attentes du programme et c'est tout.

                      @edgarjacobs :

                      fwrite(values, 1, values[0]+1, st);

                      semble être suffisant non ?

                      -
                      Edité par drx 28 mars 2020 à 18:19:01

                      • Partager sur Facebook
                      • Partager sur Twitter

                      Bonhomme !! | Jeu de plateforme : Prototype.

                        28 mars 2020 à 19:03:35

                        Oui, si le type est char. Mais j'ai écrit cela en pensant aussi aux types entier, et le second paramètre est la taille d'un élément à écrire. Et j'ai toujours employé employé 1 pour le nombre d'éléments à écrire, préférant donner le nombre d'octets à écrire.

                        -
                        Edité par edgarjacobs 28 mars 2020 à 19:06:04

                        • Partager sur Facebook
                        • Partager sur Twitter

                        On écrit "j'ai tort", pas "tord" qui est le verbe "tordre" à la 3ème personne de l'indicatif présent

                          28 mars 2020 à 23:12:24

                          J'ai tendance à préférer fprintf()/fgets() suivi éventuellement d'un sscanf()
                          • Partager sur Facebook
                          • Partager sur Twitter

                          Le crayon la gomme et le papier sont les meilleurs outils du programmeur !

                          BIzarrerie dans la lecture d'un fichier (texte)

                          × 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