Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

fgets fin de fichier n

12 réponses
Avatar
Nico
salut,


Soit un fichier
ligne1\n
ligne2\n
ligne3[EOF]

dont chaque ligne a un nombre variable et inconnu de caractères.
Je cherche a lire ligne par ligne le fichier, et a stocker dans un
tableau de chaine ces lignes. Chaque chaine du tableau doit avoir la
bonne taille, pas trop grande.

Mon code marche pour les n-1 premières lignes, car je détecte et
supprime le '\n' conservé par fgets, le problème est pour la dernière
ligne... voici mon code, que me proposez vous ?

char **
str_readnline(FILE *pf,unsigned int n)
{
char ligne[10];
char **tbuf=NULL;
char *buf = NULL,*buf_tmp = NULL,*p = NULL;
int cpt=0,sizet=0,size,cptl=0;

tbuf = malloc(n*sizeof(*tbuf)); /*alloue le tableau de chaines*/
if(tbuf)
{
while(fgets(ligne,sizeof(ligne),pf))
{
p = strchr(ligne,'\n'); /*recherche le \n*/

if(p && !cpt) /*on l'a trouve du premier coup*/
{
*p = 0; /*on le supprime*/
size = strlen(ligne); /*recupere la taille*/
buf = malloc(size); /*alloue un buffer de la taille qui va bien*/
if(buf)
{
strcpy(buf,ligne);
tbuf[cptl] = buf;
cptl++;
cpt = 0;
}
}
else if(p && cpt) /*on l'a trouve, mais pas au 1er coup*/
{
*p = 0;
size = strlen(ligne); /*recupere la taille*/
buf_tmp = realloc(buf,sizet + size);
if(buf_tmp)
{
buf = buf_tmp;
strcat(buf,ligne);
tbuf[cptl] = buf;
cptl++;
cpt = 0;
}
}
else if(!p && !cpt) /*1er coup, pas trouve*/
{
buf = malloc(sizeof(ligne)); /*on recopie dans un buffer*/
if(buf)
{
strcpy(buf,ligne);
sizet += sizeof(ligne);
cpt++;
}
}
else if(!p && cpt) /*pas trouve n-ieme coup*/
{
buf_tmp = realloc(buf,sizet + sizeof(ligne));
if(buf_tmp)
{
buf = buf_tmp;
strcat(buf,ligne);
sizet += sizeof(ligne);
cpt++;
}
}
}
}
return tbuf;
}


le problème pour la dernière ligne, c'est que je ne connais pas sa
taille, il est possible qu'elle ne rentre pas dans ma chaine temporaire
'ligne', et qu'il faille plusieurs fgets() pour la lire... or mon moyen
de détection pour la fin d'une chaine c'est justement le \n qui manque
pour celle-ci :(

--
Nico,
http://astrosurf.com/nicoastro
http://nicolas.aunai.free.fr

10 réponses

1 2
Avatar
Nico
Nico avait soumis l'idée :

[...]

le problème pour la dernière ligne, c'est que je ne connais pas sa taille, il
est possible qu'elle ne rentre pas dans ma chaine temporaire 'ligne', et
qu'il faille plusieurs fgets() pour la lire... or mon moyen de détection pour
la fin d'une chaine c'est justement le n qui manque pour celle-ci :(




ok je me corrige tout seul, j'ai rajouté ceci dans le while :


while((ptmp=fgets(ligne,sizeof(ligne),pf)))

et après le while j'ai rajouté :


if(!ptmp)
{
buf_tmp = realloc(buf,sizet + sizeof(ligne));
if(buf_tmp)
{
buf = buf_tmp;
strcat(buf,ligne);
tbuf[cptl] = buf;
cptl++;
cpt=0;
}
}

ce qui me permet de récupérer la dernière chaine stockée juste avant la
lecture du EOF

je trouve pas ma fonction trés jolie :(

--
Nico,
http://astrosurf.com/nicoastro
http://nicolas.aunai.free.fr

Avatar
Nico
Nico avait écrit le 19/12/2004 :


[...]

if(!ptmp)
{
buf_tmp = realloc(buf,sizet + sizeof(ligne));
if(buf_tmp)
{
buf = buf_tmp;
strcat(buf,ligne);
tbuf[cptl] = buf;
cptl++;
cpt=0;
}
}



autant pour moi, quitte a faire un monologue... autant le finir

if(!ptmp)
{
tbuf[cptl] = buf;
cptl++;
cpt=0;
}

--
Nico,
http://astrosurf.com/nicoastro
http://nicolas.aunai.free.fr

Avatar
DINH Viêt Hoà

le problème pour la dernière ligne, c'est que je ne connais pas sa
taille, il est possible qu'elle ne rentre pas dans ma chaine temporaire
'ligne', et qu'il faille plusieurs fgets() pour la lire... or mon moyen
de détection pour la fin d'une chaine c'est justement le n qui manque
pour celle-ci :(


le code me paraît bien complexe, pourquoi ne donnes-tu pas plus
d'abstraction à tout cela ?

je te propose de placer une structure de données :
- string_table
- string_table * string_table_new(void);
- my_string_table_add(string_table * table, my_string * string);
- void my_string_table_free(string_table * table);

- my_string qui te permettra de manipuler les chaînes de caractères de
façon aisées.
- my_string * my_string_new(void);
- my_string_append(my_string * string, char * data);
- my_string_remove_lf(my_string * string);
- void my_string_free(string);

- read_line() qui te permettra de lire une ligne complète dans un
fichier texte. Cette fonction allouera une chaîne de type
my_string.
- my_string * read_line(FILE * f);

ton programme sera ainsi plus clair.

--
DINH V. Hoa,

"tu as bientot 15 ans, faut que tu commences à être autonome" -- jul

Avatar
Nico
DINH Viêt Hoà avait soumis l'idée :


le code me paraît bien complexe, pourquoi ne donnes-tu pas plus
d'abstraction à tout cela ?

je te propose de placer une structure de données :
- string_table
- string_table * string_table_new(void);
- my_string_table_add(string_table * table, my_string * string);
- void my_string_table_free(string_table * table);

- my_string qui te permettra de manipuler les chaînes de caractères de
façon aisées.
- my_string * my_string_new(void);
- my_string_append(my_string * string, char * data);
- my_string_remove_lf(my_string * string);
- void my_string_free(string);

- read_line() qui te permettra de lire une ligne complète dans un
fichier texte. Cette fonction allouera une chaîne de type
my_string.
- my_string * read_line(FILE * f);

ton programme sera ainsi plus clair.



beh je fais déjà pas mal "d'objets" perso, que j'utilise dans des
programmes, et que je stocke dans une biblio perso, cependant, cette
fonction est destinée à n'être lue que par moi... l'utilisateur ne
verrait que son proto, et le char *, comme un objet de plus haut niveau
qu'il est dans le C de base, donc je vois pas trop trop l'intéret de me
casser la tête a encapsuler tout ça dans un my_string...

j'avoue c'est aussi la hâte d'avoir du résultat, et la fénéantise de
refaire tout un module string

--
Nico,
http://astrosurf.com/nicoastro
http://nicolas.aunai.free.fr

Avatar
DINH Viêt Hoà

beh je fais déjà pas mal "d'objets" perso, que j'utilise dans des
programmes, et que je stocke dans une biblio perso, cependant, cette
fonction est destinée à n'être lue que par moi... l'utilisateur ne
verrait que son proto, et le char *, comme un objet de plus haut niveau
qu'il est dans le C de base, donc je vois pas trop trop l'intéret de me
casser la tête a encapsuler tout ça dans un my_string...

j'avoue c'est aussi la hâte d'avoir du résultat, et la fénéantise de
refaire tout un module string


rien ne t'empêche d'utiliser my_string uniquement en interne
puis d'extraire la chaîne obtenue de my_string.
le but de faire cette structure de données est de séparer le problème en
plusieurs parties simples au lieu d'essayer de résoudre un gros problème
assez complexe.

--
DINH V. Hoa,

"tu as bientot 15 ans, faut que tu commences à être autonome" -- jul

Avatar
Etienne de Tocqueville
Nico a écrit sur fr.comp.lang.c :

dont chaque ligne a un nombre variable et inconnu de caractères.
Je cherche a lire ligne par ligne le fichier, et a stocker dans un
tableau de chaine ces lignes. Chaque chaine du tableau doit avoir la
bonne taille, pas trop grande.


En Tcl, ça prend une seule ligne pour faire ça :
set list [split [read $f] 'n']
mais bon, s'il faut vraiment le faire en C... ;-)

Mon code marche pour les n-1 premières lignes, car je détecte et
supprime le 'n' conservé par fgets, le problème est pour la dernière
ligne... voici mon code, que me proposez vous ?


Ton code me parait trop compliqué pour le travail à faire, et en fait je
dois avouer que je n'ai pas compris comment tu déterminais "n" lorsque
tu appelais ta fonction str_readnline

En plus, je trouve la série de "if" superflue, générant du code
redondant. Et il me semble que tu gères mal les lignes de moins de 10
caractères (c'est à dire si "ligne" contient deux "n"). En fait même,
je n'ai pas bien compris ce que tu faisais d ce qui suivait le "n" que
tu venait de lire...

Un truc cloche aussi : tu ne copie pas le '' dans ton tableau, et
comme tu ne stocke nulle part la longueur de la chaine, je ne vois pas
trop comment tu pourrais exploitable le résultat stocké par la suite...

Avatar
Nico
Etienne de Tocqueville a présenté l'énoncé suivant :

En Tcl, ça prend une seule ligne pour faire ça :
set list [split [read $f] 'n']
mais bon, s'il faut vraiment le faire en C... ;-)


ouep


Ton code me parait trop compliqué pour le travail à faire,


ah ?

et en fait je
dois avouer que je n'ai pas compris comment tu déterminais "n" lorsque
tu appelais ta fonction str_readnline


avec la fonction 'file_nbline(FILE *pf)'

En plus, je trouve la série de "if" superflue, générant du code
redondant.


beh oui c'est ce que je voulait dire par "je trouve ma fonction pas
jolie"
cependant 4 cas se présentent

1/ on a lu la ligne du premier coup (< caractères)
on supprime le n et on stocke le resultat, et on incrémente le
compteur de ligne

2/ on a un bout de la ligne, i.e. elle fait plus de 10 carac.
=> on stocke ce qu'on a lu, incrémente un compteur et on repasse
=> le compteur ici sert a savoir si on doit allouer ou réallouer,
copier ou concaténer

3/ on a toujours pas la chaine entière
=> on concatène se qu'on a chopé avec ce qu'on avait déjà avant, on
incrémente le compteur
=>on repasse

4/ on a enfin la chaine, i.e. on a repéré le n, on vire le n,
concatène a ce qu'on avait déjà, et voilà


Et il me semble que tu gères mal les lignes de moins de 10
caractères (c'est à dire si "ligne" contient deux "n"). En fait même,
je n'ai pas bien compris ce que tu faisais d ce qui suivait le "n" que
tu venait de lire...



les lignes de moins de 10 caractères sont chopées du premier coup,
fgets() va lire jusqu'au n, le reste sera lu au prochain fgets, en 1
ou plusieurs coups



Un truc cloche aussi : tu ne copie pas le '' dans ton tableau,


quel 0 ? mon tableau de chaine voit chacune de ses cases assignées par
chacunes des chaines trouvées


et
comme tu ne stocke nulle part la longueur de la chaine, je ne vois pas
trop comment tu pourrais exploitable le résultat stocké par la suite...



bah justement vu que y'a les 0, un strlen() me donne la longueur de
chaque chaine....

enfin euh.. ça marche j'ai fait des tests

--
Nico,
http://astrosurf.com/nicoastro
http://nicolas.aunai.free.fr

Avatar
Etienne de Tocqueville
Nico a écrit sur fr.comp.lang.c :

et en fait je
dois avouer que je n'ai pas compris comment tu déterminais "n" lorsque
tu appelais ta fonction str_readnline


avec la fonction 'file_nbline(FILE *pf)'


Oh la vache ! Tu lis tout le fichier une première fois rien que pour
connaitre le nombre de ligne... Tu pourrais aussi bien conter les lignes
tout en lisant le fichier ! Ca serait d'ailleurs pas mal de terminer ta
liste de ligne dans tbuf par un NULL pour indiquer que c'est la dernière
ligne.

Et il me semble que tu gères mal les lignes de moins de 10
caractères (c'est à dire si "ligne" contient deux "n"). En fait même,
je n'ai pas bien compris ce que tu faisais d ce qui suivait le "n" que
tu venait de lire...


les lignes de moins de 10 caractères sont chopées du premier coup,
fgets() va lire jusqu'au n, le reste sera lu au prochain fgets, en 1 ou
plusieurs coups


Là, c'est moi qui me suis trompé. Il n'y a bien sur rien derrière le
'n' avec fgets. Je ne sais pas pourquoi, mais j'avais en tête que tu
utilisais "read" pour lire le fichier par blocs de 10 caractères.

Un truc cloche aussi : tu ne copie pas le '' dans ton tableau,


quel 0 ? mon tableau de chaine voit chacune de ses cases assignées par
chacunes des chaines trouvées


Ah oui j'ai compris ce qui me chiffonnait ! Effectivement tu copie le
'' dans ton tableau, mais tu n'as pas réservé assez de place pour le
mettre :

size = strlen(ligne); /*recupere la taille*/
buf = malloc(size); /*alloue un buffer de la taille qui va bien*/
strcpy(buf,ligne);

Il faut soit allouer "size+1", soit ne pas copier le "" avec un
"memcpy(buf,ligne,size)" (mais dans ce cas, il faut bien stocker la
taille quelque part)


Avatar
Etienne de Tocqueville
Nico a écrit sur fr.comp.lang.c :

et en fait je
dois avouer que je n'ai pas compris comment tu déterminais "n" lorsque
tu appelais ta fonction str_readnline


avec la fonction 'file_nbline(FILE *pf)'


Oh la vache ! Tu lis tout le fichier une première fois rien que pour
connaitre le nombre de ligne... Tu pourrais aussi bien compter les
lignes tout en lisant le fichier ! Ca serait d'ailleurs pas mal de
terminer ta liste de ligne dans tbuf par un NULL pour indiquer que c'est
la dernière ligne.

Et il me semble que tu gères mal les lignes de moins de 10
caractères (c'est à dire si "ligne" contient deux "n"). En fait même,
je n'ai pas bien compris ce que tu faisais d ce qui suivait le "n" que
tu venait de lire...


les lignes de moins de 10 caractères sont chopées du premier coup,
fgets() va lire jusqu'au n, le reste sera lu au prochain fgets, en 1 ou
plusieurs coups


Là, c'est moi qui me suis trompé. Il n'y a bien sur rien derrière le
'n' avec fgets. Je ne sais pas pourquoi, mais j'avais en tête que tu
utilisais "read" pour lire le fichier par blocs de 10 caractères.

Un truc cloche aussi : tu ne copie pas le '' dans ton tableau,


quel 0 ? mon tableau de chaine voit chacune de ses cases assignées par
chacunes des chaines trouvées


Ah oui j'ai compris ce qui me chiffonnait ! Effectivement tu copie le
'' dans ton tableau, mais tu n'as pas réservé assez de place pour le
mettre :

size = strlen(ligne); /*recupere la taille*/
buf = malloc(size); /*alloue un buffer de la taille qui va bien*/
strcpy(buf,ligne);

Il faut soit allouer "size+1", soit ne pas copier le "" avec un
"memcpy(buf,ligne,size)" (mais dans ce cas, il faut bien stocker la
taille quelque part)


Avatar
Nico
Etienne de Tocqueville a exprimé avec précision :

Oh la vache ! Tu lis tout le fichier une première fois rien que pour
connaitre le nombre de ligne... Tu pourrais aussi bien conter les lignes
tout en lisant le fichier !


c'est pas si halucinant que ça....
bon ok ça pourrait l'être si j'avais 12 millions de lignes...
en général j'en ai... 3 !


Ca serait d'ailleurs pas mal de terminer ta
liste de ligne dans tbuf par un NULL pour indiquer que c'est la dernière
ligne.



sauf si je connais le taille de tbuf

Ah oui j'ai compris ce qui me chiffonnait ! Effectivement tu copie le
'' dans ton tableau, mais tu n'as pas réservé assez de place pour le
mettre :

size = strlen(ligne); /*recupere la taille*/
buf = malloc(size); /*alloue un buffer de la taille qui va bien*/
strcpy(buf,ligne);

Il faut soit allouer "size+1", soit ne pas copier le "" avec un
"memcpy(buf,ligne,size)" (mais dans ce cas, il faut bien stocker la
taille quelque part)



ok, c'est de ma faute, je me rapelle jamais que strlen ne compte pas le
''

--
Nico,
http://astrosurf.com/nicoastro
http://nicolas.aunai.free.fr

1 2