Partage
  • Partager sur Facebook
  • Partager sur Twitter

Problème d'écriture dans un fichier ( C )

Problème d'écriture dans un fichier ( C )

    14 août 2019 à 22:32:43

    Bonjour, je me permets d'écrire mon premier sujet car je rencontre un problème dans un programme "plus conséquent". J'ai isolé la partie du code qui me pose problème. Le problème rencontrer est une exception non générée lors de l'écriture dans le fichier via fprintf. Merci à vous ! :)

    typedef struct dated {
    	int d;
    	int m;
    	int y;
    } dated;
    
    typedef struct user {
    
    	char *name[SIZE_NAME];
    	dated create;
    
    } user;
    
    void main()
    {
    	user player, player_find; // actual player = player
    
    	FILE *files;
    
    
    			printf("Name : ");
    			gets(player.name);
    			files = "Register.txt";
    
    			if (fopen(files, "w") != NULL)
    			{
    				fprintf(files, "%s %02d.%02d.%d", player.name, player.create.d, player.create.m, player.create.y);
    				fclose(files);
    			}
    			else printf("error !");
    
    	_getch();
    }
    • Partager sur Facebook
    • Partager sur Twitter
      14 août 2019 à 22:45:29

      Il y a beaucoup de problème dans ton code !...

      Le plus gros c'est que tu ne récupère pas le pointeur FILE retourné par fopen et tu t'en sers pour stocker une chaîne de caractère, alors qu'il n'est pas fait pour cela.

      • Partager sur Facebook
      • Partager sur Twitter
        14 août 2019 à 22:54:37

        Merci de votre réponse ! En effet, j'ai mal recopier mon code en isolant cette partie. Ce code fonctionne normalement.

        typedef struct dated {
        	int d;
        	int m;
        	int y;
        } dated;
        
        typedef struct user {
        
        	char *name[SIZE_NAME];
        	dated create;
        
        } user;
        
        void main()
        {
        	user player, player_find; // actual player = player
        
        	FILE *files;
        
        
        			printf("Name : ");
        			gets(player.name);
        			printf("Dated : ");
        			scanf("%d/%d/%d", &player.create.d, &player.create.m, &player.create.y);
        
        			files = fopen("Register.txt", "w");
        
        			if (files != NULL)
        			{
        				fprintf(files, "%s %02d.%02d.%d", player.name, player.create.d, player.create.m, player.create.y);
        				fclose(files);
        			}
        			else printf("error !");
        
        	_getch();
        }

        -
        Edité par VHS 14 août 2019 à 23:03:55

        • Partager sur Facebook
        • Partager sur Twitter
          14 août 2019 à 23:50:38

          Essaie avec 
          char name[SIZE_NAME];

          au lieu de 

          char *name[SIZE_NAME];



          • Partager sur Facebook
          • Partager sur Twitter
            15 août 2019 à 11:35:02

            Le code fonctionne maintenant avec char *name[SIZE_NAME]; , quelle est la différence entre 

            char name[SIZE_NAME]; 

            et

            char *name[SIZE_NAME]; 

            Grâce au pointeur sur le tableau, il sera possible d'écrire name = "Jean" par exemple tandis que sans le pointeur sur le tableau, on est obliger de faire l'acquisition de chaque lettre du nom dans une for  ? Si oui, pourquoi me proposez-vous de supprimer * ? 

            • Partager sur Facebook
            • Partager sur Twitter
              15 août 2019 à 11:57:30

              Là, tu t'embrouille les pinceaux !

              Une chaîne de caractère se stocke dans un tableau de char pas dans un tableau de pointeur sur char. Au vu de ton code, ton but est bien de stocker une chaîne de caractère dans un tableau de char.

              Relis ton cours sur les chaînes de caractères.

              • Partager sur Facebook
              • Partager sur Twitter
                15 août 2019 à 12:22:21

                Merci de votre réponse ! J'ai relu mon cours. En effet, je me mélange les pinceaux car j'ai du mal à comprendre la notion de variables de types pointeur... Serait-il possible d'avoir des explications supplémentaires, voir des exemples afin de savoir leurs utilités et leurs fonctionnement ? Serait-il également possible de savoir comment les modifier dans une fonction ? Merci à vous.

                • Partager sur Facebook
                • Partager sur Twitter
                  15 août 2019 à 12:42:59

                  Une explication qui me semble simple, je pense, c'est de dire qu'un pointeur est une variable destinée à contenir une adresse. Par exemple :

                  char *name[SIZE_NAME];

                  est un tableau dont les éléments sont de type char *, c'est-à-dire dont les éléments sont des adresses (plus précisément des adresses de variables contenant un char). Ces adresses peuvent être utiles ou non, ça dépend du programmeur...

                  Le meilleur cours sur les pointeurs à ma connaissance est celui-ci : https://www.labri.fr/perso/billaud/travaux/Pointeurs/pointeurs.html (attention, c'est complet, mais on apprend déjà plein de choses en lisant le début). Son auteur est un intervenant régulier du forum.

                  -
                  Edité par robun 15 août 2019 à 12:44:21

                  • Partager sur Facebook
                  • Partager sur Twitter
                    15 août 2019 à 12:47:35

                    Merci, j'ai du mal à voir l'utilité d'un tableau d'adresse, auriez-vous un exemple concret ? Je vais allez voir ça :) Merci !
                    • Partager sur Facebook
                    • Partager sur Twitter
                      15 août 2019 à 13:42:40

                      Un exemple concret... heu... Ah, j'en ai un :

                      Imaginons que le texte d'un fichier (dont les lignes sont de longueur variable) soit mis dans un tableau de caractères appelé tabchar. C'est un tableau unidimensionnel : il contient les caractères à la suite, y compris les \n de passage à la ligne.

                      Une fonction pourrait alors analyser ce tableau et créer un tableau de pointeurs appelé tabligne contenant les adresses des débuts de chaque ligne. Le premier élément de ce tableau de pointeurs serait l'adresse du premier élément de tabchar. Ensuite, la fonction parcourt le tableau tabchar et, chaque fois qu'elle rencontre un \n, elle le remplace par un \0 et ajoute l'adresse de l'élément suivant dans notre tableau d'adresses (tabligne). On pourra alors écrire tabligne[n] pour accéder à la ligne numéro n du fichier, et ce sera géré comme une chaîne de caractères puisqu'on a remplacé \n par \0. Par exemple on pourra écrire printf("%s", tabligne[n]).

                      -------------------------------------------------------

                      Du coup je me suis fait un petit T.P. pour profiter du 15 août, je trouvais l'idée intéressante.

                      Voici le programme (je l'ai fait à la va-vite, il y a sûrement des trucs à revoir, par exemple dans la dernière boucle j'ai dû mettre i <= numligne au lieu de i < numligne sinon je n'avais pas la dernière ligne, je ne sais pas trop pourquoi (*)) :

                      /*                  Découpage d'un fichier en lignes                  */
                      /*                                                                    */
                      /* Ce programme lit un fichier et range les caractères dans un        */
                      /* tableau unidimensionnel, puis le décompose en lignes.              */
                      
                      #include <stdbool.h>
                      #include <stdio.h>
                      #include <stdlib.h>
                      #define TAILLEMAX 65536    // nombre maxi de caractères du fichier
                      #define LIGNEMAX  10000    // nombre maxi de lignes du fichier
                      
                      void remplir(FILE *fichier, char tableau[], int *pnbchar)
                      // Remplit le tableau de caractères à partir du fichier
                      {
                          int i = 0 ; // compteur
                          int c ;     // caractère courant
                          while (true)
                          {
                              c = fgetc(fichier) ;
                              if ((c == EOF) || (i >= TAILLEMAX))
                                  break ;
                              else
                              {
                                  tableau[i] = c ;
                                  i++ ;
                              }
                          }
                          *pnbchar = i ;
                      }
                      
                      void pointerlignes(char tabchar[], char *tabligne[], int nbchar, int *pnumligne)
                      // Remplit le tableau d'adresses des lignes à partir du tableau de caractères
                      {
                          // Première ligne :
                          *pnumligne = 0 ;
                          tabligne[0] = &tabchar[0] ;
                          // Les autres lignes :
                          for (int i = 0 ; i < nbchar ; i++)
                          {
                              if (tabchar[i] == '\n')
                              {   // le prochain caractère sera une nouvelle ligne...
                                  tabchar[i] = '\0' ;
                                  (*pnumligne)++ ;
                                  // ... du moins si ce n'est pas le dernier caractère du fichier
                                  if (i+1 < nbchar)
                                      tabligne[*pnumligne] = &tabchar[i+1] ;
                              }
                          }
                      }
                      
                      int main(void)
                      {
                          char nomfichier[256] ;
                          FILE *fichier ;
                          char tabchar[TAILLEMAX] ;   // tableau contenant tous les caractères
                          char *tabligne[LIGNEMAX] ;  // tableau des adresses de chaque ligne
                          int nbchar ;                // nombre de caractères du fichier
                          int numligne ;              // nombre de lignes du fichier
                          printf("Entrer le nom d'un fichier :\n") ;
                          printf("(Le fichier doit exister et faire 64 Kio maxi)\n") ;
                          printf("--> ") ;
                          scanf("%s", nomfichier) ;
                          fichier = fopen(nomfichier, "r") ;
                          if (fichier == NULL) return 1 ;
                          // Création du tableau unidimensionnel
                          remplir(fichier, tabchar, &nbchar) ;
                          // Création du tableau de pointeurs...
                          pointerlignes(tabchar, tabligne, nbchar, &numligne) ;
                          // ... ce qui permet d'afficher les lignes numérotées
                          for (int i = 0 ; i <= numligne ; i++)
                          {
                              printf("%3d : %s\n", i+1, tabligne[i]) ;
                          }
                          fclose(fichier) ;
                          return 0 ;
                      }

                      Et quand on applique ce programme sur le fichier source, ça donne :

                      robun@Ordi:~/prog/c/2019 > decfich
                      Entrer le nom d'un fichier :
                      (Le fichier doit exister et faire 64 Kio maxi)
                      --> decfich.c
                        1 : /*                  Découpage d'un fichier en lignes                  */
                        2 : /*                                                                    */
                        3 : /* Ce programme lit un fichier et range les caractères dans un        */
                        4 : /* tableau unidimensionnel, puis le décompose en lignes.              */
                        5 : 
                        6 : #include <stdbool.h>
                        7 : #include <stdio.h>
                        8 : #include <stdlib.h>
                        9 : #define TAILLEMAX 65536    // nombre maxi de caractères du fichier
                       10 : #define LIGNEMAX  10000    // nombre maxi de lignes du fichier
                       11 : 
                       12 : void remplir(FILE *fichier, char tableau[], int *pnbchar)
                       13 : // Remplit le tableau de caractères à partir du fichier
                       14 : {
                       15 :     int i = 0 ; // compteur
                       16 :     int c ;     // caractère courant
                       17 :     while (true)
                       18 :     {
                       19 :         c = fgetc(fichier) ;
                       20 :         if ((c == EOF) || (i >= TAILLEMAX))
                       21 :             break ;
                       22 :         else
                       23 :         {
                       24 :             tableau[i] = c ;
                       25 :             i++ ;
                       26 :         }
                       27 :     }
                       28 :     *pnbchar = i ;
                       29 : }
                       30 : 
                       31 : void pointerlignes(char tabchar[], char *tabligne[], int nbchar, int *pnumligne)
                       32 : // Remplit le tableau d'adresses des lignes à partir du tableau de caractères
                       33 : {
                       34 :     // Première ligne :
                       35 :     *pnumligne = 0 ;
                       36 :     tabligne[0] = &tabchar[0] ;
                       37 :     // Les autres lignes :
                       38 :     for (int i = 0 ; i < nbchar ; i++)
                       39 :     {
                       40 :         if (tabchar[i] == '\n')
                       41 :         {   // le prochain caractère sera une nouvelle ligne...
                       42 :             tabchar[i] = '\0' ;
                       43 :             (*pnumligne)++ ;
                       44 :             // ... du moins si ce n'est pas le dernier caractère du fichier
                       45 :             if (i+1 < nbchar)
                       46 :                 tabligne[*pnumligne] = &tabchar[i+1] ;
                       47 :         }
                       48 :     }
                       49 : }
                       50 : 
                       51 : int main(void)
                       52 : {
                       53 :     char nomfichier[256] ;
                       54 :     FILE *fichier ;
                       55 :     char tabchar[TAILLEMAX] ;   // tableau contenant tous les caractères
                       56 :     char *tabligne[LIGNEMAX] ;  // tableau des adresses de chaque ligne
                       57 :     int nbchar ;                // nombre de caractères du fichier
                       58 :     int numligne ;              // nombre de lignes du fichier
                       59 :     printf("Entrer le nom d'un fichier :\n") ;
                       60 :     printf("(Le fichier doit exister et faire 64 Kio maxi)\n") ;
                       61 :     printf("--> ") ;
                       62 :     scanf("%s", nomfichier) ;
                       63 :     fichier = fopen(nomfichier, "r") ;
                       64 :     if (fichier == NULL) return 1 ;
                       65 :     // Création du tableau unidimensionnel
                       66 :     remplir(fichier, tabchar, &nbchar) ;
                       67 :     // Création du tableau de pointeurs...
                       68 :     pointerlignes(tabchar, tabligne, nbchar, &numligne) ;
                       69 :     // ... ce qui permet d'afficher les lignes numérotées
                       70 :     for (int i = 0 ; i <= numligne ; i++)
                       71 :     {
                       72 :         printf("%3d : %s\n", i+1, tabligne[i]) ;
                       73 :     }
                       74 :     fclose(fichier) ;
                       75 :     return 0 ;
                       76 : }
                      robun@Ordi:~/prog/c/2019 > 
                      

                      (*) Ah : à la ligne 38 j'aurais dû initialiser i = 1 je crois.

                      -
                      Edité par robun 15 août 2019 à 14:46:33

                      • Partager sur Facebook
                      • Partager sur Twitter
                        15 août 2019 à 16:47:34

                        Merci, j'ai tout compris grâce à votre exemple ! C'est effectivement très fort :lol:   Cependant, pourriez-vous me dire à quoi sert le #include <stdbool.h> ? Merci beaucoup ! 

                        -
                        Edité par VHS 15 août 2019 à 16:47:54

                        • Partager sur Facebook
                        • Partager sur Twitter
                          15 août 2019 à 17:07:43

                          EzrealAp a écrit:

                          .... pourriez-vous me dire à quoi sert le #include <stdbool.h> ? .... 

                          Un peu de recherche....

                          -
                          Edité par edgarjacobs 15 août 2019 à 17:08:21

                          • 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

                            15 août 2019 à 17:14:35

                            Je m'en suis servi uniquement pour écrire while (true), qui est une boucle dont on ne sort que par une instruction break. Sinon on peut faire while (1), mais true est plus clair je trouve. C'est une façon un peu bizarre d'écrire des boucles, mais je la trouve pratique. (Les pros du C feraient plutôt while (c = fgetc(fichier) != EOF) mais je n'aime pas mélanger deux instructions en une, ça m'embrouille...)
                            • Partager sur Facebook
                            • Partager sur Twitter
                              15 août 2019 à 17:18:30

                              La boucle infinie : autrefois, on écrivait   for(;;){...}, sachant que la condition (second paramètre) du for est vraie par défaut. C'est une des premières choses qu'on apprenait : Infinite loop, kernighan et ritchie 2nd ed. ,p60

                              De nos jours, on joue moins à faire du "code cryptique" sous prétexte d'optimisation, les compilateurs font ça mieux que les  programmeurs. Une version clean pour une boucle sur un texte :

                              for(;;) {                         // ou while(true)
                                 int c = getchar();
                                 if (c == EOF) break;
                                 ...
                              }
                              

                              qui a l'intérêt de limiter la portée de la déclaration de c.

                              Mais bon, si on y tient, on peut aussi se faire une macro

                              #define forEachChar(x) for(int x; (x = getchar() != EOF);)

                              qui définit une boucle "pour tout caractère, faire".




                              A quoi sert stdbool.h ? Suffit de regarder dans le fichier stdbool.h  (/usr/lib/gcc/x86_64-linux-gnu/8/include/stdbool.h, par exemple)

                              #define bool	_Bool
                              #define true	1
                              #define false	0
                              


                              En résumé : ça définit

                              • deux constantes true et false
                              • le type  bool comme redéfinition de _Bool

                              _Bool est le type "officiel" pour les booléens, depuis C99.

                              ---

                              La question qu'on peut poser, c'est pourquoi le type officiel des booléens, c'est pas bool ?

                              Pour ça il faut revenir aux débuts de C. La philosophie de l'époque, c'était

                              > pour les booléens, y a pas besoin de type spécial au niveau du compilateur. Déja 1) le compilateur considère comme vrai tout ce qui n'est pas zero,  2) vous le collez dans un entier ou un champ de bits de structure, et ça vous fait l'affaire. Démerdez-vous avec.

                              En pratique, les programmeurs aimaient quand même bien se définir un type bool, ou boolean avec deux constantes true/false. Ou True/False. Ou TRUE/FALSE. Dans un int, un short, ou un char. Ou alors une enumération. Avec un consensus pour que false soit 0, mais pour true, ça pouvait être 1 ou -1 (tous les bits à 1).

                              Donc on se retrouve, dans un programme,  à utiliser des bibliothèques qui n'ont pas les mêmes conventions pour représenter les booléens. C'est le bordel.

                              Un autre problème, c'est que si un booléen n'est pas égal à faux, c'est pas pour autant qu'il est égal à vrai (la constatnte qui représente vrai) :

                              typef int bool;
                              
                              #define true 1
                              #define false 0
                              
                              bool truc = 3;  // vrai parce que non nul
                              
                              if (truc == true) {
                                 // ah non, on ne passe pas par là
                              }

                              D'où la nécessité faire quelque chose. En C99, le comité a donc introduit un type _Bool géré par le compilateur, qui s'occupe de "normaliser la représentation".  C'est à dire que si on affecte quelque chose qui n'est pas faux dans un booléen, il est rectifié à sa valeur standard qui est "true". De façon à ce qu'une variable booléenne ne puisse prendre que DEUX valeurs.

                              Pourquoi l'avoir appelé _Bool plutôt que bool ? Parce que chaque code existant ou presque avait déjà défini le type bool (voir plus haut), et que ça aurait sévèrement clashé à la première compilation. Quand on fait évoluer un langage, il faut essayer de rester compatible...

                              Donc stratégie :

                              • le type _Bool est prédéfini et généré par le compilateur
                              • pour ceusses qui n'en veulent, on fournit  bool, true et false via un #include <stdbool.h>

                              -
                              Edité par michelbillaud 15 août 2019 à 17:40:43

                              • Partager sur Facebook
                              • Partager sur Twitter
                                15 août 2019 à 17:28:22

                                D'accord, merci à tous pour votre aide.
                                • Partager sur Facebook
                                • Partager sur Twitter
                                  15 août 2019 à 17:28:27

                                  Merci pour toutes ces précisions, je ne connaissais pas toute l'histoire !
                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                    15 août 2019 à 17:45:05

                                    Y a deux trucs à savoir sur le langage C

                                    1. Il était mal foutu au départ (les tableaux et les chaines de caractères sont un désastre), avec une philosophie qui n'est plus du tout au goût du jour (qui s'est avérée ruineuse en coût de développement/maintenance  - quand vous entendez parler de fuites mémoire ou d'attaque par débordement de tampon, pas besoin de trop réfléchir pour savoir dans quel langage ça a été programmé )

                                    2. les tentatives pour l'arranger sont bloquées par la masse de code existant, et n'ont pas rattrapé le coup.

                                    -
                                    Edité par michelbillaud 15 août 2019 à 17:48:32

                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      17 août 2019 à 12:17:03

                                      Bonjour à tous ! :) Ne trouvant pas sur internet, je me demandais si par hasard il serait possible de calculer la dimension d'un tableau sur base de la taille d'un fichier ? Je voudrais donc pouvoir avoir un tableau qui fait la taille de mon fichier. Serait-il possible de savoir comment faire ? Merci à tous et bon week-end ! 

                                      					int size_array = sizeof(files);
                                      
                                      					char array[size_array];



                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                        17 août 2019 à 12:24:41

                                        Oui, c'est possible.

                                        • Un appel système pour connaître la taille du fichier
                                        • Une allocation dynamique

                                        Mais c'est un autre sujet.

                                        -
                                        Edité par michelbillaud 17 août 2019 à 12:25:22

                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          17 août 2019 à 12:46:50

                                          Bonjour, je me permets de poser une question qui est surement débile mais je suppose qu'il n'est pas possible de faire le calcul de la dimension d'un tableau a chaque passage dans une boucle do while sachant que la réservation des emplacements mémoire se fait lors du débogage ? o_O

                                          -
                                          Edité par VHS 18 août 2019 à 12:12:12

                                          • Partager sur Facebook
                                          • Partager sur Twitter

                                          Problème d'écriture dans un fichier ( C )

                                          × 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