> Tous les forums > Forum Autres langages
 Lire un CSV en C
Ajouter un message à la discussion
Page : [1] 
Page 1 sur 1
DrChal
  Posté le 02/06/2006 @ 14:28 
Aller en bas de la page 
Nouvel astucien
Bonjour, Je suis novice en C. Mais comme tout ceux qui se trouve dans une SSII, j'ai été envoyé chez un client pour faire qqch que je ne connais pas. Je dois faire en C sous unix un programme qui doit lire un fichier CSV, par exemple: Col1 Col2 Col3 1 toto 50 2 titi 80 Et je souhaiterai le mettre dans un tableau dynamique pour pouvoir l'utiliser et générer des instruction SQL. Malheureusement, comme je suis null en C. J'ai cafouillé en cherchant par moi même. Est ce que quelqu'un pourrait me donner un exemple, pouvant lire le fichier CSV. le mettre dans un tableau dynamique. Merci d'avance Fred
Publicité
koala01
 Posté le 02/06/2006 à 19:38 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Astucien
Salut, Le mieux, ce serait de commencer par créer une structure (de type "file", "pile" ou "liste") dans laquelle mettre toutes tes informations, selon un principe de [code]typedef struct lecture{ type1 data1; type2 data2; type3 data3; lecture *suivant; }lu; [/code] Le tout, en veillant à ce que tes types et les nombre de caractères autorisés soient cohérent par rapport aux données à récupérer… Par la suite, il te "suffira" de lire soit chaque ligne, et d'utiliser la fonction strtok(source, delimiter), si la ligne est lue en une fois, pour séparer les différentes données… Une fois ta pile/file/liste créée, il deviendra relativement facile d'en sortir des instruction SQL (que tu pourrais tres bien mettre dans un fichier .sql d'exportation [clindoeil]) Ceci dit, je serais étonné que le SGBDR de ton client ne soit pas en mesure d'importer un fichier CSV [question][langue]
DrChal
 Posté le 06/06/2006 à 10:19 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Nouvel astucien
Salut Merci pour ton aide, mais j'ai tjs un problème. Lorsque je mets en mémoire le fichier, j'ai l'impression qu'il écrase à chaque fois toutes les données par la dernière lecture du fichier CSV. Je te mets le code que j'ai écris. J'ai surement oublié qqch. [code] struct s_colonne { int iCol; int iRow; char **cColumnName; char **cValues; }cFieldLine; typedef struct cfield { char **cNom; struct s_colonne t_col[500]; }TAB_REEL; /* Lire les entêtes de colonne */ int getHeaderFile(TAB_REEL *theader, FILE *file,char delimiter ) { long lLenDeb = 0 ; long lPositionFin = 0 ; int iTaille = 0; int iCol = 0; char *header ; char* cName; char *c; char line[LINE_LENGTH]; //lPositionInit = ftell(file); fseek(file, 0, SEEK_SET); /* Compte le Nombre de Colonne */ header = fgets(line, LINE_LENGTH, file); c = header; lLenDeb = strlen(c); iCol = 1; char *input = header, *tok; for (iCol = 1;(tok = getSplitFieldWithDelimiter(&input, delimiter)) != NULL; iCol++) { theader[1].t_col[iCol].cColumnName =(char **)tok; } return iCol; } /* Lire ligne par ligne les champs */ int getDataFile(TAB_REEL *tdata, FILE *file,char delimiter, int iNumRow ) { long lLenDeb = 0 ; long lPositionFin = 0 ; int iTaille = 0; int iCol = 0; char *header ; char* cName; char *c; char line[LINE_LENGTH]; header = fgets(line, LINE_LENGTH, file); c = header; lLenDeb = strlen(c); iCol = 1; char *input = header, *tok; for (iCol = 1;(tok = getSplitFieldWithDelimiter(&input, delimiter)) != NULL; iCol++) { tdata[iNumRow].t_col[iCol].cValues =(char **)tok; } return iCol; free(tdata); } main() { FILE *file; char cFileName[] = "test.csv"; file = fopen(cFileName,"r"); int i, j; int bHasHeader = 1; /* 1 = Entete ; 0 = Pas d'entete de colonne */ int iNbCol = getNbColumns(file,'\ '); int iNbRow = getNbRows(file); int iDebLg = 1; /* Test */ char *header ; char *input = header, *tok; char line[LINE_LENGTH]; /* Fin de Test */ TAB_REEL *p; TAB_REEL *p1; if (bHasHeader == 1) iDebLg = 2; p = (TAB_REEL *)calloc( TAILLE, sizeof(TAB_REEL)*(iNbCol));//*iNbRow) ); if( ! p ) { fprintf( stderr, "Erreur à l'allocation\n\n" ); exit(1); } getHeaderFile(p,file,'\ '); for( i=iDebLg; i <= iNbRow; i++ ) { p[i].cNom =(char **)"Colonne"; p[i].iRow = i ; for (j=1; j<= iNbCol; j++) { p[i].t_col[j].iCol = j; p[i].t_col[j].iRow = i; iCol = 1; } getDataFile(p,file,'\ ', i); } for( i=1; i <= iNbRow; i++ ) { for (j=1; j<= iNbCol; j++) { printf("Nom : %-10.10s - Ligne : %.3d - Colonne : %.2d - Valeur : %-50.50s\n",p[1].t_col[j].cColumnName, p[i].t_col[j].iRow, p[i].t_col[j].iCol, p[i].t_col[j].cValues); } } return 0; } [/code] Je te remercie d'avance de ton aide. Fred
koala01
 Posté le 06/06/2006 à 12:06 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Astucien
Déjà, pourquoi utiliser un pointeur de pointeur dans la structures_colone et dans cfield [question] (force 6 sur [url="http://mapage.noos.fr/emdel/goret.htm"]=>L'échelle de goret<=[/url]) Cela n'a aucun intéret, bien au contraire: typiquement un "pointeur de pointeur" est passé en parametre d'une fonction quand on souhaite que la fonction renvoie une valeur (de réussite/échec, par exepmle) et modifie l'adresse même du pointeur (parce qu'il aurait été réalloué, par exemple) Comme il y a moyen d'obtenir un pointeur de pointeur simplement en demandant "l'adresse du pointeur" contente toi d'un pointeur dans ta structure [clindoeil] Ensuite, je te conseillais une structure "dynamique" pour garder les données récupérée du fichier… Cela permettra d'avoir quelque chose qui pourra fonctionner quel que soit le nombre de colone ou le nombre d'enregistrements, sans nécessiter cet *affreux* struct s_colonne t_col[500]; qui ou bien fera utiliser trop de mémmoire ou bien finra à un moment où un autre par provoquer un dépassement… Les structures en elle-memes pourraient ressembler à [code] struct ListeNomChamps { char *Nom; ListeNomChamps *Suivant; }; struct ListeValeurs { char *Valeur; ListeValeur *Suivant; }; struct ListeEnregistrements { ListeValeurs *PremierEnregistrement; ListeEnregistrements *Suivant; }; [/code] Nom et Valeur sont juste du bon format pour prendre un tableau de caractères, et donc obtenir une chaine de caractère et l'élément Suivant permet à chaque fois de pointer vers… l'élément suivant du meme type que la structure… Dans la fonction main, tu déclares donc cinq pointeurs du genre de [code] ListeNomChamps *Noms; ListeNomChamps *derniernom; ListeEnregistrements *Enregistrements; ListeEnregistrements *dernierenreg; ListeValeurs *dernierevaleur; [/code] et de les initialiser tous les cinq à NULL… -Nom étant destiné à recevoir le nom du premier champs; -derniernom étant un pointeur de travail qui pointera systématiquement vers le dernier nom de la liste, -Enregistrements étant lui destiné à recevoir le premier enregistrement; -dernierenreg étant un pointeur de travailpointant systématiquement vers le dernier enregistrement rajouté… ListeNomChamps -dernierevaleurs étant destiné à servir de pointeur de travail lors de l'ajout des valeurs des différents champs de l'enregistrement Apres ouverture du fichier, il te "suffit", lors de la lecture de la premiere ligne d'allouer la mémoire pour un type ListeNomChamps, de donner la valeur calculée pour le nom du champs à ce nouveau->Nom; et la valeur NULL à nouveau->Suivant; Ceci fait, si Noms vaut NULL, on donne la valeur de nouveau à Noms et à derniernom, sinon, on donne la valeur de nouveau à derniernom->Suivant… Pour les lignes suivantes, tu commencera par allouer la mémoire pour un pointeur de type ListeEnregistrements…(nous le nommerons nouveauenreg [clindoeil] Tu mettra les valeurs de nouveauenreg->Valeurs et de nouveau->Suivant à NULL, avant lecture de la ligne… (n'oublie pas de mettre également la valeur de dernierevaleur à null [clindoeil]) Apres lecture de la valeur de chaque champs: allouer un espace mémoire pour une structure ListeValeurs, et lui donner, comme valeur de Valeur, le pointeur de tableau de caractere qu'on vient de lire si nouveauenre->Valeurs vaut null, donner la valeur de notre nouvelle structure de type ListeValeurs à nouveauenre->Valeurs et à dernierevaleur sinon, donner la valeur de la structure à dernierevaleur->Suivant; Une fois la ligne entierement lue, si Enregistrement vaut null, donner la valeur de nouveauenreg à Enregistrements et à dernierenreg, sinon, donner la valeur de nouveauenreg à denierenreg->Suivant; Recommencer le travail pour chaque ligne…

Modifié par koala01 le 06/06/2006 12:35
AlexPrince
 Posté le 07/06/2006 à 05:37 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Petit astucien
Petite note:
Utilisation de ! au lieu de == NULL dans une expression avec pointeur
Ce n'est pas la même chose. (Ça vient du lien) En fait, après une lecture, il est clair que celui qui a écrit cet article n'y connaît rien au C / C++. Edit: Petite liste de tout ce que je considère absolument injustifiable de faire part de cette liste:
    Définition de plus d'une variable par ligne. Prototype séparé pour une fonction non exportée (static). Utilisation de ! au lieu de == NULL dans une expression avec pointeur Commentaires en fin de ligne Indentation > 4 espaces Usage de TAB au lieu de SPACE Definition d'une chaine sans 'const'. (la plus stupide...) Ligne de plus de 80 colonnes Commentaires longs en fin de ligne Usage de la 'Notation Hongroise' version Microsoft (avec les types) Fonction de plus d'une page visible (50 à 100 lignes selon l'éditeur) switch de plus de 20 case case de switch de plus de 10 lignes (Vraiment stupide !) Plus d'un return par fonction (wow...) Passage d'une structure en parametre Usage de variables statiques modifiables dans une fonction Définition de code dans un .h
À tout newbie qui passe par ici, ne vous fiez pas à cet article pour juger votre code.

Modifié par AlexPrince le 07/06/2006 05:44
DrChal
 Posté le 07/06/2006 à 08:30 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Nouvel astucien
Salut à tous, Je suis complètement perdu avec tout ça. Je suis vraiment un débutant en C. J'y connais malheureusement pas grand chose. J'ai commencé à faire ce que Koala ma conseillé. [code] struct ListeNomChamps { char *Nom; struct ListeNomChamps *Suivant; }LstFieldName; struct ListeValeurs { char *Valeur; struct ListeValeur *Suivant; }; struct ListeEnregistrements { struct ListeValeurs *PremierEnregistrement; struct ListeEnregistrements *Suivant; }; /* ---------------------------------------------------------------- */ /* Méthode qui découpe un champ selon un séparateur */ /* */ /* Retour un pointeur . */ /* */ /* ---------------------------------------------------------------- */ static char *getSplitFieldWithDelimiter(char **stringp, char delimiter) { char *s, *tok = *stringp; char c; if (tok == NULL) return NULL; for (s = tok;(c = *s);s++) { if (delimiter == c) { *s = 0; *stringp = s+1; return tok; } } *stringp = NULL; return tok; } main() { /* Définition des Structures */ struct ListeNomChamps *Noms; struct ListeNomChamps *Nouveau; struct ListeNomChamps *derniernom; struct ListeEnregistrements *Enregistrements; struct ListeEnregistrements *dernierenreg; struct ListeValeurs *dernierevaleur; /* Fin de définition des Structures */ /* Initialisation des structures */ Noms = NULL; derniernom = NULL; Enregistrements = NULL; dernierenreg = NULL; dernierevaleur = NULL; /* Fin d'initialisation */ FILE *file; char cFileName[] = "test.csv"; file = fopen(cFileName,"r"); char delimiter ="\t"; char *header ; char *input = header, *tok; Nouveau = (LstFieldName *)calloc( Nouveau, sizeof(Nouveau->Nom)*iNbCol ); for (j = 1;(tok = getSplitFieldWithDelimiter(&input, delimiter)) != NULL; j++) { Nouveau->Nom = (char **)tok; Nouveau->Suivant = NULL; printf("Nom : %-10.10s \n", Nouveau->Nom); } return 0; } [quote] JE suis perdu au niveau définition de l'allocation mémoire du tableau dynamique et de comment on affecte les valeurs au tableau. Puis je avoir un exemple ? SVP Merci pour vos aides. Fred
koala01
 Posté le 07/06/2006 à 18:32 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Astucien
AlexPrince a écrit :
Petite note:
Utilisation de ! au lieu de == NULL dans une expression avec pointeur
Ce n'est pas la même chose. (Ça vient du lien) En fait, après une lecture, il est clair que celui qui a écrit cet article n'y connaît rien au C / C++. Edit: Petite liste de tout ce que je considère absolument injustifiable de faire part de cette liste:
Bien que ce ne soit pas moi qui ai écrit cet article, je souhaites me faire l'avocat du diable pour entamer une discution constructive là dessus… Je vais donc me permettre de te donner une "certaine" justification de la plupart des points que tu mets en cause, ce qui nous permettra un échange constructif de points de vue…
Définition de plus d'une variable par ligne.
La définition de plusieurs variables sur une même ligne complique la relecture… Qu'il s'agisse pour quelqu'un de reprendre un code inconnu ou pour celui qui l'a écrit de replancher dessus plusieurs mois apres avoir mis la derniere main au code existant (car la seule chose dont on puisse etre sur dans la vie, c'est qu'on ne peut etre sur de rien), il est beaucoup plus facile de remettre la main sur toutes les variables si elles sont définies sous la forme générique de [code] entier variable_1 eniter variable_2 eniter variable_3 [/code] que quand elles se présentent sous la forme de [code]entier variable_1,variable_2, variable_3[/code] et l'écart de facillité/difficulté ne fait que s'accroitre en meme temps que la complexité des noms et du nombre des variables mises sur une meme ligne…
Utilisation de ! au lieu de == NULL dans une expression avec pointeur
Un pointeur non alloué devrait systématiquement etre initialisé à NULL, qui est d'ailleurs la valeur renvoyée lorsque l'allocation échoue… L'utilisation de ! peut passer inaperçue dans le code si la personne qui le relit est ne serait-ce qu'un peu distraite au moment où elle le fait… mais son absence modifie radicalement le test… Et le "relecteur" risque alors purement et simplement de croire à une erreur de logique… L'utilisation de ==NULL a au moins l'avantage de signifier explicitement qu'on teste si le pointeur est (ou plutot n'est pas) alloué…
Commentaires en fin de ligne
Un commentaire n'a de sens qu'à partir du moment où il précède ce sur quoi il fournit une précision… Il est en effet beaucoup plus logique de savoir ce que va faire le code qui suit plutot que d'obtenir, a posteriori, une information sur du code qu'on a déjà parcourru mais non compris… La seule exception que j'y trouve est le commentaire indiquant quel niveau de bloc d'instruction on ferme quand l'ouverture de celui-ci est réellement fort écartée de la fermeture…
Indentation > 4 espaces
Le seul avantage de l'indentation d'un code est, encore une fois, d'en faciliter la relecture, en repérant facilement le début et la fin de chaque bloc… Bien que je ne sois pas forcément catégorique sur le nombre d'espaces à utiliser, un nombre décidément trop petit (1 surement, 2 sans doute, 3 c'est à voir) n'apportera pas cette facilité, et donc ne servira purement et simplement à rien… Autant dans ce cas s'éviter la peine de l'indentation [clindoeil]… mais il ne faudra pas s'étonner, dans ce cas, qu'on éprouve des difficultés à retrouver le début (ou la fin) d'une boucle ou d'un test[clindoeil]
Usage de TAB au lieu de SPACE
(je ne suis en effet pas forcément d'accord avec ce principe, mais)N'importe quel éditeur de texte est en mesure de retrouver deux espaces ou plus dans un texte, alors qu'il est très difficile de retrouver un caractère tabulation (dont, de plus, la taille peut varier d'un réglage à l'autre, et donc avoir un effet désastreux, du point de vue de la relecture, si le fichier n'est pas réouvert avec le bon réglage)… L'utilisation de l'espace facilite donc la recherche par rapport à la tabulation et s'assure que l'ensemble de l'indentation reste cohérent quel que soit le réglage ou l'éditeur utilisé pour réouvrir le fichier du code source…
Ligne de plus de 80 colonnes
Classiquement, un affichage "console" se fait sur 25 lignes de 80 colones… Cela signifie que tout caractère supplémentaire sera affiché après un retour à la ligne, ce qui rendra dans bien des cas le travail d'indentation du code inutile si l'on demande l'affichage du code dans la console (et sous linux c'est pratique courrante [clindoeil]) Sans compter que le retour à la ligne pourrait alors se produire à n'importe quel moment, y compris au beau milieu du nom d'une variable ou d'une fonction, et que cela compliquera énormément la relecture [clindoeil]
Commentaires longs en fin de ligne
cf les commentaires en fin de ligne…
Fonction de plus d'une page visible (50 à 100 lignes selon l'éditeur)
(je ne suis en effet pas forcément d'accord avec ce principe, mais)il peut sembler réellement intéressant de pouvoir avoir l'ensemble des actions entreprises par la fonction "d'une traite" sous les yeux, sans devoir faire monter/descendre l'affichage de celle-ci en se demandant où l'on veut arriver… De plus, il est beaucoup plus facile de s'y retrouver sur 5 fois 50 lignes plutot que sur 1 fois 250 lignes, entre autres en période de débuggage… Il est aussi difficile d'imaginer que, sur ces plus de 100 lignes, la premiere ligne vise (en dehors du but propre à la fonction, cela va de soi) un but qui ne pourra etre atteint qu'à la 100eme… Pourquoi ne pas "scinder" cette fonction en plusieurs autres plus petites qui permettront sans doute de mieux s'y retrouver [question]
case de switch de plus de 10 lignes (Vraiment stupide !)
(je ne suis en effet pas forcément d'accord avec ce principe, mais)S'il y a tellement de choses que cela à faire pour ce cas, pourquoi ne pas créer une fonction qui remplirait l'office [question]
Plus d'un return par fonction (wow...)
Une fonction n'est pas une autoroute qui doit permettre de sortir à Quebec ou à Montreal, mais doit etre prise comme un chapitre de livre: avec une seule entrée (ca, c'est d'office) et une seule sortie… Certains (anciens) compilateurs rechignent d'ailleurs à compiler les fonctions avec plusieurs return… D'autant plus qu'il est très simple d'y arriver quand on a créé un algoritme *correct* pour cette fonction…
Passage d'une structure en parametre
Passer une structure en parametre créera une copie de travail de cette structure propre à la fonction, qui sera détruite lorsque la fonction sera quittée, sans avoir modifié l'original… Le fait de disposer d'une structure passée en paramètre pourrait donner la fausse impression que l'on modifie les valeurs de la structure originale plutot que la structure de copie… C'est sans compter sur le fait que, si l'on ne passe que la structure du premier élément d'une structure dynamique (pile, file, liste etc), on ne passe qu'un seul élément, et non l'ensemble de la structure dynamique… Essayer d'accéder à Liste->Suivant ou à Pile->Precedant dans la fonction risque d'amener droit à la catastrophe… Passons plutot un pointeur ou une référence sur la structure… Cela aura en plus l'avantage d'économiser un tout petit peu la mémoire…
À tout newbie qui passe par ici, ne vous fiez pas à cet article pour juger votre code.
Encore une fois, tu arrives avec tes avis pris "à l'emporte piece", mais tu ne prends, malheureusement, pas la peine de les justifier…

Modifié par koala01 le 07/06/2006 18:42
breizhbugs
 Posté le 07/06/2006 à 19:18 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Astucien
Salut koala, Dis donc quand apprendra tu a faire des post de taille raisonnable! c'est que c'est long a lire toussa [clindoeil] Bon y a des choses que je suis d'accord et d'autre moins. Je vais pas tout detailler mais voila 2/3 trucs: -commentaire en fin de ligne: je les utilise quand je dois faire une precision sur ce qui est en partie droite pour justifier un nombre magique par exemple ou une subtilité. Pour commenter un bloc(fonction, if, for...), je fais le commentaire(s'il tient sur une ligne, 2 max) apres la parenthese fermante et avant l'accolade ouvrante. - accolades que je mets toujours sur une nouvelle ligne d'ailleurs. Par commentaire j'entends quelque chose qui aide a la comprehension de l'algo, pas un truc qu'on peut mettre dans une doc genre javadoc! -TAB au lieu de SPACE. Franchement au debut que je programmais je mettais 2 espaces. Je crois que c'est VS qui m'a fais passer aux tabulations. Je pense que quand l'EDI est bonne autant mettre des TAB ca va plus vite. Enfin je dirais aux newbies de faire un code qui marche d'abord, ils comprendront ensuite plus facilement l'importance de la mise en page lorsque leurs projets prendront de l'ampleur. (c'est plus facile d'expliquer a quelqu'un pourquoi son switch...case est illisible quand il sait comment le switch...case fonctionne!, enfin c'est mon avis!) Mais ce post n'est peut etre pas le meilleur endroit pour debattre de cela pour ne pas trop pourrir le post de DrChal...
AlexPrince
 Posté le 07/06/2006 à 19:23 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Petit astucien
J'ai envoyé un mail à l'administrateur du site et il a fait certaines modifications. C'est avec lui que je me suis expliqué et non pas avec vous.
Publicité
koala01
 Posté le 07/06/2006 à 19:59 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Astucien
breizhbugs a écrit :
Salut koala, Dis donc quand apprendra tu a faire des post de taille raisonnable! c'est que c'est long a lire toussa [clindoeil]
Désolé, je me laisses toujours avoir par ma verve… Pourtant j'essaye, je te jures [clindoeil]
-commentaire en fin de ligne: je les utilise quand je dois faire une precision sur ce qui est en partie droite pour justifier un nombre magique par exemple ou une subtilité.
S'il s'agit de "rappeler" que "ceci vient de là" je peux le consevoir… S'il s'agit de donner une explication sur le code, j'aime autant savoir à quoi il servira avant de le lire [clindoeil]
Pour commenter un bloc(fonction, if, for...), je fais le commentaire(s'il tient sur une ligne, 2 max) apres la parenthese fermante et avant l'accolade ouvrante. - accolades que je mets toujours sur une nouvelle ligne d'ailleurs.
Si c'est sur une nouvelle ligne, alignée avec la première letre du bloc, c'est merveilleux… Si c'est avec l'indentation suppérieure, personnellement, je n'aimes pas trop [clindoeil][bigsmile]
Par commentaire j'entends quelque chose qui aide a la comprehension de l'algo, pas un truc qu'on peut mettre dans une doc genre javadoc!
Nous sommes bien d'accord là dessus… la documentation "utilisateur" n'a pas sa place dans un code source…
-TAB au lieu de SPACE. Franchement au debut que je programmais je mettais 2 espaces. Je crois que c'est VS qui m'a fais passer aux tabulations. Je pense que quand l'EDI est bonne autant mettre des TAB ca va plus vite.
As tu remarqué la parenthese [question] Quand je me fais l'avocat du diable, je le fais à fond [clindoeil]… et on peut effectivement trouver des justifications pour les espaces…
Enfin je dirais aux newbies de faire un code qui marche d'abord, ils comprendront ensuite plus facilement l'importance de la mise en page lorsque leurs projets prendront de l'ampleur. (c'est plus facile d'expliquer a quelqu'un pourquoi son switch...case est illisible quand il sait comment le switch...case fonctionne!, enfin c'est mon avis!)
De fait, mais il est généralement plus facile de donner directement de "bonnes" habitudes, plutot que de devoir commencer par faire oublier les "mauvaises" (si tant est qu'une habitude en programmation puisse etre considérée comme "bonne" ou "mauvaise"…)
Mais ce post n'est peut etre pas le meilleur endroit pour debattre de cela pour ne pas trop pourrir le post de DrChal...
Ouppsss…[rougir] Désolé, DrChal[bierre], mais j'ai l'affreuse habitude de réagir "in situ" aux avis que je trouve par trop catégoriques… Principalement s'ils manquent de justification…
AlexPrince a écrit :
J'ai envoyé un mail à l'administrateur du site et il a fait certaines modifications. C'est avec lui que je me suis expliqué et non pas avec vous.
Justement, il serait un minimum logique que tu expliques ton avis sur le site sur lequel tu le donnes… Que tu te sois mis d'accord avec l'administrateur du site est tout à ton honneur, mais le malheur veut que nous n'en avons pas profité, et que l'on se demande toujours quelles motivations tu donnes à tes avis si catégoriques… Quand je donnes mon avis, j'explique ce qui m'a amené (à tord ou à raison) à penser de cette manière… Cela permet au minimum aux autres de se faire une idée de la qualité de l'avis donné et d'entamer un dialogue constructif… Alors que tout avis non justifié a de grande chance d'être considéré, peut etre à tord, mais comment juger, comme une attaque injustifiée…
DrChal
 Posté le 08/06/2006 à 08:48 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Nouvel astucien
Salut, Merci j'ai appris pas mal de chose. Mais je suis toujours bloqué. J'avance dans mon développement. Mais je suis bloqué avec l'appel d'un tableau. J'explique : J'ai un tableau qui contient mes entetes de colonne. Je l'appelle n fois pour lire le nom des colonnes que j'ajoute dans un autre tableau dynamique. J'ai remarqué que la taille du tableau contenant mes entêtes vaut 4. Et lorsque ma boucle qui alimente pour second tableau, arrive au 5 ème rang. Le programme se plante. Et j'ai remarqué en regardant le contenu de ce premier tableau. j'ai n'importe quoi dedans alors que les 4 premières passes marchent nickel. Je laisse mon code pour voir si j'ai oublié qqch: [code] typedef struct cFileCSV { int iRow; /* Numéro de la ligne */ int iCol; /* Numéro de la Colonne */ char *cColumnName; /* Nom de la colonne */ char *cValues; /* Valeurs de la colonne*/ struct cFileCSV *next; /* Pointeur sur l'élément suivant. */ }cFieldLine; typedef cFieldLine *t_CSV; /* Type de liste de de valeurs du fichier CSV. */ struct cFileCSV TAB_REEL[iNB_COL]; /* Fonctions de gestion du fichiers CSV */ /* Fonction d'initialisation d'une liste de données dans un fichier CSV. La liste est passée par variable pour permettre son initialisation. */ void init_list(t_CSV *lst) { *lst = NULL; } /* Fonction d'ajout d'une ligne du fichier CSV. Les paramètres de la personne sont passés par variables, mais ne peuvent être modifiés car ils sont constants. Ce sont des chaînes de caractères C, qui sont donc assimilées à des pointeurs de caractères constants. */ int add_line(t_CSV *lst,int iRow, int iCol, const char *cColName, const char *cValeurs) { /* Crée un nouvel élément : */ cFieldLine *p = (cFieldLine *) malloc(sizeof(cFieldLine)); if (p != NULL) { p->iRow = (int *) malloc( sizeof(int)); p->iCol = (int *) malloc( sizeof(int)); /* Alloue la mémoire pour le nom de la colonne et la valeur. Attention, il faut compter le caractère nul terminal des chaînes : */ p->cColumnName = (char *) malloc((sizeof(cColName) + 1) * sizeof(char)); p->cValues = (char *) malloc((sizeof(cValeurs) + 1) * sizeof(char)); if (p->cColumnName != NULL && p->cValues != NULL) { /* Copie le nom et la valeur : */ strcpy(p->cColumnName, cColName); strcpy(p->cValues, cValeurs); p->next = *lst; *lst = p; } else { free(p); p = NULL; } } return (p != NULL); } /* Simple fonction d'affichage. */ void print_list(t_CSV const *lst) { cFieldLine const *p = *lst; int i = 1; while (p != NULL) { printf("Ligne %d : %-20.20s (%-10.10s)\n", i, p->cColumnName, p->cValues); p = p->next; ++i; } } /* Fonction de destruction et de libération de la mémoire. */ void destroy_list(t_CSV *lst) { while (*lst != NULL) { cFieldLine *p = *lst; *lst = p->next; free(p->cColumnName); free(p->cValues); free(p->iRow); free(p->iCol); free(p); } return ; } /* Fonction de suppression d'une personne. La structure de la liste est modifiée par la suppression de l'élément de cette personne. Cela peut impliquer la modification du chaînage de l'élément précédent, ou la modification de la tête de liste elle-même. */ int remove_line(t_CSV *lst,int iRow) { /* Recherche la personne et son antécédant : */ cFieldLine *prev = NULL; cFieldLine *p = *lst; while (p != NULL) { /* On sort si l'élément courant est la personne recherchée : */ if (p->iRow == iRow) break; /* On passe à l'élément suivant sinon : */ prev = p; p = p->next; } if (p != NULL) { /* La personne a été trouvée, on la supprime de la liste : */ if (prev == NULL) { /* La personne est en tête de liste, on met à jour le pointeur de tête de liste : */ *lst = p->next; } else { /* On met à jour le lien de l'élément précédent : */ prev->next = p->next; } /* et on la détruit : */ free(p->cColumnName); free(p->cValues); free(p->iRow); free(p->iCol); free(p); } return (p != NULL); } /* Méthode qui compte le nombre de ligne dans un fichier */ int getNbRows(FILE *file) { char line[LINE_LENGTH]; int count=1; /* Compte le nombre de ligne . */ while ( fgets(line, LINE_LENGTH, file) != NULL) count++; return count; } /* Méthode qui compte le nombre de colonne par rapport à un séparateur */ int getNbColumns(FILE *file, char delimiter) { long lPosition = 0 ; int iCol = 0; char *header ; char *c; char line[LINE_LENGTH]; lPosition = ftell(file); /* Compte le Nombre de Colonne */ header = fgets(line, LINE_LENGTH, file); c = header; iCol = 1; while (*c) if (*(c++) == delimiter) iCol ++; return iCol; } /* ---------------------------------------------------------------- */ /* Méthode qui découpe un champ selon un séparateur */ /* */ /* Retour un pointeur . */ /* */ /* ---------------------------------------------------------------- */ static char *getSplitFieldWithDelimiter(char **stringp, char delimiter) { char *s, *tok = *stringp; char c; char cEndString = '\ '; char *Ch1=NULL; int l,i=0; l=strlen(tok); if ((tok == NULL) || (l ==0)) return NULL; Ch1=(char*)malloc(sizeof(char)); for (s = tok;(c = *s);s++) { if ((delimiter == c)||(cEndString ==c)) { *s = 0; *stringp = s+1; return tok; } } *stringp = NULL; return tok; } /* Lire les entêtes de colonne */ int getHeaderFile(t_CSV *theader, FILE *file,char delimiter ) { long lLenDeb = 0 ; int iColumn = 1; char *header ; char *c; char line[LINE_LENGTH]; char cColName; char cName; //lPositionInit = ftell(file); fseek(file, 0, SEEK_SET); /* Compte le Nombre de Colonne */ header = fgets(line, LINE_LENGTH, file); c = header; lLenDeb = strlen(c); //cFieldLine *TAB_REEL = (cFieldLine *) malloc(sizeof(cFieldLine)); char *input = header, *tok; for (iColumn = 1;(tok = getSplitFieldWithDelimiter(&input, delimiter)) != NULL; iColumn++) { if ((strcmp((char *)tok,"\\")!=0) && (tok != NULL)) { add_line(&theader, 1,iColumn, (char *)tok,(char *)tok ); TAB_REEL[iColumn].cColumnName = (char *)tok ; TAB_REEL[iColumn].iRow = 1; TAB_REEL[iColumn].iCol = iColumn; TAB_REEL[iColumn].cValues = (char *)tok ; printf("Colonne : %-5.5s - Taille du Tableau : %d\n" ,TAB_REEL[iColumn].cValues, sizeof(TAB_REEL)); } else return iColumn; } return iColumn; } int main(void) { FILE *file; char cFileName[] = "test.csv"; file = fopen(cFileName,"r"); char delimiter ="\t"; char line[LINE_LENGTH]; /* Crée une liste de données venant du fichier CSV : */ t_CSV p; t_CSV pHeader; init_list(&p); init_list(&pHeader); int i, j; int iNbCol = getNbColumns(file,'\ '); int iNbRow = getNbRows(file); int iDebLg = 1; char *header ; char *input = header, *tok; /* TO DO */ getHeaderFile(pHeader,file,'\ '); cFieldLine *p2 = TAB_REEL; //p2= (cFieldLine *)malloc (sizeof(TAB_REEL)+1); for( i=iDebLg; i <= iNbRow; i++ ) { header = fgets(line, LINE_LENGTH, file); input = header; char *cNameCol; if (p2 == NULL) printf("NULL \n"); if (i >= 5 ) { printf("Taille de P2: %d \n", sizeof(p2)); p2 = NULL; p2 = (cFieldLine *)realloc(p2, sizeof(p2)+1); p2= TAB_REEL; printf("Attention \n"); } for (j = 1;(tok = getSplitFieldWithDelimiter(&input, '\ ')) != NULL; j++) { cNameCol = p2[j].cColumnName printf("Nom : %s \n",cNameCol); add_line(&p, i,j,cNameCol ,(char *)tok ); } } print_list(&p); destroy_list(&p); return 0; } [/code] Merci d'avance. PS: Pour ce qui est des indentations et des commentaires, Désolé [clindoeil]
koala01
 Posté le 08/06/2006 à 14:14 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Astucien
Tes structures sont bien trop compliquées pour l'usage que tu veux en faire… [code] typedef struct cFileCSV { int iRow; /* Numéro de la ligne */ int iCol; /* Numéro de la Colonne */ char *cColumnName; /* Nom de la colonne */ char *cValues; /* Valeurs de la colonne*/ struct cFileCSV *next; /* Pointeur sur l'élément suivant. */ }cFieldLine; [/code] iCol, iRow et next sont inutils… Un simple compteur (pour chaque variable, ca va de soi) dans la fonction de lecture fera tout aussi bien l'affaire que iCol et iRow, et sera bien plus facilement accessible Pour ce qui est de next, une fois la lecture d'un élément finie, le pointeur fichier pointera de toutes manière sur l'élément suivant [clindoeil] En outre, pourquoi refuser l'idée d'avoir une structure pour les noms de colones et une autre uniquement pour les données [question] Tu crées une redondance des informations qui n'aura qu'un seul effet: nécessiter plus de mémoire pour l'exécution de la tache… Or, cette redondance des informations est parfaitement inutile [cliindoeil] [code] typedef cFieldLine *t_CSV; /* Type de liste de de valeurs du fichier CSV. */ struct cFileCSV TAB_REEL[iNB_COL]; [/code] l'utilisation d'un tableau sur une structure dont on ignore à priori le nombre d'occurences (qui force à d'abord compter le nombre d'occurences à gérer, puis à créer le tableau, et enfin à refaire toute une partie du travail pour remplire le tableau) est un PAC (Piege à C[censure]) qu'il vaut mieux réellement éviter… Les risques liés sont très importants: mauvais comptage, dépassement de mémoire lors du remplissage du tableau etc… Une structure dynamique est le meilleur moyen de gérer de nombreuses informations dont on ne connait a priori pas le nombre d'occurences [clindoeil] [code] /* Fonctions de gestion du fichiers CSV */ /* Fonction d'initialisation d'une liste de données dans un fichier CSV. La liste est passée par variable pour permettre son initialisation. */ void init_list(t_CSV *lst) { *lst = NULL; } [/code] Heuu, je relirai le code pour voir son utilité, mais a priori, pourquoi faire une fonction (avec la mise en pile que cela implique) pour… une seule instruction [question] [code] /* Fonction d'ajout d'une ligne du fichier CSV. Les paramètres de la personne sont passés par variables, mais ne peuvent être modifiés car ils sont constants. Ce sont des chaînes de caractères C, qui sont donc assimilées à des pointeurs de caractères constants. */ int add_line(t_CSV *lst,int iRow, int iCol, const char *cColName, const char *cValeurs) { /* Crée un nouvel élément : */ cFieldLine *p = (cFieldLine *) malloc(sizeof(cFieldLine)); if (p != NULL) { p->iRow = (int *) malloc( sizeof(int)); p->iCol = (int *) malloc( sizeof(int)); /* Alloue la mémoire pour le nom de la colonne et la valeur. Attention, il faut compter le caractère nul terminal des chaînes : */ p->cColumnName = (char *) malloc((sizeof(cColName) + 1) * sizeof(char)); p->cValues = (char *) malloc((sizeof(cValeurs) + 1) * sizeof(char)); if (p->cColumnName != NULL && p->cValues != NULL) { /* Copie le nom et la valeur : */ strcpy(p->cColumnName, cColName); strcpy(p->cValues, cValeurs); p->next = *lst; *lst = p; } else { free(p); p = NULL; } } return (p != NULL); } [/code] Déjà, tu essaie d'allouer un pointeur pour une variable qui n'est pas définie comme telle dans la structure (iCol et iRow sont des entier, non des pointeurs sur les entier)… en plus, dans le cadre où l'on accepte l'idée de ne pas mettre plus que nécessaire dans les structures, il y a moyen d'éviter de nombreuses allocations (avec le vérifications qui vont avec) [clindoeil] [code] /* Simple fonction d'affichage. */ void print_list(t_CSV const *lst) { cFieldLine const *p = *lst; int i = 1; while (p != NULL) { printf("Ligne %d : %-20.20s (%-10.10s)\n", i, p->cColumnName, p->cValues); p = p->next; ++i; } } [/code] aucun problème la dessus… [quote] /* Fonction de destruction et de libération de la mémoire. */ void destroy_list(t_CSV *lst) { while (*lst != NULL) { cFieldLine *p = *lst; *lst = p->next; free(p->cColumnName); free(p->cValues); free(p->iRow); free(p->iCol); free(p); } return ; } [/code] Tu essaie de libérer un pointeur qui est défini comme variable normale dans la structure (iRow et iCol ne sont pas des pointeurs, mais des entiers[clindoeil]) [code] /* Fonction de suppression d'une personne. La structure de la liste est modifiée par la suppression de l'élément de cette personne. Cela peut impliquer la modification du chaînage de l'élément précédent, ou la modification de la tête de liste elle-même. */ int remove_line(t_CSV *lst,int iRow) { /* Recherche la personne et son antécédant : */ cFieldLine *prev = NULL; cFieldLine *p = *lst; while (p != NULL) { /* On sort si l'élément courant est la personne recherchée : */ if (p->iRow == iRow) break; /* On passe à l'élément suivant sinon : */ prev = p; p = p->next; } if (p != NULL) { /* La personne a été trouvée, on la supprime de la liste : */ if (prev == NULL) { /* La personne est en tête de liste, on met à jour le pointeur de tête de liste : */ *lst = p->next; } else { /* On met à jour le lien de l'élément précédent : */ prev->next = p->next; } /* et on la détruit : */ free(p->cColumnName); free(p->cValues); free(p->iRow); free(p->iCol); free(p); } return (p != NULL); } [/code] Il y a moyen d'éviter le break, et encore une fois tu essaye de libérer l'espace mémoire de variable réelles… [code] /* Méthode qui compte le nombre de ligne dans un fichier */ int getNbRows(FILE *file) { char line[LINE_LENGTH]; int count=1; /* Compte le nombre de ligne . */ while ( fgets(line, LINE_LENGTH, file) != NULL) count++; return count; } [/code] Aucun problème, mais ne devrait pas etre utile [clindoeil] [quote] /* Méthode qui compte le nombre de colonne par rapport à un séparateur */ int getNbColumns(FILE *file, char delimiter) { long lPosition = 0 ; int iCol = 0; char *header ; char *c; char line[LINE_LENGTH]; lPosition = ftell(file); /* Compte le Nombre de Colonne */ header = fgets(line, LINE_LENGTH, file); c = header; iCol = 1; while (*c) if (*(c++) == delimiter) iCol ++; return iCol; } [/code] La fonction C strtok() pourrait t'éviter bien des soucis pour le comptage… [code] /* ---------------------------------------------------------------- */ /* Méthode qui découpe un champ selon un séparateur */ /* */ /* Retour un pointeur . */ /* */ /* ---------------------------------------------------------------- */ static char *getSplitFieldWithDelimiter(char **stringp, char delimiter) { char *s, *tok = *stringp; char c; char cEndString = '\ '; char *Ch1=NULL; int l,i=0; l=strlen(tok); if ((tok == NULL) || (l ==0)) return NULL; Ch1=(char*)malloc(sizeof(char)); for (s = tok;(c = *s);s++) { if ((delimiter == c)||(cEndString ==c)) { *s = 0; *stringp = s+1; return tok; } } *stringp = NULL; return tok; } [/code] ici aussi strtok() fera des miracles… [code] /* Lire les entêtes de colonne */ int getHeaderFile(t_CSV *theader, FILE *file,char delimiter ) { long lLenDeb = 0 ; int iColumn = 1; char *header ; char *c; char line[LINE_LENGTH]; char cColName; char cName; //lPositionInit = ftell(file); fseek(file, 0, SEEK_SET); /* Compte le Nombre de Colonne */ header = fgets(line, LINE_LENGTH, file); c = header; lLenDeb = strlen(c); //cFieldLine *TAB_REEL = (cFieldLine *) malloc(sizeof(cFieldLine)); char *input = header, *tok; for (iColumn = 1;(tok = getSplitFieldWithDelimiter(&input, delimiter)) != NULL; iColumn++) { if ((strcmp((char *)tok,"\\")!=0) && (tok != NULL)) { add_line(&theader, 1,iColumn, (char *)tok,(char *)tok ); TAB_REEL[iColumn].cColumnName = (char *)tok ; TAB_REEL[iColumn].iRow = 1; TAB_REEL[iColumn].iCol = iColumn; TAB_REEL[iColumn].cValues = (char *)tok ; printf("Colonne : %-5.5s - Taille du Tableau : %d\n" ,TAB_REEL[iColumn].cValues, sizeof(TAB_REEL)); } else return iColumn; } return iColumn; } [/code] On relit les explications depuis le début [clindoeil] [code] int main(void) { FILE *file; char cFileName[] = "test.csv"; file = fopen(cFileName,"r"); char delimiter ="\t"; char line[LINE_LENGTH]; /* Crée une liste de données venant du fichier CSV : */ t_CSV p; t_CSV pHeader; init_list(&p); init_list(&pHeader); int i, j; int iNbCol = getNbColumns(file,'\ '); int iNbRow = getNbRows(file); int iDebLg = 1; char *header ; char *input = header, *tok; /* TO DO */ getHeaderFile(pHeader,file,'\ '); cFieldLine *p2 = TAB_REEL; //p2= (cFieldLine *)malloc (sizeof(TAB_REEL)+1); for( i=iDebLg; i <= iNbRow; i++ ) { header = fgets(line, LINE_LENGTH, file); input = header; char *cNameCol; if (p2 == NULL) printf("NULL \n"); if (i >= 5 ) { printf("Taille de P2: %d \n", sizeof(p2)); p2 = NULL; p2 = (cFieldLine *)realloc(p2, sizeof(p2)+1); p2= TAB_REEL; printf("Attention \n"); } for (j = 1;(tok = getSplitFieldWithDelimiter(&input, '\ ')) != NULL; j++) { cNameCol = p2[j].cColumnName printf("Nom : %s \n",cNameCol); add_line(&p, i,j,cNameCol ,(char *)tok ); } } print_list(&p); destroy_list(&p); return 0; } [/code] avec toutes les modifications à apporter, y a moyen de faire mieux [clindoeil] Je dois m'absenter pour quelques heures (et cette réponse est de nouveau kilométrique [rougir])… Dés que j'aurai quelques instants, je te préparerai un code qui devrait prendre en compte ces différentes remarques… Comprend bien que mon but n'était nullement de descendre en flamme ton travail, qui a au moins le mérite d'avoir été fait… Mon but était surtout de t'expliquer quelques erreurs commises, tant au point de vue de la conception que du point de vue du codage lui meme [clindoeil]
DrChal
 Posté le 08/06/2006 à 14:34 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Nouvel astucien
Salut, Je comprends bien que tu essaies de m'aider. Et si je n'ai pas suivi ce que tu m'as donné comme aide au début. C'est que je n'arrivai pas à mes fins. [clindoeil] Si tu peux m'aider avec l'exemple que tu me dis. Ce serait pour moi très sympath. Je ne sais plus quoi faire. Merci d'avance
koala01
 Posté le 08/06/2006 à 22:51 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Astucien
Choses promises, choses due… J'ai, actuellement, sous les yeux un code dont la taille tournes aux alentours de… 480 lignes, (très nombreux) commentaires compris, je te rassure tout de suite, qui devrait fonctionner… Comme je prévois de fournir quelques explications supplémentaires, ca risque de faire un post kilométrique et qui nécessitera sans doute plusieurs lectures avant de percuter sur ce que j'ai fait… Je lances donc un avis solennel à tous ceux qui voudront le lire: préparez du café, du lait, du sucre et votre paquet de cigaerttes si vous fumez [clindoeil] Pour la faciliter, je vais scinder le code en différentes parties afin de ne pas avoir *trop* d'explications à donner d'un seul coup… C'est bon [question] tout le monde est pret [question], alors accrochez vous, nous démarrons… La premiere chose à faire, c'est d'inclure les entetes qui nous seront nécessaires [code] //les inclusions classiques #include <stdio.h> #include <malloc.h> #include <string.h> [/code] malloc.h est nécessaire pour la gestion dynamique des structure et string pour strlen, strcpy et strtok Ensuite, il faut créer trois structures distinctes (qui se rapportent à peu pres à ce que j'expliquais plus haut) Il s'agira de trois listes (meme si on ne les trie pas) qui permettront de garder en mémoire les noms des champs, les valeurs des champs et les enregistrements [code] //les structures //Il s'agira à chaque fois d'une liste, ce qui permet d'allouer élément par //élément, et d'accéder à l'élément suivant grace à un code générique du genre //de Variable->Suivant; //une structure pour les noms de champs typedef struct ListeChamps { char *Nom; struct ListeChamps *Suivant; }LChamps; //une strucutre pour le champs d'un enregistrement typedef struct ListeDonnees { char *Variable; struct ListeDonnees *Suivant; }LDonnee; //une structure pour les enregistrement typedef struct ListeEnreg { //elle contient les données de champs LDonnee *Data; struct ListeEnreg *Suivant; }LEnreg; [/code] J'ai mis ensuite les prototypes des différentes fonctions… Certaines seront "indispensables", alors que d'autres n'ont été rajoutées que pour permettre, à un moment donné, d'avoir un affichage de ce qui a été fait… Voici les prototypes des fonction "indispensables" [code] //Les prototypes de fonctions //fonction de récupération des nom de champs //prend un équivalent chaine de caractère en parametre et correspondant // à la ligne lue dans le fichier //Renvoie un pointeur sur le premier élément de la liste de nom de champ LChamps * LectureNoms(char *noms); //fonction de récupération des valeur de donées //prend un équivalent chaine de caractère en parametre et correspondant // à la ligne lue dans le fichier //Renvoie un pointeur sur le premier élément de la liste de valeurs de //champ LDonnee *LectureValeur(char *donnees); //fonction de récupération des enregistrements // prend un fichier ouvert en lecture en parametre //renvoie un pointeur sur le premier élément de la liste des //enregistrements LEnreg* LectureEnregistrement(FILE *fichier); //fonction de libération de la liste d'enregistrements //prend la liste d'enregistrements en parametre //ne renvoie rien void LibererEnreg(LEnreg *enregistrement); //fonction de libération de la liste des noms de champs //prend la liste d'enregistrements en parametre //ne renvoie rien void LibererNom(LChamps *noms); //fonction de libération des listes de valeur d'enregistrement //prend un pointeur vers la liste des valeurs en parametre //ne renvoie rien void LibererVal(LDonnee *data); [/code] et voici les prototypes des fonctions "facultatives" (pensez cependant au fait que ces fonctions sont actuellement utilisées dans le code [clindoeil] [code] //quelques fonctions qui peuvent aider (mais dont l'utilisation n'est pas //obligatoire bien que pour l'exemple, j'y fasse appel) //fonction de comptage du nombre de noms de champs //prend une liste des noms de champs en parametre //renvoie un nombre entier int CompteNoms(LChamps *premier); //fonction de comptage du nombre de données par enregistrement //prend une liste des valeur de champs en parametre //renvoie un nombre entier int CompteData(LDonnee *premier); //fonction de comptage du nombre d'enregistrements //prend une liste d'enregistrement en parametre //renvoie un nombre entier; int CompteEnreg(LEnreg *premier); //fonction d'affichage dans la console //prend les listes de noms de champs et d'enregistrement en argument //ne renvoie rien void Affichage(LChamps *Noms, LEnreg *Enreg); [/code] Vient ensuite la fonction principale, qui déclare un pointeur sur une liste d'enregistrement et un autre sur une liste de noms de champs… Elle utilise également un pointeur sur un fichier (celui qui est à lire) et quelques variables de travail. Il est à noter que j'ai choisi, ici, d'écrire le nom du fichier à ouvrir en dur (et qu'il est d'ailleurs à modifier), mais qu'il serait surement plus intéressant (réutilisabilité oblige) de prévoir une séquence demandant à l'utilisateur d'introduire le chemin et le nom du fichier sur lequel il veut travailler [clindoeil] [code]int main(int argc, char* argv[]) { //les variables nécessaire LChamps *Noms; LEnreg *Enregistrements; FILE *fichier; //valeur de renvoi (typiquement 0 pour réussite, 1 pour échec) // on considère à la base que ca va marcher int renvoi=0; //une variable de bufferisation... char *buffer=NULL; fichier=fopen("chemin\\vers_le\\fichier.csv","rt"); //si l'ouverture échoue, c'est un échec if(fichier==NULL) { renvoi=1; } //on lit la premiere ligne dans buffer (uniquement si l'ouverture a réussi) if(renvoi==0) { fgets(buffer,512, fichier); //on teste si ca a marché if(buffer==NULL) { renvoi==1 } } //si la lecture a réussi, on utilise cette ligne pour récupérer la liste //des noms de champs et on continue if(renvoi==0) { Noms=LectureNoms(buffer); //si noms vaut null la fonction a échoué if(Noms==NULL) { renvoi=1; fclose(fichier); fichier=NULL; } } //il n'est intéressant d'aller plus loin que si on a bien obtenu //la liste des noms de champs if(renvoi==0) { Enregistrements=LectureEnregistrement(fichier); //si enregistrements vaut null, la fonction a échoué if(Enregistrements==NULL) { //on libère la liste des noms de champs LibererNom(Noms); renvoi=1; fclose(fichier); fichier=NULL; } } //arrivé à ce stade, si renvoi vaut toujours 0, on a toutes nos //listes correctement remplies... if(renvoi==0) { buffer=NULL; //il y a encore du boulot ici mais on verra plus tard ;-) //par exemple: Affichage(Noms, Enregistrements); } //Avant de quitter définitivement, il faut libérer les pointeurs restants //l'appel des fonctions de libération ne posera aucun problème, meme si, //pour une raison ou une autre, les listes venaient à etre vide à ce //point //n'oublions pas de fermer le fichier fclose(fichier); fichier=NULL; LibererEnreg(Enregistrements); Enregistrements=NULL; LibererNom(Noms); Noms=NULL; buffer=NULL; return renvoi; }[/code] L'impémentation des fonctions de création des liste sera identique, à quelques détails près, pour toutes les listes… Elles utiliseront toutes un pointeur sur le premier élément de la liste (vallant null au départ), un pointeur temporaire pour l'élément en cours de traitement et un pointeur sur le dernier élément de la liste à avoir été introduit… Nous pourrions nous passer de ce dernier pointeur, mais cela nous obligerait, à chaque ajout dans la liste, à reparcourrir toute celle-ci afin d'en trouver la fin… Il parrait clairement plus facile de garder "un doigt" sur la fin de la liste [clindoeil] Etant donné qu'il y a une foule de raisons pour lesquelles la boucle devrait etre quittée prématurément, elles utiliseront toutes une variable indiquant si c'est "ok pour le tour suivant" ou non (valeur 1 si ok, valeur 0 sinon)… Il s'agira de veiller à faire correspondre le caractère utilisé par strtok au caractère qui représente effectivement le séparateur… Dans tout le code, je me suis basé sur l'espace… m'est avis que, comme on parle de fichier csv, j'aurais du utiliser plutot la virgule [clindoeil] Voici l'implémentation de la fonction qui créera la liste des noms de champs [code] LChamps * LectureNoms(char *noms) { LChamps *Renvoi=NULL; LChamps *dernier=NULL; LChamps *temp=NULL; int ok=1; int cpt=0; int recomptage=0; char *tempo; //récupération du premier nom tempo=strtok(noms, " "); while( tempo!=NULL && ok==1) { //allocation d'un pointeur pour un nouveau nom temp=(LChamps *) malloc(sizeof(LChamps)); //vérification de l'allocation if(temp!=NULL) { //allocation d'un tableau de caractere susceptible d'en //contenir un nombre suffisant temp->Nom=(char *)malloc(sizeof(char)*(strlen(tempo)+1)); if(temp->Nom!=NULL) { //n'oublions pas d'initialiser Suivant à null temp->Suivant=NULL; //et copion le nom à sa place strcpy(temp->Nom,tempo); //par sécurité, on rajout le caractère \\0 en fin //de chaine temp->Nom[strlen(tempo)]='\\0'; //insérons le nouveau nom dans la liste if(Renvoi==NULL) { Renvoi=temp; dernier=temp; } else { dernier->Suivant=temp; dernier=temp; } cpt++; //on prépare le travail pour la boucle suivante tempo=strtok(NULL," "); } else { //forcera la vidange de la liste en cas d'échec ok=0; } } if(temp==NULL || ok==0) { //on libere la liste des noms de champs LibereNom(Premier); Premier=NULL; } } recomptage=CompteNoms(Renvoi); //A l'affichage, les deux valeurs devraient etre identique... printf("%d noms de champs théoriques, %d noms de champs trouves", &cpt,&recomptage); //faisons les choses dans les regles de l'art, meme si ces deux //pointeurs n'existeront bientot plus temp=NULL; dernier=NULL; return Renvoi; }[/code] Celle de la fonction qui créera la liste des valeurs d'enregistrements [code] LDonnee *LectureValeur(char *donnees) { LDonnee *Premier=NULL; LDonnee *Dernier=NULL; LDonnee *temp=NULL; int ok=1; int cpt=0; int taille; char *tempo; //récupération de la premiere donnée tempo=strtok(donnees," "); while(ok==1 && tempo!=NULL) { temp=(LDonnee *)malloc(sizeof(LDonnee)); if(temp!=NULL) { temp->Variable=NULL; temp->Suivant=NULL; temp->Variable=(char*)malloc(sizeof(char)*(strlen(tempo)+1)); if(temp->Variable!=NULL) { strcpy(temp->Variable,tempo); //par sécurité, on rajoute le caracter "\\0" //en fin de chaine temp->Variable[strlen(tempo)]='\\0'; if(Premier==NULL) { Premier=temp; Dernier=temp; } else { Dernier->Suivant=temp; Dernier=temp; } cpt++; } else { ok=0; } } if(ok==0) { //libérons la liste des valeurs LibererVal(Premier); Premier=NULL; } } ok=CompteData(Premier); printf("%d noms de champs theoriques, %d noms de champs trouves", &cpt,&ok); //faisons les choses dans les regles de l'art temp=NULL; Dernier=NULL; return Premier; }[/code] Et pour en finir avec la création de listes, celle qui créera la liste des enregistrements. Elle déroge un tout petit peu à la règle en prenant un nom de fichier en argument… Elle ira chercher les informations directement dans celui-ci [clindoeil] [code]LEnreg* LectureEnregistrement(FILE *fichier) { LEnreg *Premier=NULL; LEnreg *dernier=NULL; LEnreg *temp=NULL; //espérons qu'il n'y aura pas de ligne unique plus longue que 512 //caracteres..sinon, augmenter en conséquence chaque fois qu'on voit 512 char tempo[512]; int ok=1; int cpt=0; while(ok==1 &!feof(fichier)) { temp=(LEnreg *)malloc(sizeof(LEnreg)); if(temp!=NULL) { temp->Data=NULL; temp->Suivant=NULL; fgets(tempo,512, fichier); if(tempo!=NULL) { temp->Data=LectureValeur(tempo); if(temp->Data!=NULL) { if(Premier==NULL) { Premier=temp; dernier=temp; } else { dernier->Suivant=temp; dernier=temp; } cpt++; } else { ok=0; } } } else { ok=0; } if(ok==0) { while(Premier !=NULL) { LibererVal(Premier->Data); temp=Premier->Suivant; free(Premier); Premier=temp; } } } ok=CompteData(Premier->Data); //A l'affichage, les deux valeurs devraient etre identique... printf("%d noms d'enregistrements théoriques, %d noms de champs trouves", &cpt,&ok); //faisons les choses dans les regles de l'art, meme si ces deux //pointeurs n'existeront bientot plus temp=NULL; dernier=NULL; return Premier; }[/code] Apres avoir rempli les listes, veillons à gérer leur vidange, et surtout, la libération de toute la mémoire qui leur a été allouée… Les trois fonction qui suivent s'en occupent (chacune pour une des liste en particulier) et suivront toujours le meme shcéma: -utilisation d'un pointeur temporaire vers "l'élément qui suit dans la liste" -"mise de coté" de l'élément qui suit dans la liste -libération du pointeur qui correspond au "premier de la liste" -L'élément qui suit mis de coté (anciennement deuxième élément) devient le premier, et on recommence jusqu'à ce qu'il n'y en aie plus… A noter que, pour ce qui est de la liste des enregistrements, comme elle est elle meme composée d'une liste de données, il faut, avant de libérer la mémoire de l'enregistrement, penser à libérer la liste de ses données [clindoeil] Voici l'implémentation pour la liste des enregistrements [code] void LibererEnreg(LEnreg *enregistrement) { LEnreg *enregtemp; while(enregistrement!=NULL) { //récupération de l'élément suivant dans la liste enregtemp=enregistrement->Suivant; //il faut libérer la liste des données LibererVal(enregistrement->Data); //libération du pointeur free(enregistrement); //le suivant devient premier de la liste enregistrement=enregtemp; } }[/code] Qui ne pourrait rien faire sans celle de libération de la liste des données, que voici [code] void LibererVal(LDonnee *data) { //nous aurons besoin d'un pointeur temporaire de travail LDonnee *datatemp; while(data!=NULL) { //récupération de l'élément suivant dans la liste datatemp=data->Suivant; //liberation du pointeur free(data); //l'élement suivant devient premier de la liste data=datatemp; } }[/code] Et pour terminer dans le secteur, "last but not least", celle de libération des noms de champs [code] void LibererNom(LChamps *noms) { LChamps *nomtemp; while(noms!=NULL) { //récupération de l'élément suivant dans la liste nomtemp=noms->Suivant; //libération du pointeur free(noms); //le suivant devient premier de la liste noms=nomtemp; } }[/code] Les trois fonctions qui suivent ne sont pas obligatoires (si, du moins, vous n'oubliez pas de supprimer les appels à celles-ci), mais permettent "juste" de récupérer le nombre d'éléments dont disposent les différentes listes… Elles utilisent toute un compteur, valant initialement 0, et se contentent de parcourrir la liste d'un bout à l'autre, en incrémentant le compteur à chaque fois… Quand le bout de la liste est atteint, elles renvoient la valeur du compteur… Voici l'implémentation pour la liste des noms de champs [code] int CompteNoms(LChamps *premier) { //le compteur que l'on utilisera int cpt=0; do { cpt++; premier=premier->Suivant; }while(premier!=NULL); return cpt; }[/code] Celle pour la liste des données d'enregistrement [code] int CompteData(LDonnee *premier) { //le compteur que l'on utilisera int cpt=0; do { cpt++; premier=premier->Suivant; }while(premier!=NULL); return cpt; }[/code] et enfin celle pour le comptage du nombre d'enregistrements [code] int CompteEnreg(LEnreg *premier) { //le compteur que l'on utilisera int cpt=0; do { cpt++; premier=premier->Suivant; }while(premier!=NULL); return cpt; }[/code] La derniere procédure implémentée sert à provoquer l'affichage console des couples noms de champs= valeur du champs pour tous les champs de tous les enregistrements… La grande amélioration qui pourrait etre apportée est d'ajouter un compteur qui veillerait au fait qu'il n'y aie que maximum 25 lignes (hauteur habituelle en console) d'affichée à chaque fois, avec demande d'appuis sur une touche pour la suite… Les modifications à apporter pour que les données soient écrite dans un fichiers (sous la forme d'une equete SQL, par exemple) ne devraient pas être particulièrement difficiles à mettre au point [clindoeil] La fonction se base principalement sur deux boucles imbriquées: la premier parcourrant tous les éléments de la listes d'enregistrements, la seconde (imbriquée dans la premiere) parcourrant la liste des noms de champs et celle des valeur de l'enregistrement en cours de traitement… En voici l'implémentaiton: [code]void Affichage(LChamps *Noms, LEnreg *Enreg) { //nous aurons besoin de deux pointeurs de travail pour éviter de perdre //nos listes en cours de route LDonnee *datatemp; LChamps *nomtemp; //"par sécurité" on prévoit une valeur de sortie (1=ok, 0=nok) int ok=1; //nous créons une boucle qui parcourrera la liste des enregistrements while(Enreg!=NULL && ok==1) { //on initialise nos pointeurs de travail sur les premieres //valeurs respectives de chamque liste nomtemp=Noms; datatemp=Enreg->Data; //nous créons une boucle pour parcourrir les deux listes en //meme temps while(nomtemp!=NULL && datatemp!=NULL) { //on affiche le couple nom de champs, valeur de champs printf("%s=%s\t",nomtemp->Nom,datatemp->Variable); //on passe au suivant des deux listes nomtemp=nomtemp->Suivant; datatemp=datatemp->Suivant; } //En sortie de boucle, on passe à la ligne printf("\n"); //*théoriquement, les deux liste devraient finir en meme temps //est-ce le cas? if(nomtemp!=NULL || datatemp!=NULL) { //faudra voir que faire, mais ne attendant, on quitte la //boucle principale ok=0; } //nous passons à l'enregistrement suivant Enreg=Enreg->Suivant; } //La boucle a peut etre été quittée prématurément, ca se gere ici if(ok==0) { //faudra voir que faire } }[/code] L'ensemble du code, mis bout à bout, devrait etre en mesure de fonctionner (j'ai apporté quelques modifications auquelles je n'avais pas pensé au moment meme de l'écriture, et qui n'ont pas été testées à la compilation…) Lors de la dernière compilation, il n'y avait, avec mon compilo, aucune erreur ni aucun avertissement… Il devrait en être autant pour vous… Ne disposant malheureusement pas de fichier CSV, je n'ai pas pu le tester en utilisation réelle, mais, de prime abord, il devrait fonctionner tout à fait correctement… Je reste, bien évidemment, à votre disposition pour toute explication complémentaire (bien que je vous jure avoir essayé d'être aussi clair et précis que possible [langue][crazy][boom] Et je tire mon chapeau à tous ceux qui ont réussi à lire ce post sans (trop) se taper la tete au mur [boom][bigsmile][bigsmile]
DrChal
 Posté le 09/06/2006 à 08:46 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Nouvel astucien
Salut Merci pour ce code, il t'en a coûté du temps pour tout écrire.[clindoeil] Par contre, le programme se plante sur : [code] fgets(buffer,512, fichier); [/code] Tu sais pk? J'ai essayé de mettre le code suivant et la lecture du fichier fonctionne: [code] char line[LINE_LENGTH]; // LINE_LENGTH = 512 fgets(line, LINE_LENGTH, fichier); [/code] Par contre, je ne sais pas pk l'autre ne fonctionne pas. Merci Fred
breizhbugs
 Posté le 09/06/2006 à 13:38 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Astucien
DrChal a écrit : Par contre, le programme se plante sur : [code] fgets(buffer,512, fichier); [/code] Tu sais pk?
Salut, cette ligne est situé dans le main() buffer est initialisé a NULL avant et n'est donc pas alloué (dynamiquement). (C'est force combien ca sur l'echelle de koala, hum [bigsmile][clindoeil]?) Ta correction effectue une allocation statique.

Modifié par breizhbugs le 09/06/2006 13:39
koala01
 Posté le 09/06/2006 à 14:12 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Astucien
ouppssss.... c'est hors échelle… Désolé pour cette erreur impardonnable… As-tu compris l'ensemble du code, ou souhaites-tu quelques explications complémentaires [question] C'est le moment, c'est l'instant [clindoeil]

Modifié par koala01 le 09/06/2006 14:17
Publicité
koala01
 Posté le 09/06/2006 à 14:22 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Astucien
DrChal a écrit :
Salut Merci pour ce code, il t'en a coûté du temps pour tout écrire.[clindoeil]
Ben, à peu pres trois heures, trois heures et demie… Alors, je préprare la facture: 3,5 *12,5€=43.75€… Noublions pas les lois sociales (+/-33%): 43.75*1.33~=58.187€ Et n'oublions pas la TVA (he, l'état doit bien vivre [langue]) de 21%… Soit un total de 70,41€… Allez, une bonne bierre à la fin de l'année fera le compte [clindoeil]
DrChal
 Posté le 09/06/2006 à 14:39 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Nouvel astucien
Salut Merci pour ton aide. J'ai corrigé qqs petits oublie dans le code. Je commence à comprendre. Je deviens moins null.[clindoeil] Mais j'ai tjs des réusultats bizarre en sortie. A l'affichage, j'ai des smile des coeur des points d'interrogation à l'envers. Mais j'ai tous les champs du fichier CSV. J'ai laissé en statique comme tu dis la valeur LINE_LEnGTH [code] fgets(buffer,512, fichier); [/code] Car sinon, il ne marchait pas . Et j'ai rajouté dans la méthode LectureValeur [code] tempo = strtok (NULL, cDelimiter) ; [/code] Sinon il bouclait. Peux tu m'expliquait comment peut - on accéder directement à une valeur? Exemple : je veux atteindre la valeur de la ligne 5 et colonnne 3 Merci Fred
koala01
 Posté le 09/06/2006 à 16:22 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Astucien
J'avais bien dit que, n'ayant pas pu tester, il y avait peut etre quelques arrangements à compléter [clindoeil]… je suis désolé pour ceux-là [clindoeil] En fait, comme il s'agit d'une structure dynamique dont un élément dispose de l'adresse du suivant, et non d'un "simple" tableau, et qu'en plus, la churcharge des opérateur est impossible en C, il va falloir ruser un peu… Le principe est relativement simple, disposant du premier élément de la liste, il "suffit" de passer de suivant en suivant (et de ne rien faire d'autre) jusqu'à obtention de celui qu'on souhaite avoir… Pour ce faire, avec une structure de liste déclarée avec le nom de variable, le plus facile est une boucle incrémentale du genre de [code] for(i=0;i<numero_recherche;i++) { variable=variable->Suivant; } [/code] en prenant toujours soin de veiller à utiliser un pointeur de travail, quand c'est nécessaire, pour éviter de perdre le premier élément de la liste, et ou numero_recherche peut éventuellement etre une variable de type eniter… NOTA: si tu veux coder en dur l'acces systématique au troisième élément d'une liste (par exemple), rien ne t'empeche d'écrire un code du genre de [code] PtrTravail=Premier->Suivant->Suivant; [/code] et de donc récupérer le troisième élément de la liste dans PtrTravail (qui doit etre, tu t'en doutes, déclaré comme un pointeur sur la structure que tu souhaites utiliser [clindoeil]) S'il n'y a qu'un ou deux endroits où tu souhaites récupérer le Nieme élément d'une liste, tu peux insérer la boucle (ou l'instruction "en dur") dans n'importe quelle fonction (y compris main), pour autant que tu penses à déclarer ton pointeur de travail (si ce n'est pas encore fait)… Attention: Pour rappel: En C, toutes les variables doivent etre définies AVANT la premiere instruction de la fonction, sous peine que certains (anciens [question]) compilo ne refusent la compialtion… Si par contre, tu "subodores" que tu devra disposer de cette fonctionnalité de manière régulière, le mieux, c'est d'utiliser une fonction qui fera le travail… Voici le prototype de la fonction en exemple pour une fonction qui permettrait d'accéder au Nieme élément de la liste des noms (seuls les types changent pour les autres) [code] //fonction renvoyant le Niem élément de la liste des noms de champs //prenant en parametre un pointeur sur le premier élément de la liste, et //un entier représentant l'élément désiré //et renvoyant un pointeur sur le Nieme élément de la liste LChamps * GetChampByNum(LChamps *premier, int num) [/code] L'implémentaiton prendrait donc la forme de (seul le type change pour les autres) [code] LChamps * GetChampByNum(LChamps *premier, int num) { int i; LChamps *travail; //on initialise le pointeur de travail sur le premier élément de la liste travail=premier; for(i=0;i<num;i++) { //par sécurité, il vaut mieux s'assurer que l'on ne soit pas déjà //sorti de la liste if(travail!=NULL) { travail=travail->Suivant; } } return travail; } [/code] et sera appelées sous la forme de [code] PtrTravail=GetChampByNum(Premier, numero_champs_recherche); [/code] où PtrTravail est un pointeur de travail du type (ici LChamps), Premier est le pointeur (de type LChamps, ici) dont tu disposes sur le premier élément de la liste et numero_champs_recherche est le numero du champs (éventuellement sous forme de variable de type entier) que tu souhaites récupérer… N'oublie pas de vérifier si PtrTravail est (ou plutot n'est pas) égal à NULL, des fois que tu aies demandé le septieme élément d'une liste… qui n'en contient que 5 (ou en tout cas moins de 7)[clindoeil]

Modifié par koala01 le 09/06/2006 16:25
DrChal
 Posté le 12/06/2006 à 08:38 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Nouvel astucien
Merci pour ton aide, je vais essayer ça tout de suite. Encore une question, comment peut on trier la liste? J'ai essayé le qsort mais ça plante. Autre question, j'ai essayé de lire un fichier en utilisant fscanf. Mais je ne sais pas trop l'uttiliser. Car il me ramène un peu n'importe quoi. Exemple de ligne à lire: 0000001645;00001;CA;0001 ;20060619;TOTO Je voulais mettre ces valeurs dans des variables pour les tester. J'ai fait de la façon suivante : [code] int iSAPid ; int iSAPPoste; int iRef; char cNature[3]; char *sLine; sscanf(sLine,"%10d;%5d;%2s;%4d;",&iSAPid,&iSAPPoste,&cNature,&iRef); [/code] Et lorsque je fais afficher ce que j'ai voulu lire. Il me renvoie bien le premier groupe de chiffre mais le reste rien de correcte. Tu peux m'expliquer? merci Bonne journée
koala01
 Posté le 12/06/2006 à 13:45 
Aller en bas de la page Revenir au message précédent Revenir en haut de la page
Astucien
Attention [nonnon], ici, on crée les listes en dehors de toute implémentation préexistante… Les inclusions d'entete ont pour seul but de nous donner le minimum vital pour travailler [clindoeil]. Il s'agit donc que nous recréions NOUS-MEMES l'ensemble des fonctions dont on peu avoir besoin [clindoeil]… Pour trier la liste, l'une des solutions les plus faciles à mettre en oeuvre serait de la trier au moment meme où on la remplit… L'idée de base, c'est qu'à chaque insertion d'un nouvel élément, on reparcours la liste pour trouver la place où il doit aller… L'image que voici t'en explique le principe [img]http://koala01.free.fr/images/ajoutlisteschema.png[/img] Evidemment, si la liste est vide, l'élément à rajouter devient la tete de liste… Sur les gros fichiers à lire, travailler comme cela risque de prendre énormément de temps, pour la simple et bonne raison que tu risque, si le fichier est déjà trié, de devoir parcourrir l'entiereté de la liste un nombre équivalent au nombre d'élément de la liste (… qui augmente à chaque fois, en plus [clindoeil][langue] Pour ne pas *trop* aller modifier le code, je conseillerais de créer une nouvelle fonction (que nous nommerions insertion) et qui aurait le prototype suivant [code]//fonction d'insertion d'un élément de la liste qui prend le tri en charge //recoit en argument un pointeur sur le premier élément de la liste //et le pointeur sur l'élément à rajouter //renvoie le premier élément de la liste LEnreg * Insertion(LEnreg *premier, LEnreg *ajout); [/code] et dont l'implémentation ressemblerait à [code] LEnreg * Insertion(LEnreg *premier, LEnreg *ajout) { //la liste est peut etre vide... il faut alors que l'élément //à rajouter devienne le premier élément de la liste LEnreg *travail; if(premier==NULL) { premier=ajout; } else { //peut etre l'élément à rajouter doit il devenir le premier? //n'oublie pas d'adapter le test if(premier->Data->champs_a_tester>ajout->Data->champ_a_tester) ajout->Suivant=premier; premier=ajout; else { //si on arrive ici, il faudra parcourir la liste entièrement :p travail=premier; while((travail->Suivant->Data->champs_a_tester>ajout->Data->champ_a_tester))&& travail->Suivant!=NULL) travail=travail->Suivant; } } return premier; } [/code] ATTENTION: Dans cette fonction, il faudra systématiquement modifier les valeurs Data->champs_a_tester par l'acces à la valeur du champs que tu veux utiliser pour le tri [clindoeil](et peut etre utiliser la fonction strcmp pour la vérification) Il faudra alors modifier le code de LectureEnregistrment pour supprimer les lignes [code] if(Premier==NULL) { Premier=temp; dernier=temp; } else { dernier->Suivant=temp; dernier=temp; } [/code] et les remplacer par [code] premier=Insertion(premier, temp);[/code] Une autre méthode de tri, relativement facile à mettre en oeuvre, et utilisable une fois que la liste est complete, est la méthode dite "de permutations"… En gros, on va comparer chaque élément de la liste avec celui qui le suit… S'il apparait que l'élément que l'on compare aves son suivant devrait etre derriere (car plus grand) son suivant, on permute les deux… et on incrémente un compteur (ca pourrait n'etre qu'une variable passée à 1 [clindoeil])… Tant qu'une permutation a eu lieu, on reparcours la liste pour voir si il faut faire d'autre permutation… S'il s'agissait de créer une fonction de ce type, le prototype en serait [code] //fonction de tri de la liste par permutation //recoit le pointeur vers le premier élément de la liste //renvoie le pointeur sur le premier élément de la liste LEnreg * Permutation(LEnreg *premier) [/code] et l'implémentation pourrait ressembler à ceci [code] LEnreg * Permutation(LEnreg *premier) { //il nous faudra un pointeur de travail et quelques pointeurs temporaire LEnreg *travail; //"avant" permettra de garder le pointeur qui a déjà été vérifié LEnreg *avant; LEnreg *apres; //ainsi qu'une variable de controle int permute; // la premiere boucle vérifiera après coup si on a fait des permutations //on en sort quand permute vaut 0 do { //réinitialisons nos valeurs de travail permute=0; avant=NULL; apres=NULL; //initialisons notre pointeur de travail sur le premier élément travail=premier; //le premier élément de la liste n'est peut etre pas à la bonne place //la deuxieme boucle parcourt toute la liste while(travail!=NULL) { //si la valeur du champs de référence de travail est plus //grande que celle du champs de référence de son suivant, //il faut permuter (n'oublie pas d'adapter le test) if(travail->Data>travail->Suivant->data) { //apres prend l'élément qui suit celui sur lequel //on travaille; apres=travail->Suivant; //faisons pointer travail->Suivant sur le suivant //du suivant; travail->Suivant=apres->Suivant; //apres->Suivant doit pointer sur travail apres->Suivant=travail; //remettons apres dans la liste //si avant vaut null, c'est que travail était //le premier élément de la liste if(avant==NULL) { premier=apres; } else { avant->Suivant=apres; } //indiquons qu'on a fait une permutation permute=1; } //il faut passer à l'élément suivant avant=travail; travail=travail->Suivant; } }while(permute!=0); //"yapuka" renvoyer la tete de liste return premier; }[/code] L'appel de la fonction se fera sous la forme de [code] premier=Permutation(premier); [/code] et peut etre effectuer partout où tu dispose du premier élément de la liste… Attention: Ici aussi, il s'agira d'adapter le test de manière à tester le bon élément de Data, vu que Data est… une liste (mais pense à Data->Suivant->Suivant->Suivant->Variable [clindoeil]) Encore une fois, je ne garanti pas que ce soient les meilleures solutions, (ni meme, d'ailleurs, que je n'ai pas laissé passer une erreur, j'ai écrit ces codes "tels quels, sans les tester)… Je dis juste que ces solutions devraient (sauf erreur de codage) fonctionner sans problèmes… Il existe peut etre d'autres algorithmes de tri utilisables, mais… comme je me suis déjà fait taper sur les doigts du fait de la longueur de mes posts [rouleau][birthday] et qu'il est déjà kilométrique… Si ces deux propositions ne te satisfont pas, on essayera autre chose plus tard [clindoeil]

Modifié par koala01 le 12/06/2006 13:54
Publicité
Page : [1] 
Page 1 sur 1

Vous devez être connecté pour participer à la discussion.
Cliquez ici pour vous identifier.

Vous n'avez pas de compte ? Créez-en un gratuitement !
Recevoir PC Astuces par e-mail


La Lettre quotidienne +226 000 inscrits
Avec l'actu, des logiciels, des applis, des astuces, des bons plans, ...

Les bonnes affaires
Une fois par semaine, un récap des meilleurs offres.

Les fonds d'écran
De jolies photos pour personnaliser votre bureau. Une fois par semaine.

Les nouveaux Bons Plans
Des notifications pour ne pas rater les bons plans publiés sur le site.

Les bons plans du moment PC Astuces

Tous les Bons Plans
294,96 €Mini PC T-BAO TBOOK MN27 (Ryzen 7 2700U, 16Go RAM, 512Go SSD NVME) à 298,48 € avec le code BGSPTB27
Valable jusqu'au 22 Janvier

Banggood propose actuellement le mini PC T-BAO TBOOK MN27 à 298,48 € (avec livraison et assurance comprises) avec le code promo BGSPTB27. Ce mini PC au format NUC d'Intel possède un processeur Ryzen 7 2700U avec chip graphique Vega 10, 16 Go de RAM DDR4 et un SSD NVME de 512 Go. Il dispose d'une connectique complète : un emplacement 2,5 pouces libre (pour ajouter un disque dur ou un SSD supplémentaire, le WiFi5, le bluetooth 4.1, 4 ports USB 3.0, 2 ports USB 2.0, un port HDMI 2.0, un DisplayPort, un port Ethernet Gigabit et tourne sous Windows 10 que vous pourrez mettre en français. Ce mini PC fait 12,8 x 12,8 x 5 cm et pèse 1,2 kg. Il est livré avec une alimentation européenne. Branchez ce mini PC sur une TV ou un écran et vous avez un ordinateur discret et performant.

Ce marchand sérieux se trouvant en Chine, la livraison peut prendre une vingtaine de jours. Vous pouvez payer par carte bancaire ou par Paypal (conseillé pour bénéficier de la garantie Paypal).


> Voir l'offre
529,90 €Ultrabook HONOR MagicBook 14 (Ryzen 5 3500U, 8Go, 256 Go SSD) à 529,90 €
Valable jusqu'au 31 Janvier

HONOR fait une promotion sur son ultrabook HONOR MagicBook 14 qui passe à 529,90 € au lieu de 600 €. Cet ordinateur portable possède un écran 14 pouces Full HD IPS, un processeur AMD Ryzen 5 3500U (avec chip graphique Vega 8), 8 Go de mémoire DDR4, un SSD 256 Go PCIe NVME, le WiFi5 / Bluetooth 5.0, un lecteur d'empreintes, une webcam, un clavier rétro éclairé, une batterie 56 Wh (jusqu'à 10h d'autonomie) et ne pèse que 1,38 kg. Il fonctionne sous Windows 10. Une très bonne affaire pour une machine compacte et puissante.


> Voir l'offre
19,63 €Carte mémoire Samsung 128 Go MicroSDXC Evo Select U3 (100 Mo/s) à 19,63 € livrée
Valable jusqu'au 20 Janvier

Amazon Allemagne fait une promotion sur la toute nouvelle carte mémoire Samsung 128 Go MicroSDXC Evo Select U3 qui passe à 15,12 €. Comptez 4,51 € pour la livraison en France soit un total de 19,63 € livrée. Une bonne affaire pour cette carte que l'on trouve ailleurs à partir de 25 € et qui offre des vitesses de 100 Mo/s en lecture et 90 Mo/s en écriture.  Elle est idéale pour les téléphones, caméras et appareils photo 4K. Elle est étanche, anti-choc et résiste aux rayons X et aux champs magnétiques. Elle est garantie 10 ans.

Vous pouvez utiliser votre compte Amazon France sur Amazon Allemagne et il n'y a pas de frais de douane. Si vous êtes perdu en allemand, vous pouvez traduire le site en anglais.


> Voir l'offre

Sujets relatifs
lire animation en .LUA
ouvrir et lire un fichier asc rar
Comment lire un texte dans un zipped fichier
Problème pour un batch urgent, veuillez lire svp
Lire une occurence dans une phrase
arrive pas à télécharger un prog pour lire les pdf
lire du son (java)
Comment ouvir et lire un fichier executable .exe?
Comment lire un fichier "swf" ?
Lire Sans acces ?
Plus de sujets relatifs à Lire un CSV en C
 > Tous les forums > Forum Autres langages