Problème: se déplacer dans un fichier - C - Programmation
Marsh Posté le 29-01-2007 à 15:41:29
En C, on peut utiliser les fonctions suivantes :
fopen(...) N.B Attention, préciser si le fichier est binaire ou si c'est du texte (il n'y a pas de gestion des fichiers indexés).
fclose()
Entre les fopen et fclose, faire une boucle until feof() ou jusqu'à avoir trouvé l'info désirée.
Dans la boucle, appeler fgets() pour lire une ligne de texte à la fois (fread() sert généralement pour les fichiers binaires), ou fgetc() pour lire seulement un caractère à la fois.
Après fgets ou fgetc, un pointeur, que l'on ne voit pas, est automatiquement incrémenté.
Pour se déplacer on peut utiliser fseek() (habituellement dans un fichier binaire, rarement dans un fichier texte, parce que ceux-ci ont souvent des longueurs de lignes variables et donc on peut rarement déterminer à priori que la n-ième ligne commence à la poisition x). La fonction ftell() permet de connaitre la position courante.
Marsh Posté le 29-01-2007 à 15:43:35
Qhrim a écrit : Bonjour à tous et à toutes ! |
Je présume que tu utilises des "FILE*"
Bon, pour te déplacer tu as 2 façons de faire
Marsh Posté le 29-01-2007 à 15:50:23
olivthill a écrit : Entre les fopen et fclose, faire une boucle until feof() ou jusqu'à avoir trouvé l'info désirée. |
NON !!!!!
Déjà la boucle "until" n'existe pas en C
Ensuite la fonction "feof()" ne détecte pas la fin de fichier (faudrait quand-même lire le man de temps en temps)
Son but est, une fois que la lecture du fichier s'est terminée, d'indiquer si on a fini de lire parce qu'on est en fin de fichier ou si on a fini de lire pour une autre raison (fichier perdu, disque dur crashé, etc...)
La bonne façon de traiter un fichier est de faire une boucle
while (<fonction_de_lecture>(...) != <valeur_spéciale_quand_il_n'y_a_plus_rien_à_lire> ) |
Pour connaître la valeur spéciale, faut faire un "man" de la fonction de lecture utilisée. Par exemple "fgetc()" renvoie "EOF"; "fread()" renvoie "0"; "fgets()" renvoie "NULL" etc...
Marsh Posté le 29-01-2007 à 15:52:14
Merci a vous deux pour vos réponses ca va déja mieux.
J'utilise un fichier texte, est-ce qu'il existe une fonction permettant d'aller directement a la ligne désiré?
Mon fichier texte je l'ai organiser comme ceci:
1 grille-pain 270,90
2 micro-onde 399,50
etc...
en faite je met "code article prix". Donc mon but va être de retrouver le bon code saisie par l'utilisateur et d'extraire le nom de l'article et le prix mais j'ai vraiment du mal...
Marsh Posté le 29-01-2007 à 15:57:07
Qhrim a écrit : Merci a vous deux pour vos réponses ca va déja mieux. |
En fait, ton éditeur te montre ton fichier ligne par ligne mais dans la réalité, il est écrit "physiquement" comme ceci:
1 grille-pain 270,90<return>2 micro-onde 399,50<return>etc...
C'est l'éditeur qui repère les "<return>" et qui affiche proprement les lignes
T'as la fonction "fgets()" qui te permet de lire une ligne entière (elle s'arrête à chaque "return" ).
Personnellement, je ferais comme ceci
lire une ligne avec "fgets()" => la fonction te stocke ta ligne dans une zone de travail
faire un "sscanf()" avec cette zone pour en extraire
comparer le code récupéré avec le code que tu cherches et si c'est correct afficher l'ensemble
Marsh Posté le 29-01-2007 à 16:05:01
Trés bonne démarche je vais essayer merci beaucoup !!!
On ne peut pas faire une fonction qui detecte les <return> aussi? comme ça si l'utilisateur rentre le code "5" on detecte 5 return avant de faire fgets et sscanf non?
Marsh Posté le 29-01-2007 à 16:23:58
Qhrim a écrit : Trés bonne démarche |
Oui ben je suis un peu habitué....
Qhrim a écrit : On ne peut pas faire une fonction qui detecte les <return> aussi? |
ben le pb c'est que pour détecter un "<return>" faut d'abord le lire => ensuite tu peux plus le relire puisque t'as dépassé la zone => retour en arrière avec "fseek()" => devient trop compliqué non ???
Marsh Posté le 29-01-2007 à 16:29:11
Oui je vais garder ta méthode c'est vrai que ca a l'air plus simple
Mais j'ai une question, quand on fait fgets(), je le stock ou? dans une chaîne de caractères?
Et je n'ai pas trés bien compris a quoi sert sscanf()...
Marsh Posté le 29-01-2007 à 18:11:47
Qhrim a écrit : Mais j'ai une question, quand on fait fgets(), je le stock ou? dans une chaîne de caractères? |
Ben oui (man fgets) => le premier paramètre est l'adresse de la zone où stocker => faut bien entendu que de ton coté tu aies une zone assez grande pour stocker une ligne dont tu ne connais pas la taille à l'avance => tu peux donc "estimer" qu'aucune ligne ne dépassera 512 octets (ou 1024 ou 2048) et tu définis une zone de cette taille.
Si vraiment tu veux traiter un jour un fichier dont tu ne peux absolument pas estimer la taille max d'une ligne, alors tu peux utiliser la fonction "getline()" qui s'adapte à toute longueur de ligne => c'est la fonction qui alloue elle-même l'espace nécessaire pour stocker la ligne lue => à toi de libérer l'espace (free()) quand tu n'en as plus besoin...
Marsh Posté le 29-01-2007 à 18:20:10
Donc la fonction fgets() contient 3 paramètres:
- la chaîne de caractère dans laquelle je vais mettre la ligne,
- Le nombre de caracères lu, c'est la le problème je met quoi? car je ne connait pas la longueur de la ligne,
- Le fichier a lire,
C'est bien ca?
Marsh Posté le 29-01-2007 à 18:40:59
Qhrim a écrit : Donc la fonction fgets() contient 3 paramètres: |
Presque. le 2° paramètre indique la longueur de ta zone de stockage. Si la ligne est plus courte c'est pas grave, mais si elle est plus longue elle sera tronquée pour ne pas dépasser la zone de stockage. A toi d'estimer quelle peut être la longueur max d'une ligne pour tailler ta zone en conséquence. Par exemple, si ton article fait 20 caractère max (parce que ce fichier a été écrit par un autre programme où c'est défini comme ça), alors ta ligne ne fera que
Donc au total 34. Si tu tailles ta zone à 128 ou 256 tu ne demandes pas trop de mémoire et tu as de grandes chances de ne jamais avoir de ligne qui dépasse. Ensuite tout dépend de la criticité de ton programme. Si tu dois faire un programme pour une centrale nucléaire, vaut mieux éviter de faire trop d'estimations et essayer de controler au maximum tes inconnues. Si tu travailles pour un TP de cours, ça peut aller...
Marsh Posté le 29-01-2007 à 18:46:45
Oui je vais mettre 128 je serais tranquille mais j'espère que la prof ne va pas aller s'amuser a modifier la longueur de la ligne
Merci en tout cas Sve@r j'ai apprit plein de chose grace a toi
Marsh Posté le 29-01-2007 à 19:29:04
Qhrim a écrit : Merci en tout cas Sve@r j'ai appris plein de chose grace a toi |
C'est parce que j'ai été le premier à répondre. Mais quand ce sera Harkonnen ou Elmoricq ou Taz ou Joel F ou Delahaye qui te répondront, là tu connaitras la vraie signification du verbe "apprendre"... (quoique Taz soit plus un fan de la cat. C++ que de la cat. C...)
Marsh Posté le 30-01-2007 à 10:49:58
Le plus simple pour la structure du fichier est de séparer les champs de l'article par un \; ou un caractère dont tu es sûr qu'il n'apparaitra pas dans la composition des champs, c'est plus sur qu'un espace. Renseigne toi sur le format CSV.
C'est plus facile à lire, un fgets suivi d'un strtok pour la recherche des champs
Marsh Posté le 30-01-2007 à 11:38:17
Trap D a écrit : Le plus simple pour la structure du fichier est de séparer les champs de l'article par un \; ou un caractère dont tu es sûr qu'il n'apparaitra pas dans la composition des champs, c'est plus sur qu'un espace. Renseigne toi sur le format CSV. |
Il n'est pas maître de sa structure, c'est un fichier qu'il reçoit de l'extérieur...
Trap D a écrit : C'est plus facile à lire, un fgets suivi d'un strtok pour la recherche des champs |
Non
Quand on a une entrée formatée de type "géométrie fixe", sscanf() est fait pour ça. C'est quand-même mieux de mettre un seul sscanf qu'une boucle while() strtok()
De plus, strtok est hyper dangereuse du fait qu'elle utilise un pointeur statique pour mémoriser les appels successifs. Donc si tu imbriques deux strtok dans deux boucles différentes, il y aura mélange.
Maintenant, si tu as une entrée de type "géométrie variable", alors on peut se pencher sur la solution de la boucle strtok(). Mais alors il vaut mieux lui préférer "strtok_r()" où c'est toi qui gère la mémorisation du pointeur courant....
Marsh Posté le 30-01-2007 à 13:28:10
Effectivement il n'est pas maître de la structure du fichier, mais perso je n'aime pas le sscanf et je préfère manipuler les chaines moi-même avec strtok (ou sa version safe strtok_r).
Marsh Posté le 30-01-2007 à 19:15:13
Trap D a écrit : ...perso je n'aime pas le sscanf et je préfère manipuler les chaines moi-même avec strtok (ou sa version safe strtok_r). |
Ben c'est comme si tu me disais "je n'aime pas strcpy() et je préfère copier les chaînes avec memcpy()". Même si c'est possible, c'est plus "bizarre".
J'utilise souvent sscanf qui est totalement fiable si l'entrée qu'on lui fournit est bien formatée...
Marsh Posté le 30-01-2007 à 23:19:50
Bonsoir,
Voila j'ai quasi terminer mon programme ^^ a la fin je dois afficher la facture complète, article, quantité etc...
j'ai donc tout stocker dans une structure.
Le problème est que quand j'affiche le nom de l'article, il m'affiche <null> à la place et je n'arrive pas a voir d'ou ca vient :s
Marsh Posté le 31-01-2007 à 00:29:36
Qhrim a écrit : Mon problème est que je n'arrive pas du tout à me déplacer dans le fichier je sais pas comment on fait et on a aucun cours ! |
Tu n'as pas de livre de C ? Il y a des références de cours et de livre en ligne sur mon site.
Marsh Posté le 31-01-2007 à 00:33:52
Qhrim a écrit : Trés bonne démarche je vais essayer merci beaucoup !!! |
fgets() fait une lecture par ligne (si le tableau est suffisamment grand...). Il suffit de les compter. Mais il est rare que les codes articles soient consécutifs, ni même triés. Il vaut mieux être indépendant et tester chaque ligne. C'est pas long...
Marsh Posté le 31-01-2007 à 00:34:59
Emmanuel Delahaye a écrit : Tu n'as pas de livre de C ? Il y a des références de cours et de livre en ligne sur mon site.
|
En faite j'ai enfin réussi a piger la lecture de fichier (je n'en suis pas encore a l'écriture), mais la j'ai un tout autre problème bien différent!!
En effet, si j'effectue un printf de la chaîne de caractère qui contient mon article comme ceci:
printf("L'article choisi est %s",fact[i].art);
il m'affiche <null> a la place de l'article
Marsh Posté le 31-01-2007 à 00:35:46
Emmanuel Delahaye a écrit : fgets() fait une lecture par ligne (si le tableau est suffisamment grand...). Il suffit de les compter. Mais il est rare que les codes articles soient consécutifs, ni même triés. Il vaut mieux être indépendant et tester chaque ligne. C'est pas long... |
C'est ce que j'ai fait a l'aide d'un while qui detecte le bon code et ca marche bien maintenant
Marsh Posté le 31-01-2007 à 00:35:58
Qhrim a écrit : Donc la fonction fgets() contient 3 paramètres: |
WTF ? La taille du tableau de char concerné par le premier paramètre. C'est écrit dans la doc, non ? Faut pas inventer. Faut lire...
Marsh Posté le 31-01-2007 à 00:37:36
Sve@r a écrit : C'est parce que j'ai été le premier à répondre. Mais quand ce sera Harkonnen ou Elmoricq ou Taz ou Joel F ou Delahaye qui te répondront, là tu connaitras la vraie signification du verbe "apprendre"... (quoique Taz soit plus un fan de la cat. C++ que de la cat. C...) |
Ne fais pas le modeste. Tu expliques très bien des choses justes.
Marsh Posté le 31-01-2007 à 00:39:36
Qhrim a écrit : Voila j'ai quasi terminer mon programme ^^ a la fin je dois afficher la facture complète, article, quantité etc... |
Ca veut dire que tu as passé une valeur valant NULL à printf(). C'est mal. Montre ton code.
Marsh Posté le 31-01-2007 à 00:42:00
Code :
|
Marsh Posté le 31-01-2007 à 00:45:11
Emmanuel Delahaye a écrit : WTF ? La taille du tableau de char concerné par le premier paramètre. C'est écrit dans la doc, non ? Faut pas inventer. Faut lire... |
j'utilise des pointeurs je définie pas la taille .
Marsh Posté le 31-01-2007 à 00:50:00
Qhrim a écrit : j'utilise des pointeurs je définie pas la taille . |
La taille ne va pas se définir toute seule comme par magie.
Il faut passer l'adresse d'un bloc ayant une taille déterminée. Soit tu utilises un tableau
fgets(line, sizeof line, fp); |
soit tu utilises un pointeur initialisé avec l'adresse d'un bloc valide :
Code :
|
qu'il faut bien sûr penser à libérer...
Marsh Posté le 31-01-2007 à 00:54:19
Ah et bien la prof nous avait pourtant clairement expliquer qu'un pointeur s'adaptait a la taille demander...
Enfin merci c'est déja plus clair maintenant !
reste toujours ce problème de <null>...
Marsh Posté le 31-01-2007 à 00:58:13
Qhrim a écrit : Ah et bien la prof nous avait pourtant clairement expliquer qu'un pointeur s'adaptait a la taille demander.. |
Change de prof, et vite. Je donne des cours de C, des vrais, ça t'intéresse ?
Marsh Posté le 31-01-2007 à 01:05:29
Emmanuel Delahaye a écrit : Change de prof, et vite. Je donne des cours de C, des vrais, ça t'intéresse ? |
Elle nous a aussi dit qu'il fallait allouer un espace mémoire du genre:
new char[128];
pas trop compris ^^
Marsh Posté le 31-01-2007 à 09:48:23
Qhrim a écrit : Elle nous a aussi dit qu'il fallait allouer un espace mémoire du genre: |
Ca c'est du C++. Rien à voir avec le C. Mais c'est quoi cette école ?
Marsh Posté le 31-01-2007 à 17:17:31
Emmanuel Delahaye a écrit : Ca veut dire que tu as passé une valeur valant NULL à printf(). C'est mal. Montre ton code. |
Mais non c'est pas "mal". printf sait gérer ce genre de cas donc ça va (encore)
Emmanuel Delahaye a écrit : Ne fais pas le modeste. Tu expliques très bien des choses justes. |
Qhrim a écrit : Ah et bien la prof nous avait pourtant clairement expliquer qu'un pointeur s'adaptait a la taille demander... |
Un pointeur ne s'adapte jamais à quoi que ce soit. Ce n'est qu'une variable qui contient un simple nombre, rien de plus. Sauf que ce nombre est en fait une adresse mémoire (donc un n° de case mémoire) et à cette adresse, il y a une valeur (forcément puisque toute case mémoire contient une valeur) que tu peux récupérer grace à l'opérateur "étoile".
Va récupérer mon cours de ici http://fr.lang.free.fr/cours/Langa [...] e_v2.0.pdf il y a un gros chapitre très détaillé sur les pointeurs...
Qhrim a écrit : Elle nous a aussi dit qu'il fallait allouer un espace mémoire du genre: |
"allouer" signifie "réserver". Dans cet exemple, tu réserves de façon dynamique 128 octets qui seront attribués à la variable (dont le nom n'est pas affiché ici). Ca sert à demander au système de te donner des cases mémoires en dynamique (si par exemple le nombre de cases nécessaires est issu d'un calcul, tu ne peux pas utiliser de tableaux car tu ne sais pas, au moment où tu écrits ton code, de combien t'auras besoin => nécessaire alors de passer par une allocation).
Mais comme l'a dit Emmanuel, l'instruction "new" est une instruction C++ et son "équivalent" C est la fonction "malloc()" (Memory ALLOCate)
Marsh Posté le 31-01-2007 à 19:11:31
Sve@r a écrit : Mais non c'est pas "mal". printf sait gérer ce genre de cas donc ça va (encore) |
Non. A ma connaissance (je vais quand même vérifier), ça ne va pas du tout. Le fait que NULL soit géré avec "%s" est un effet direct de la QoI (Quality of Implementation). Pas du tout du langage C pour lequel c'est un comportement indéfini (sauf pour "%p" ). Ce n'est donc pas portable, et il ne faut pas s'amuser à passer des NULL à printf() avec "%s".
Dans la norme, on lit :
Citation : |
Il n'est pas écrit que NULL est accepté (shall indique une obligation dans ce texte, c'est expliqué au début de la norme). Je confirme donc que le comportement est indéfini avec NULL.
Marsh Posté le 31-01-2007 à 20:07:40
Emmanuel Delahaye a écrit : Non. A ma connaissance (je vais quand même vérifier), ça ne va pas du tout. Le fait que NULL soit géré avec "%s" est un effet direct de la QoI (Quality of Implementation). Pas du tout du langage C pour lequel c'est un comportement indéfini (sauf pour "%p" ). Ce n'est donc pas portable, et il ne faut pas s'amuser à passer des NULL à printf() avec "%s". |
Yes, je suis d'accord, c'était juste de l'humour. Un bon programmeur est un programmeur qui sait ce qu'il envoie quand il l'envoie.
De toute façon, il y a le bon programmeur, et le mauvais programmeur:
Marsh Posté le 31-01-2007 à 21:33:25
Emmanuel Delahaye a écrit :
|
Il y a malheureusement toujours une marge entre ce qu'un prof explique et ce que l'élève comprend ...
Par contre je suis d'accord qu'il y a un problème avec le new dans un programme C (pas C++ comme je l'avais écrit précédemment)
Marsh Posté le 29-01-2007 à 15:22:24
Bonjour à tous et à toutes !
Pour demain j'ai a rendre un tp sur une nouvelle notion que l'on a pas acquérie encore qui est les fichiers.
Le tp consiste à allez lire des informations dans un fichier .txt pour pouvoir établir une facture pour un client. Le client rentre le code de l'article et le programme va chercher dans le .txt le nom et le prix de l'article en fonction de ce code.
Mon problème est que je n'arrive pas du tout à me déplacer dans le fichier je sais pas comment on fait et on a aucun cours !
Donc une petite aide serait fort sympathique ^^
merci!