code de débutant, ça tient debout

Le
bpascal123
Bonjour cyberespace,

Pour une fois, ce code fonctionne. J'ai une version sans pointeurs ni
fonctions d'écriture de fichier, dans tout les cas, le mécanisme est
le même. Je ne vois pas pour ce code ce que les pointeurs peuvent
apporter de plus mais peut-être c'est un avantage de connaître un peu
les pointeurs sans avoir envie d'aller trop loin dans ce langage.

Je cherche juste à savoir si je prends de bonnes habitudes, c'est
surtout au niveau de la saisie.

Merci


/*
Ce prg cree un fichier avec des enregistrements dont le nombre varie
selon ce qu'entre l'utilisateur.
*/

#include <stdio.h> /* getchar putchar getc printf fprint fopen fclose
*/
#include <stdlib.h> /* strtol */

#define CMAXREC 20 /* nbr d'enreg. max*/
#define CMAXCAR 30 /* nbr de caracteres max. */

void ReadTxt(char FTxt[]) ;
void ReadSaveToFile(FILE *FP_FILE) ;
void ReadFile1(FILE *FP_FILE) ;
void ReadFile2(FILE *FP_FILE) ; /* fscanf fprintf printf */
long int ConvToDigit(char FTxt[]) ;

int main(void)
{
FILE *MP_FILE ;
char MP_Name[] = "/media/XP/code_c/FamFile.txt" ;


printf("Ce prg cree un fichier avec des enregistrements dont le nbr
varie"
"selon ce qu'entre l'utilisateur.") ;

if ((MP_FILE = fopen(MP_Name, "w")) == NULL)
{
printf("Erreur ouverture fichier.") ;
exit(EXIT_FAILURE) ;
}

ReadSaveToFile(MP_FILE) ;
fclose(MP_FILE) ;

if ((MP_FILE = fopen(MP_Name, "r")) == NULL)
{
printf("Erreur de lecture du fichier") ;
exit(EXIT_FAILURE) ;
}

ReadFile1(MP_FILE) ;
fclose(MP_FILE) ;

printf("") ;
return 0 ;
}


void ReadTxt(char FTxt[])
{
int i ;
int c ;

for (i = 0 ; ((c = getchar()) != '') ; i++)
{
if (i < CMAXCAR)
FTxt[i] = c ;
else
{
while(getchar() != '') ;
break ;
}
}
FTxt[i] = '' ;
}

void ReadSaveToFile(FILE *FP_FILE)
{
char MFBuff[CMAXCAR] ;
char MFPrePere[CMAXCAR] ;
char MFPreMere[CMAXCAR] ;
char MFPreEnf[CMAXCAR] ;
char rep1 ;
int nb_rec, i ;
long int nb_enf ;

rep1 = 'x' ;
nb_rec = 0 ;
do
{
printf("Enregistrement (o/n) ? ") ;
rep1 = getc(stdin) ;
while (getchar() != '') ;

if (rep1 == 'o' && nb_rec < CMAXREC)
{
printf("Prenom du pere : ") ;
ReadTxt(MFPrePere) ;
printf("Prenom de la mere : ") ;
ReadTxt(MFPreMere) ;

fprintf(FP_FILE, "%s%s", MFPrePere, MFPreMere) ;

printf("Nombre d'enfants : ") ;
ReadTxt(MFBuff) ;

nb_enf = ConvToDigit(MFBuff) ;

if (nb_enf > 0)
{
for (i = 0 ; i < nb_enf ; i++)
{
printf("Enfant %d : ", i+1) ;
ReadTxt(MFPreEnf) ;
fprintf(FP_FILE, "%s", MFPreEnf) ;
nb_rec++ ;
}
}

nb_rec++ ;
}
} while (rep1 == 'o') ;
}

long int ConvToDigit(char FTxt[])
{
char *p ;
long int rslt ;

rslt = strtol(FTxt, &p, 10) ;

return rslt ;
}

void ReadFile1(FILE *FP_FILE)
{
int c ;

while ((c = getc(FP_FILE)) != EOF)
putchar(c) ;
}

void ReadFile2(FILE *FP_FILE) /* Pour info : ca ne marche car le nbr
de prenom enfant est variable donc different de 3 (variable déclarée)
*/
{
char FPrePere[CMAXCAR] ;
char FPreMere[CMAXCAR] ;
char FPreEnf[CMAXCAR] ;

while (fscanf(FP_FILE, "%s%s%s", FPrePere, FPreMere, FPreEnf) == 3)
printf("%s%s%s", FPrePere, FPreMere,FPreEnf) ;

}
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
bpascal123
Le #22674081
En fait, je suis assez satisfait que ca marche mais j'ai passé
beaucoup à essayer le même code sans passer par l'écriture d'un
fichier. Mais j'ai trouvé difficile l'algorithme pour ça parce que le
fait que le nombre d'enfant peut varier rend le stockage en mémoire
plutôt difficile surtout pour une restitution à l'écran, il y a soit
un chevauchement en mémoire entre les enregistrements parents et les
enregistrements des enfants soit un écart et entre les 2 des contenus
mémoires "poubelle".

La seule solution me semble d'écrire dans un fichier tampon et de lire
ce fichier après saisie.

Peut-être sans passer par l'écriture/lecture de fichier, on peut faire
la même chose avec un tableau de pointeurs et realloc...
Tonton Th
Le #22674151
On 10/14/2010 02:06 AM, wrote:


FILE *MP_FILE ;



Pourquoi mp_file en majuscules ?

--
Ma coiffeuse est formidable - http://sonia.buvette.org/
-ed-
Le #22674981
On 14 oct, 02:06, ""
Bonjour cyberespace,

Pour une fois, ce code fonctionne. J'ai une version sans pointeurs ni
fonctions d'écriture de fichier, dans tout les cas, le mécanisme est
le même. Je ne vois pas pour ce code ce que les pointeurs peuvent
apporter de plus mais peut-être c'est un avantage de connaître un peu
les pointeurs sans avoir envie d'aller trop loin dans ce langage.

Je cherche juste à savoir si je prends de bonnes habitudes, c'est
surtout au niveau de la saisie.






-------------- Build: Debug in hello ---------------

Compiling: main.c
Linking console executable: binDebughello.exe
Output size is 64.89 KB
Process terminated with status 0 (0 minutes, 3 seconds)
0 errors, 0 warnings

C'est de bon augure ...


Ce prg cree un fichier avec des enregistrements dont le nbr varie
selon ce qu'entre l'utilisateur.

Erreur ouverture fichier.
Process returned 1 (0x1) execution time : 0.073 s
Press any key to continue.

Par contre ça, c'est bizarre. J'ai juste lancé le programme.

char MP_Name[] = "/media/XP/code_c/FamFile.txt" ;

printf("nCe prg cree un fichier avec des enregistrements dont le nbr
varien"
"selon ce qu'entre l'utilisateur.n") ;

if ((MP_FILE = fopen(MP_Name, "w")) == NULL)




Présuppose que j'ai un répertoire "/media/XP/code_c/". Comme il n'y a
rien dans le code pour le créer, il faut informer l'utilisateur qu'il
doit le créer lui-même. C'est pas un problème de langage, mais de
définition du comportement du programme à réaliser.

Pour tester rapidement, j'ai donc remplacé par

char MP_Name[] = "/media/XP/code_c/FamFile.txt" ;

et ça donne, par exemple :

char MP_Name[] = "FamFile.txt" ;

Ce prg cree un fichier avec des enregistrements dont le nbr varie
selon ce qu'entre l'utilisateur.

Enregistrement (o/n) ? o

Prenom du pere : Homer

Prenom de la mere : Marge

Nombre d'enfants : 3

Enfant 1 : Bart

Enfant 2 : Lisa

Enfant 3 : Maggie

Enregistrement (o/n) ? n
Homer
Marge
Bart
Lisa
Maggie



Process returned 0 (0x0) execution time : 54.434 s
Press any key to continue.

Ce qui parait être un comportement correct. (à part que j'aurais mis
la question "enregistrement (o/n)" à la fin et non au début, donc avec
un do-while)

Le fichier créé contient :

Homer
Marge
Bart
Lisa
Maggie

Analysons maintenant le code.

/*
Ce prg cree un fichier avec des enregistrements dont le nombre varie
selon ce qu'entre l'utilisateur.
*/

#include */
#include
#define CMAXREC 20              /* nbr d'enreg. max*/



Pour un fichier, ce n'est pas nécessaire.

#define CMAXCAR 30              /* nbr de caracteres max. * /

void ReadTxt(char FTxt[]) ;
void ReadSaveToFile(FILE *FP_FILE) ;
void ReadFile1(FILE *FP_FILE) ;
void ReadFile2(FILE *FP_FILE) ;   /* fscanf fprintf printf */
long int ConvToDigit(char FTxt[]) ;



Il n'est pas utile de séparer les prototypes si on définit les
fonctions avant de les utiliser

int main(void)
{
        FILE *MP_FILE ;



Habituellement, on utilise les majuscule pour les constantes. Que
signifie MP ?

        char MP_Name[] = "/media/XP/code_c/FamFile.txt" ;

        printf("nCe prg cree un fichier avec des enregistrements dont le nbr
varien"
        "selon ce qu'entre l'utilisateur.n") ;

        if ((MP_FILE = fopen(MP_Name, "w")) ==  NULL)



Bien garder à l'esprit que "w" créée un nouveau fichier à chaque fo is.
"a" est probablement plus adapté ...

        {
                printf("nErreur ouverture fichier.") ;



Pour garantir l'affichage, le texte doit être terminé par 'n'.


                exit(EXIT_FAILURE) ;
        }

        ReadSaveToFile(MP_FILE) ;
        fclose(MP_FILE) ;



Il n'est pas forcément très utile de séparer l'ouverture/fermeture du
fichier et son utilisation. Souvent, on ne fait qu'une fonction, à
moins qu'une partie du code soit factorisable et utilisé plusieurs
fois. Mais comme on ne peut pas savoir dans la fonction, si le fichier
a été ouvert en "w", "a" ou "r", ça peut poser des problèmes
bizarres ...


        if ((MP_FILE = fopen(MP_Name, "r")) ==  NULL)



Je n'aime pas beaucoup le recyclage sauvage de variable, mais
admettons...

        {
                printf("nErreur de lecture du fichier") ;
                exit(EXIT_FAILURE) ;
        }

        ReadFile1(MP_FILE) ;
        fclose(MP_FILE) ;

        printf("nn") ;
        return 0 ;

}




OK.

void ReadTxt(char FTxt[])
{
        int i ;
        int c ;

        for (i = 0  ;  ((c = getchar()) != 'n')  ;  i++)
        {
                if (i < CMAXCAR)
                        FTxt[i] = c ;
                else
                {
                        while(getchar() != 'n' ) ;
                        break ;
                }
        }
        FTxt[i] = '' ;

}

void ReadSaveToFile(FILE *FP_FILE)
{
        char MFBuff[CMAXCAR] ;
        char MFPrePere[CMAXCAR] ;
        char MFPreMere[CMAXCAR] ;
        char MFPreEnf[CMAXCAR] ;



Est-il bien utile de séparer ces tableaux ?

        char rep1 ;



Il est rare d'utiliser un char tout seul. Seul cas connu : scanf()
avec "%c".

        int nb_rec, i ;
        long int nb_enf ;



long int pour un nombre d'enfants, c'est un peu paranoïaque, non ? int
garantit 32767, c'est déjà pas mal, non ?


        rep1 = 'x' ;
        nb_rec = 0 ;
        do
        {
                printf("nEnregistrement (o/n) ? ") ;
                rep1 = getc(stdin) ;
                while (getchar() != 'n') ;



Bien, le vidage. rep1 devrait être de type int (le type retourné par
getc(). Est-ce si difficile de lire la doc des fonctions avant de les
utiliser ?)


                if (rep1 ==  'o'  &&    nb_re c < CMAXREC)
                {
                        printf("nPrenom du pere : ") ;
                        ReadTxt(MFPrePere) ;
                        printf("nPrenom de la me re : ") ;
                        ReadTxt(MFPreMere) ;




C'est bien d'avoir fait une fonction de saisie.

                        fprintf(FP_FILE, "%sn%s n", MFPrePere, MFPreMere) ;



Comme on enregistre quasiment à la volée, il n'était pas utile de
séparer les chaines de saisie, mais ça a le mérite d'être clair.

On aurait pu faire :

1 - prompt
2 - saisie
3 - enregistrement

par élément.


                        printf("nNombre d'enfant s : ") ;
                        ReadTxt(MFBuff) ;

                        nb_enf = ConvToDigit(MF Buff) ;



Oui, OK.


                        if (nb_enf > 0)
                        {
                                for (i = 0 ; i < nb_enf  ;  i++)
                                {
                                        printf("nEnfant %d : ", i+1) ;
                                        ReadTxt(MFPreEnf) ;
                                        fprintf(FP_FILE, "%sn", MFPreEnf) ;



et voilà la séquence

1 - prompt
2 - saisie
3 - enregistrement

que tu aurais pu faire pour les parents, comme je l'ai indiqué au-
dessus ...

                                        nb_rec++ ;
                                }
                        }

                        nb_rec++ ;
                }



J'aurais mis la question de la répétition ici ...

        } while (rep1 == 'o') ;




J'aurais intégré l'ouverture et la fermeture, et j'aurais passé le no m
du fichier en paramètre. Ça fait un bloc autonome qui ne doit rien à
personne...

}

long int ConvToDigit(char FTxt[])
{
        char *p ;
        long int rslt ;

        rslt = strtol(FTxt, &p, 10) ;



SI tu n'utilises pas p, autant mettre :

rslt = strtol(FTxt, NULL, 10) ;

Sinon, i faut apprendre à utiliser p correctement (lire la doc, poser
des questions précises ...). Il peut servir déterminer si la saisie a
été correcte.

        return rslt ;

}

void ReadFile1(FILE *FP_FILE)
{
        int c ;

        while ((c = getc(FP_FILE)) != EOF)
                putchar(c) ;

}



OK. C'est une lecture 'brute' du fichier, mais c'est correct. J'aurais
intégré l'ouverture et la fermeture, et j'aurais passé le nom du
fichier en paramètre. Ça fait un bloc autonome qui ne doit rien à
personne...


void ReadFile2(FILE *FP_FILE)  /* Pour info : ca ne marche car le nbr
de prenom enfant est variable donc different de 3 (variable déclarée)
*/
{
        char FPrePere[CMAXCAR] ;
        char FPreMere[CMAXCAR] ;
        char FPreEnf[CMAXCAR] ;

        while (fscanf(FP_FILE, "%s%s%s", FPrePere, FPreMere, FPre Enf) == 3)
                printf("n%sn%sn%s", FPrePere, FPreMere ,FPreEnf) ;

}



Si tu veux ce type d'info, il faut organiser ton fichier de façon à
retrouver logiquement les informations à une place bien précise.

Il faut organiser le fichier en structures (famille) et en champs
(père, mère, nb-enfants, liste d'enfants). C'est surtout un problème
de conception.

Sinon, c'est pas mal du tout. (ça manque cependant de
sécurisation ...)
Antoine Leca
Le #22675121
écrivit :
Pour une fois, ce code fonctionne.



Je suis content de voir que tu as fait des progrès. Continue !

void ReadSaveToFile(FILE *FP_FILE)
printf("nEnregistrement (o/n) ? ") ;
rep1 = getc(stdin) ;
while (getchar() != 'n') ;



Celui-là est un piège classique : si l'utilisateur tape directement la
touche d'entrée ('n'), rep1 reçoit cette valeur, puis le programme va
se bloquer sur le getchar() suivant, et l'utilisateur ne comprendra pas
ce qui se passe.
Il ne faut donc pas entrer dans la boucle while() dans ce cas-là.
Compare avec la fonction ReadTxt(), où le piège n'intervient pas.


long int ConvToDigit(char FTxt[])
{
char *p ;
long int rslt ;

rslt = strtol(FTxt, &p, 10) ;



Logiquement, ici tu devrais vérifier que p est > FTxt (ce qui
indiquerait une entrée invalide) et que *p vaut le '' final (le cas
contraire indiquant que des caractères supplémentaires auraient été
introduits).


return rslt ;
}


void ReadFile2(FILE *FP_FILE) /* Pour info : ca ne marche car le nbr
de prenom enfant est variable donc different de 3 (variable déclarée)



Oui. La solution logique est donc d'enregistrer ce nombre dans le
fichier, par exemple après le prénom des parents et avant ceux des enfants.

while (fscanf(FP_FILE, "%s%s%s", FPrePere, FPreMere, FPreEnf)



Non : cela ne passe pas s'il y a des espaces dans les prénoms.
En effet, si tu entres (après la réduction à 20 caractères)
Louis-Ferdinand
Marie-Josèphe Caroli
Marie Zéphirine
Louis Joseph Xavier
Xavier Marie Joseph
Louis Auguste
Louis Stanislas Xavi
Charles Philippe
Marie-Adélaïde Cloti
Élisabeth Philippine
la spécification %s, qui s'arrête sur les caractères d'espacement
(espace, tabulations ou saut de lignes) va découper cela en
1er-> Louis-Ferdinand
2e -> Marie-Josèphe
3e -> Caroli
4e -> Marie
5e -> Zéphirine
6e -> Louis
7e -> Joseph
8e -> Xavier
9e -> Xavier
10e-> Marie
11e-> Joseph
12e-> Louis
13e-> Auguste
14e-> Louis
15e-> Stanislas
16e-> Xavi
17e-> Charles
18e-> Philippe
19e-> Marie-Adélaïde
20e-> Cloti
21e-> Élisabeth
22e-> Philippine
qui n'est pas ce que tu veux...
Solutions : soit abandonner scanf, soit utiliser des trucs plus
compliqués comme [^n], ou encore filtrer les espaces (ici, on pourrait
accepter de mettre des virgules).


Antoine
Publicité
Poster une réponse
Anonyme