IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Delphi Discussion :

Temps pour parser un fichier de + de 3 millions de lignes


Sujet :

Langage Delphi

  1. #41
    Membre émérite Avatar de edam
    Homme Profil pro
    Développeur Delphi/c++/Omnis
    Inscrit en
    Décembre 2003
    Messages
    1 894
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Maroc

    Informations professionnelles :
    Activité : Développeur Delphi/c++/Omnis
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 1 894
    Points : 2 771
    Points
    2 771
    Par défaut
    en plus si tu utlise des clientdataset (un peut bon choix) laisse midas faire la recherche en créant des index "unique" sur les champq de recherche
    PAS DE DESTIN, C'EST CE QUE NOUS FAISONS

  2. #42
    Membre chevronné Avatar de chaplin
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 215
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 215
    Points : 1 819
    Points
    1 819
    Par défaut
    J'utiliserais SQLITE, c'est une BDD répondant à la norme SQL, et franchement ça déménage au niveau performance. Insert tous les enregistrements de ton fichier dans cette base et fait un essai, juste pour voir.

    Par contre comment faire un "select disctint colonne1, colonne2'.
    j'ai bien la propriété filtre, mais pour faire l'équivalent d'un select distinct je ne vois pas
    Tu pourras faire ensuite tes select distinct .... . La bibliothèque fournie avec SQLite est de bas de niveau, pas de Dataset,
    ça ne t'empèche pas de faire des manips en SQL.

    Quand je lis ton code, j'ai l'impression qu'on est à l'époque du batch.
    Quand je vois les champs date en 200710.. ça me fait rire !
    Il est vrai que c'est un fichier export, mais vu comme ça se présente, on ne doit pas être loin de l'original.

  3. #43
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Bon, je comprends mieux ce que tu cherches à faire. Tu as un fichier CSV en entrée, qui doit plus ou moins être un fichier log à plat.
    Et tu cherches à charger la base de données, en normalisant les données au passage. Personellement pour faire ce type de traitement, je charge d'abord le fichier avec un bulkload dans une table temporaire du SGBD, puis je fais des requêtes sur la table temporaire pour remplir les autres tables.

    Si ton chef veux tout faire directement, je dois dire que je préfère aussi la première version avec les listes et les pointeurs : Elle évite de charger la totalité du fichier en mémoire, tu ne charges en mémoire que ce dont tu as besoin. Tu peux faire les recherches d'unicités très rapidement (enfin c'est relatif), en évitant les recopies et conversions inutilent... Bref c'est ce qui peut donner les meilleurs performances (mais plus complexe à coder).

    Quelques remarques :
    - Sur ClassGrille : Je suis étonné de voir que tu stockes une clé étrangère sur l'opérateur, mais qu'ensuite tu ne fait les recherches que sur les dates, indépendemment de tout opérateur : Tu dois pouvoir avoir deux intervales de dates avec les mêmes bornes mais pour des opérateurs différents non ? Hors tu n'effectue les recherches de la grille en te basant uniquement sur les dates, puis ce sont ces dernières qui détermine l'opérateur... ça m'a l'air douteux... A moins que tu n'ai qu'un seul et unique opérateur dans le fichier ?

    - Je pense que tu fais pas mal de traitements inutiles qui vont avoir un effet certain sur les performances :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
              if (copy(TempWork.GiveOccurence(3), 1, 6) = '200808') or
                   (copy(TempWork.GiveOccurence(4), 1, 6) = '200808') or
                   (copy(TempWork.GiveOccurence(4), 1, 6) = '200809') or
                   (copy(TempWork.GiveOccurence(4), 1, 6) = '200809')
    Tu calcules 3 fois : copy(TempWork.GiveOccurence(4), 1, 6). Stocke le dans une variable et teste la variable.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    TempWork.GiveOccurence(1)
    Tu appelles constamment cette fonction. J'espère que ce n'est pas elle qui analyse la chaîne et compte les ';' pour retourner le champ que tu cherches. Parce que si tu refais l'analyse à chaque fois, bonjour les perfs...
    Encore une fois, tu gagnerais à stocker la valeur de chaque occurence dans une variable locale après la lecture de la ligne, puis à ne tester que ces variables locales.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    k:=MigrationFormule.SearchInOperateur([TempWork.GiveOccurence(1)]);
                      if  k = -1 then
                      begin
                          new(ClassOperateur);
                          FmaxOperateur:= FmaxOperateur+1;
                          ClassOperateur.ope_id := FmaxOperateur;
                          ClassOperateur.ope_nom_long     := TempWork.GiveOccurence(1);
                          ClassOperateur.ope_nom_court     := TempWork.GiveOccurence(1);
                          lOperateurInMem.Add(ClassOperateur);
                          k:= MigrationFormule.SearchInOperateur([ClassOperateur.ope_nom_court]);
                      end;
                      opeidinterne :=TClassOperateur(lOperateurInMem.Items[K]^).ope_id;
    Tu appelles SearchInOperateur au moins une fois de trop : ce qui t'intéresse à la fin c'est d'initialiser opeidinterne avec l'ope_id. Si tu dois créer l'opérateur, tu connais l'ope_id que tu lui as affecté. Donc tu devrais restructurer ton code pour initialiser opeidinterne directement en cas de création et ne pas rechercher à nouveau le code que tu viens de définir.

    - Enfin, à moins que le nombre d'éléments ne soit très faibles (<5 à 6) n'utilise pas des TList pour construire tes dictionnaires. Avec un TList, tu dois faire une recherche séquentielle pour savoir si un élément fait partie de la liste (donc recherche en O(n)).
    Utilise plutôt un TStringList ordonné. Tu utilise la clé de recherche comme valeur chaîne, et la classe associée dans Objects. De cette façon, la TStringList peut faire les recherches avec une recherche dichotomique (O(ln(n)) qui est beaucoup plus rapide lorsque le nombre d'éléments augmente.
    Si le nombre d'élément devient très grands, il faudra peut-être passer par un tableau hashcodé (recherche en O(1)). Les temps de recherche sont alors indépendant du nombre d'éléments dans la liste.

    C'est certainement en remplaçant les TList avec des TStringList que tu obtiendras les meilleurs gains de perf.

    Ensuite, si le fichier est vraiement important, le temps de lecture du fichier en lui même sera de toute façon important (et la on n'y peut rien). Et le temps d'écriture des résultats en base également.
    Pour simplement lire un fichier texte de 300 Mo, tu peux déjà compter quelques minutes...
    Essaie de faire un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    while not eof do 
    begin
      readln
    end
    Et dit toi bien que quoi que tu fasses, tu as peu de chance de descendre en dessous du temps de lecture du fichier (bien sûr tu pourras l'ouvrir en binaire et non pas en texte, mais ça fixe un ordre de grandeur)...

    De même, insérer 800 000 de ligne dans une BDD ça prend un certain temps. Si ton SGBD le permet, tu pourras faire l'insertion de façon optimal avec un bulkload (tu peut alors espérer insérer 10 000 lignes/seconde), mais même comme ça, il faudra déjà 2 à 3 minutes pour mémoriser le résultat.

    Le temps de lecture du fichier d'origine et le temps d'insertion des résultats en base devraient être supérieur aux temps de recherche dans les listes. Si ce n'est pas le cas, tu as de quoi optimiser.

    Après tu peux encore multi-threader le tout pour faire les recherches d'unicités d'un bloc de lignes, pendant que tu charges le bloc suivant ou que tu écris en base les résultats des lignes précédentes... Regarde la fenêtre CPU de la machine pendant toute la durée du traitement. Si le CPU descend en dessous de 100% tu pourras réduire le temps de traitement globale grâce au multi-threading.

  4. #44
    Membre actif
    Profil pro
    Inscrit en
    Août 2004
    Messages
    696
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 696
    Points : 225
    Points
    225
    Par défaut
    merci bcp de vos réponse riche et intérréssantes:

    Personellement pour faire ce type de traitement, je charge d'abord le fichier avec un bulkload dans une table temporaire du SGBD, puis je fais des requêtes sur la table temporaire pour remplir les autres tables.
    je lui est proposé il n' a pas voulu

    Bref c'est ce qui peut donner les meilleurs performances (mais plus complexe à coder).
    il y a un petit mieux mais rien de très transcendant avec un fichier de plus de 1,3M de lignes, traitement de 10% en plus de 30 minutes, car la recherche d'unicité se fait parfois sur plus de 50 000 occurences dans un list alors s'il doit répeté ce recherche (1,3 M de x) sur 5un minimum de 10 000 occ


    Sur ClassGrille : Je suis étonné de voir que tu stockes une clé étrangère sur l'opérateur, mais qu'ensuite tu ne fait les recherches que sur les dates, indépendemment de tout opérateur
    le critères d'insertion ne doit se faire que via la date,
    Tu appelles constamment cette fonction. J'espère que ce n'est pas elle qui analyse la chaîne et compte les ';' pour retourner le champ que tu cherches. Parce que si tu refais l'analyse à chaque fois, bonjour les perfs...
    Cette fonction ne ramnèe que l'occurence pas d calcul, mais tu as raison je vais les stocker je gagnerais surement u peu


    Enfin, à moins que le nombre d'éléments ne soit très faibles (<5 à 6) n'utilise pas des TList pour construire tes dictionnaires. Avec un TList, tu dois faire une recherche séquentielle pour savoir si un élément fait partie de la liste (donc recherche en O(n)).
    Utilise plutôt un TStringList ordonné.
    Je l'avais fait au départ avec un Tstringlist et il n'a pas voulu, j'ai du modifier le code pour passer par les TList

    pour ce qui est d'insére je lui est triuvé un moyene de contournement temporaire, car il semble trop speed, (tout tout de suite) mon traitement généère les requete d'insert dans un fichier SQL et ensuite j'insere le tout moins de 3 minutes pour mettre à jour la base de données mais ce ne lui plait pas

  5. #45
    Membre chevronné Avatar de chaplin
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 215
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 215
    Points : 1 819
    Points
    1 819
    Par défaut
    Je ne serais même pas étonné si on me disait que la clé de la table d'origine est basée sur operateur,date-debut,date-fin . J'ai vu des trucs tellement dingues que plus rien ne m'étonne aujourd'hui .

    Qu'est ce que ton chef pense de Delphi ?

  6. #46
    Membre actif
    Profil pro
    Inscrit en
    Août 2004
    Messages
    696
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 696
    Points : 225
    Points
    225
    Par défaut
    Ils m'ont embauché pour faire du delphi

  7. #47
    Membre chevronné Avatar de chaplin
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 215
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 215
    Points : 1 819
    Points
    1 819
    Par défaut
    A priori, ils ont quand même l'air de savoir ce qu'ils font. Si l'approche de ton chef n'est pas mauvaise, et là je lui donne raison pour ne pas employer de Dataset, compte tenu de la puissance des machines aujourd'hui, je préfère largement l'approche de Franck Soriano:

    Bon, je comprends mieux ce que tu cherches à faire. Tu as un fichier CSV en entrée, qui doit plus ou moins être un fichier log à plat.
    Et tu cherches à charger la base de données, en normalisant les données au passage. Personellement pour faire ce type de traitement, je charge d'abord le fichier avec un bulkload dans une table temporaire du SGBD, puis je fais des requêtes sur la table temporaire pour remplir les autres tables.
    Après tu prends la BDD avec laquelle tu es le plus à l'aise.

    Ces traitements en disent long sur la BDD sous jacente et donnent aussi une certaine vue sur la limite des BDD centralisées.
    Rien ne vaut un bon PC, sur lequel tu peux faire tous les essais sans emmerder les utilisateurs avec un batch qui aurait ralenti tout le système.

    Cela dit, j'en tire aussi un enseignement, il y a beaucoup de méthodes interessantes abordées dans ton sujet.

    Si ton chef a dit qu'il le voulait comme il l'a décrit, ça ne t'empêche pas d'utiliser le SQL dans un premier temps
    pour valider les tests de la deuxième méthode que préconise ton chef. puisqu'il te le demande ainsi.

  8. #48
    Modérateur

    Homme Profil pro
    Ingénieur retraité
    Inscrit en
    Octobre 2005
    Messages
    2 396
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Ingénieur retraité

    Informations forums :
    Inscription : Octobre 2005
    Messages : 2 396
    Points : 3 263
    Points
    3 263
    Par défaut
    Bonjour,

    Le boss : il ne veut pas entendre de dataset ou de table temporaire, tout doit ce faire par lecture de fichier et pointeurs
    Je l'avais fait au départ avec un Tstringlist et il n'a pas voulu, j'ai du modifier le code pour passer par les TList
    Ton boss est visiblement borné, plein de préjugés, mais nul :
    Tu peux lui dire que fondamentalement un tStringList est justement un tableau dynamique de pointeurs où chaque pointeur pointe vers une cellule, et chaque cellule peut contenir une chaîne de caractères ET un pointeur qui lui peut pointer : vers un objet ou un composant Delphi et en surtypant un peu, vers n'importe quelle donnée en mémoire comme par exemple un tRecord avec ses champs de données. En bref en utilisant un tStringList tu utilises en fait en arrière plan tout un schmilblic de pointeurs.

    A+
    N'oubliez pas de consulter les FAQ Delphi et les cours et tutoriels Delphi

  9. #49
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Citation Envoyé par QAYS Voir le message
    Je l'avais fait au départ avec un Tstringlist et il n'a pas voulu, j'ai du modifier le code pour passer par les TList
    Pourtant c'est la seule chose qui donnera un résultat significatif (sauf si tu ajoute au TList un index implémenté avec un TStringList, ou une table de hashage maison...).
    Moi a ta place, je fairais les mesures de perf sur la recherche dans un TList, la recherche dans un TStringList et je lui fairais la preuve par A+B qu'avec un TStringList c'est beaucoup plus performant.
    Si ensuite il ne veut toujours pas comprendre, c'est pas la peine de chercher plus loin...

  10. #50
    Membre chevronné Avatar de chaplin
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 215
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 215
    Points : 1 819
    Points
    1 819
    Par défaut
    Moi a ta place, je fairais les mesures de perf sur la recherche dans un TList, la recherche dans un TStringList et je lui fairais la preuve par A+B qu'avec un TStringList c'est beaucoup plus performant.
    Au moins tu auras des preuves. En venant sur le forum, tu as posé des questions parce que tu avais un doute,
    dis toi bien qu'il ne détient pas la vérité. S'il le prétend, qu'il me donne la combinaison gagnante du loto pour le prochain tirage

  11. #51
    Membre actif
    Profil pro
    Inscrit en
    Août 2004
    Messages
    696
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 696
    Points : 225
    Points
    225
    Par défaut
    En fait je n'ai aucun cahier des charges ou spec pour savoir ce qu'il veut faire.

    Maintenant cerise sur le gâteau, supposons que la solution soit ok pour la recherche (via les record et les pointeurs) les insert ok en base.

    Un autre problème qui me fait dire que il faut se poser et réfléchir sur le "comment faire", car ce fichier de plus de 1M d'occurrences peux arrivé régulièrement avec d'autres opérateurs ou des opérateurs ou des grille déja enregistrés en base

    Si je travail toujours en local je n'ai pas connaissance des informations qui sont déjà enregistré en base, donc j'effectue donc en local le parssage et la recherche et ensuite j'enregistre en bdd les informations parsées mais là n'ayant pas effectuer des recherche en base de données je risque de créer des doublons (impossible grâce aux clés) en résumé lors de la première importation pas de problème j'importe ce que j'ai trouvé en local, mais lors des prochains import, il faut contrôler aussi bien ce qui existe en base de donnée que ce qui existe dans le fichier à importé. et toujours sur des millions de lignes, dont les temps seront toujours important à mon sens.

    c'est pourquoi je pense que passé par une base tempo est le meilleurs moyen de travailler correctement la première foir et ensuite les fois suivante

  12. #52
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 455
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 455
    Points : 24 867
    Points
    24 867
    Par défaut
    Je pense qu'entre Franck SORIANO, Chaplin et Gilbert Geyer, tout a été dit, comme prévisible, rien que le parsage du fichier CSV doit être lent (voir ma fonction Explode pour dépoter tout ça !), ainsi tu accéderas un à tableau qui aura pré-découpé la chaine (attention, le code est optimisé pour D7, pour les Delphi incluant FastMM en natif, le pré-calcul de l'allocation n'est pas pertinent) au lieu de n appel de GiveOccurence

    Citation Envoyé par Franck SORIANO Voir le message
    Pour simplement lire un fichier texte de 300 Mo, tu peux déjà compter quelques minutes...
    Essaie de faire un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    while not eof do 
    begin
      readln
    end
    Tu abuses, ça ne prend que 8 sescondes pour 300Mo pour 3M de ligne ... sur mon P4 3Ghz ... ça c'est vraiment un temps négligeable !

    tient, un détaile

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    if cur_date <> TempWork.GiveOccurence(3)+' '+TempWork.GiveOccurence(4) then begin
      cur_date := TempWork.GiveOccurence(3)+' '+TempWork.GiveOccurence(4);
    passons sur les GiveOccurence à optimiser, mais, là c'est évident quelle perte de temps, tu concatène une fois et tu compare en refaisant la concaténation ...

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
     work_date := TempWork.GiveOccurence(3)+' '+TempWork.GiveOccurence(4);
     if cur_date <> work_date then begin
      cur_date := work_date
    ou en séparant, mais je ne suis pas convaincu que cela plus rapide, StrCatN n'alloue qu'un seul buffer et recopie les trois chaines, alors qu'en séparant on alloue deux plus petit buffer, donc là j'hésite à savoir lequel est le plus performant (en plus ça peut énormément varier, D7 mieux vaut une grosse allocation que plein de petit, avec FastMM, cela a moins d'importance)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    work_date_3 := TempWork.GiveOccurence(4);
    work_date_4 := TempWork.GiveOccurence(4);
     if cur_date_3 <> TempWork.GiveOccurence(3) and cur_date_4 <> TempWork.GiveOccurence(4) then
    begin
      cur_date_3 := work_date_3
      cur_date_4 := work_date_4
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  13. #53
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 455
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 455
    Points : 24 867
    Points
    24 867
    Par défaut
    Bon, j'aurais pas du (j'ai du vrai travail à faire), mais cela me faisait envie, en m'inspirant de ce que j'avais fait pour ma THashStringList que j'avais revisité pour le bureau, j'ai ajouté à l'unité la classe TTreeHashingObjectList, permettant d'indexer un tableau d'objet dans un arbre ... bon j'ai testé vite fait, ça semble fonctionner ... si quelqu'un avait l'ame généreuse pour l'utiliser et me signaler des bugs que j'ai pas vu dans la TTreeHashingObjectList (eh ! je l'ai pondu en 1h30 ).
    D'ailleurs, si quelqu'un connait un objet similaire, cela m'interesse aussi !

    Code Complet de uHashList, comprenant THashStringList et TTreeHashingObjectList, la tache suivante serait d'utiliser la TTreeHashingObjectList comme objet interne de la THashStringList, ce qui aurait améliorait grandement les performances, faut que j'ajoute pour cela un CodeFromIndex dans TTreeHashingObjectList

    Pour info, Remplir une TStringList Triée avec les nombres de 1 à 565 535, cela dure 75 s
    Remplir la TTreeHashingObjectList, avec le même contenu, cela ne dure que 1.6 s
    La Recherche dans la TStringList Triée ou dans la TTreeHashingObjectList, c'est moins de la milli-secondes
    TStringList Triée, dans les 0.009 ms
    TTreeHashingObjectList, 0.001 ms

    à 3 000 000,
    pour la TStringList Triée, ça dépasse les 20 minutes, j'ai pas eu la patience de terminer, c'est exponentiel (à mon avis, il y en a pour l'heure !)
    pour la TTreeHashingObjectList, c'est nettement mieux 9s ... bon je triche, les valeurs à chaque niveau vont de 1 à 9, mais il n'y a pas photos !


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    580
    581
    582
    583
    584
    585
    586
    587
    588
    589
    590
    591
    592
    593
    594
    595
    596
    597
    598
    599
    600
    601
    602
    603
    604
    605
    606
    607
    608
    609
    610
    611
    612
    613
    614
    615
    616
    617
    618
    619
    620
    621
    622
    623
    624
    625
    626
    627
    628
    629
    630
    631
    632
    633
    634
    635
    636
    637
    638
    639
    640
    641
    642
    643
    644
    645
    646
    647
    648
    649
    650
    651
    652
    653
    654
    655
    656
    657
    658
    659
    660
    661
    662
    663
    664
    665
    666
    667
    668
    669
    670
    671
    672
    673
    674
    675
    676
    677
    678
    679
    680
    681
    682
    683
    684
    685
    686
    687
    688
    689
    690
    691
    692
    693
    694
    {* -----------------------------------------------------------------------------
    Unité : Liste de Hashage
    @author S******* P****** aka Shai Le Troll
    @version 0.4
    // ATTENTION : N'utilisez QUE des unités Delphi,
    // uHashList doit rester INDEPENDANT de la Lib pour les projets qui l'utilise tel que la Persistance V2
    ------------------------------------------------------------------------------ }
     
    unit uHashList;
     
    interface
     
    uses
      Windows, Classes;
     
    type
      EHashStringListError = class(EStringListError);
      {*
      Classe interne des items d'une THashStringList
      }
      THashStringItem = class;
      {*
      Classe qui permet d'associer une Clé de Hashage, une Valeur, une libellé, un Tag et un Objet
      }
      THashStringList = class(TObject)
      private
        FInternalList: TStringList; /// Champ Interne
        FUniqueTags: Boolean;
        function FindCode(const HashCode: string): Integer; /// Recherche un Code
        function FindTag(Tag: Integer): Integer; /// Recherche un Tag
        function GetItemsFromIndex(Index: Integer): THashStringItem;
        function GetCodeSorted: Boolean;
        procedure SetCodeSorted(const Value: Boolean);
      protected
        function GetCodesFromIndex(Index: Integer): string; /// Accesseur en Lecture de CodesFromIndex
        function GetCodesFromTag(Tag: Integer): string; /// Accesseur en Lecture de CodesFromTag
     
        function GetTagFromIndex(Index: Integer): Integer; /// Accesseur en Lecture de TagFromIndex
        procedure SetTagFromIndex(Index: Integer; Tag: Integer); /// Accesseur en Ecriture de TagFromIndex, Mise à Jour uniquement
        function GetTagFromCode(const HashCode: string): Integer; /// Accesseur en Lecture de TagFromCode
        procedure SetTagFromCode(const HashCode: string; Tag: Integer); /// Accesseur en Ecriture de TagFromCode, Ajoute le Code si il n'existe pas !!!
     
        function GetValuesFromIndex(Index: Integer): string; /// Accesseur en Lecture de ValuesFromIndex
        procedure SetValuesFromIndex(Index: Integer; const Value: string); /// Accesseur en Ecriture de ValuesFromTag, Mise à Jour uniquement
        function GetValuesFromCode(const HashCode: string): string; /// Accesseur en Lecture de ValuesFromCode
        procedure SetValuesFromCode(const HashCode: string; const Value: string); /// Accesseur en Ecriture de ValuesFromCode, Ajoute le Code si il n'existe pas !!!
        function GetValuesFromTag(Tag: Integer): string; /// Accesseur en Lecture de ValuesFromTag
        procedure SetValuesFromTag(Tag: Integer; const Value: string); /// Accesseur en Ecriture de ValuesFromTag, Mise à Jour uniquement
     
        function GetCaptionsFromIndex(Index: Integer): string; /// Accesseur en Lecture de CaptionsFromCode
        procedure SetCaptionsFromIndex(Index: Integer; const Caption: string); /// Accesseur en Ecriture de CaptionsFromCode, Mise à Jour uniquement
        function GetCaptionsFromCode(const HashCode: string): string; /// Accesseur en Lecture de CaptionsFromCode
        procedure SetCaptionsFromCode(const HashCode, Caption: string); /// Accesseur en Ecriture de CaptionsFromCode, Ajoute le Code si il n'existe pas !!!
        function GetCaptionsFromTag(Tag: Integer): string; /// Accesseur en Lecture de CaptionsFromTag
        procedure SetCaptionsFromTag(Tag: Integer; const Caption: string); /// Accesseur en Ecriture de CaptionsFromTag, Mise à Jour uniquement
     
        function GetObjectsFromIndex(Index: Integer): TObject; /// Accesseur en Lecture de ObjectsFromIndex
        procedure SetObjectsFromIndex(Index: Integer; Obj: TObject); /// Accesseur en Ecriture de ObjectsFromIndex, Mise à Jour uniquement
        function GetObjectsFromCode(const HashCode: string): TObject; /// Accesseur en Lecture de ObjectsFromCode
        procedure SetObjectsFromCode(const HashCode: string; Obj: TObject); /// Accesseur en Ecriture de ObjectsFromCode, Mise à Jour uniquement
        function GetObjectsFromTag(Tag: Integer): TObject; /// Accesseur en Lecture de ObjectsFromTag
        procedure SetObjectsFromTag(Tag: Integer; Obj: TObject); /// Accesseur en Ecriture de ObjectsFromTag, Mise à Jour uniquement
     
        function GetDataFromIndex(Index: Integer): Pointer; /// Accesseur en Lecture de DataFromIndex
        procedure SetDataFromIndex(Index: Integer; Obj: Pointer); /// Accesseur en Ecriture de DataFromIndex, Mise à Jour uniquement
        function GetDataFromCode(const HashCode: string): Pointer; /// Accesseur en Lecture de DataFromCode
        procedure SetDataFromCode(const HashCode: string; Obj: Pointer); /// Accesseur en Ecriture de DataFromCode, Mise à Jour uniquement
        function GetDataFromTag(Tag: Integer): Pointer; /// Accesseur en Lecture de DataFromTag
        procedure SetDataFromTag(Tag: Integer; Obj: Pointer); /// Accesseur en Ecriture de DataFromTag, Mise à Jour uniquement
     
        function GetCount(): Integer; /// Accesseur en Lecture de Count
     
        property ItemsFromIndex[Index: Integer]: THashStringItem read GetItemsFromIndex; // Accès aux éléments internes de la liste
      public
        constructor Create(); /// Constructeur
        destructor Destroy; override; /// Destructeur
        procedure Clear(); /// Clear
     
        property CodesFromIndex[Index: Integer]: string read GetCodesFromIndex; /// Hash par Index // Liste Triée En Lecture Seule
        property CodesFromTag[Tag: Integer]: string read GetCodesFromTag; /// Hash par Tag // Liste Triée En Lecture Seule
     
        property TagFromIndex[Index: Integer]: Integer read GetTagFromIndex write SetTagFromIndex; /// Tag par Index
        property TagFromCode[const HashCode: string]: Integer read GetTagFromCode write SetTagFromCode; /// Tag par Hash
     
        property ValuesFromIndex[Index: Integer]: string read GetValuesFromIndex write SetValuesFromIndex; /// Valeur par Index
        property ValuesFromCode[const HashCode: string]: string read GetValuesFromCode write SetValuesFromCode; default; /// Valeur par Hash
        property ValuesFromTag[Tag: Integer]: string read GetValuesFromTag write SetValuesFromTag; /// Valeur par Tag
     
        property CaptionsFromIndex[Index: Integer]: string read GetCaptionsFromIndex write SetCaptionsFromIndex; /// Libellé par Index
        property CaptionsFromCode[const HashCode: string]: string read GetCaptionsFromCode write SetCaptionsFromCode; /// Libellé par Hash
        property CaptionsFromTag[Tag: Integer]: string read GetCaptionsFromTag write SetCaptionsFromTag; /// Libellé par Tag
     
        property ObjectsFromIndex[Index: Integer]: TObject read GetObjectsFromIndex write SetObjectsFromIndex; /// Objet par Index
        property ObjectsFromCode[const HashCode: string]: TObject read GetObjectsFromCode write SetObjectsFromCode; /// Objet par Hash
        property ObjectsFromTag[Tag: Integer]: TObject read GetObjectsFromTag write SetObjectsFromTag; /// Objet par Tag
     
        property DataFromIndex[Index: Integer]: Pointer read GetDataFromIndex write SetDataFromIndex; /// Data par Index
        property DataFromCode[const HashCode: string]: Pointer read GetDataFromCode write SetDataFromCode; /// Data par Hash
        property DataFromTag[Tag: Integer]: Pointer read GetDataFromTag write SetDataFromTag; /// Data par Tag
     
        property Count: Integer read GetCount; /// Count
        property CodeSorted: Boolean read GetCodeSorted write SetCodeSorted; /// CodeSorted, trie les codes de Hash, cela améliore les performances !
        property UniqueTags: Boolean read FUniqueTags write FUniqueTags; /// DuplicateTags, ne vérifie pas les tags déjà existants !
      end;
     
      THashStringItem = class(TObject)
      private
        FValue: string;
        FCaption: string;
        FTag: Integer;
        FSubObject: TObject;
        FData: Pointer;
        constructor Create(const AValue: string; const ACaption: string); overload;
        constructor Create(ATag: Integer); overload;
      end;
     
      {*
      Classe interne des items d'une TTreeHashingObjectList
      }
      TTreeHashingObjectItem = class;
      {*
      Classe qui permet d'associer une Clé de Hashage et un Objet
      }
      TTreeHashingObjectList = class(TObject)
      private
        FIndexArray: array[Char] of TTreeHashingObjectItem;
        procedure AddCode(const HashCode: string; Level: Integer; LevelItem: TTreeHashingObjectItem; const Value: TObject);
        function FindCode(const HashCode: string; out Level: Integer; out LevelItem: TTreeHashingObjectItem): Boolean;
      protected
        function GetObjectsFromCode(const HashCode: string): TObject; /// Accesseur en Lecture de ObjectsFromCode
        procedure SetObjectsFromCode(const HashCode: string; const Obj: TObject); /// Accesseur en Ecriture de ObjectsFromCode, Mise à Jour uniquement
      public
        constructor Create(); /// Constructeur
        destructor Destroy; override; /// Destructeur
        procedure Clear(); /// Clear
     
        property ObjectsFromCode[const HashCode: string]: TObject read GetObjectsFromCode write SetObjectsFromCode; default; /// Objet par Hash
      end;
     
      TTreeHashingObjectItem = class(TObject)
      private
        FIndexArray: array of TTreeHashingObjectItem;
        FObject: TObject;
        FChar: Char;
     
        procedure AddCode(const HashCode: string; Level: Integer; const Obj: TObject);
        function FindCode(C: Char): TTreeHashingObjectItem;
      public
        constructor Create(AChar: Char; AObject: TObject); /// Constructeur
        destructor Destroy; override; /// Destructeur
        procedure Clear(); /// Clear
     
      end;
     
    implementation
     
    resourcestring
      SDuplicateTag = 'Tag : %d déjà affecté !';
      SNotUniqueTag = 'Tag n''est pas déclaré commme clé unique !'#13'Accès Refusé !';
     
    { THashStringList }
     
    { THashStringList - Constructeurs }
     
    {* ----------------------------------------------------------------------------}
    constructor THashStringList.Create();
    begin
      inherited;
     
      FInternalList := TStringList.Create();
     
      CodeSorted := True; // Par Défaut, on utilise cette liste pour le Hashage, le tri améliore les performances.
      UniqueTags := False; // Tag Libre par défaut, mais rend l'accès par le tag impossible car non pertinent !
    end;
     
    {* ----------------------------------------------------------------------------}
    destructor THashStringList.Destroy;
    begin
      Clear();
     
      FInternalList.Free();
      FInternalList := nil;
     
      inherited;
    end;
     
    { THashStringList - Méthodes Publiques }
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.Clear();
    var
      Index: Integer;
    begin
      if Self <> nil then
      begin
        for Index := 0 to Count - 1 do
           ItemsFromIndex[Index].Free(); // Libère l'Objet Hash mais pas le SubObject
     
        FInternalList.Clear();
      end;
    end;
     
    { THashStringList - Méthodes Accesseurs }
     
    { THashStringList - Méthodes Accesseurs - Codes }
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetCodesFromIndex(Index: Integer): string;
    begin
      Result := FInternalList.Strings[Index];
    end;
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetCodesFromTag(Tag: Integer): string;
    begin
      Result := FInternalList.Strings[FindTag(Tag)];
    end;
     
    { THashStringList - Méthodes Accesseurs - Tag }
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetTagFromIndex(Index: Integer): Integer;
    begin
      Result := ItemsFromIndex[Index].FTag;
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetTagFromIndex(Index: Integer; Tag: Integer);
    begin
      if UniqueTags and (FindTag(Tag) >= 0) then
        raise EHashStringListError.CreateFmt(SDuplicateTag, [Tag]);
     
      ItemsFromIndex[Index].FTag := Tag;
    end;
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetTagFromCode(const HashCode: string): Integer;
    begin
      Result := ItemsFromIndex[FindCode(HashCode)].FTag;
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetTagFromCode(const HashCode: string; Tag: Integer);
    var
      Index: Integer;
    begin
      if UniqueTags and (FindTag(Tag) >= 0) then
        raise EHashStringListError.CreateFmt(SDuplicateTag, [Tag]);
     
      Index := FindCode(HashCode);
      if Index >= 0 then
      begin
         TagFromIndex[Index] := Tag;
      end
      else
      begin
         FInternalList.AddObject(HashCode, THashStringItem.Create(Tag));
      end;
    end;
     
    { THashStringList - Méthodes Accesseurs - Values }
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetValuesFromIndex(Index: Integer): string;
    begin
      Result := ItemsFromIndex[Index].FValue;
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetValuesFromIndex(Index: Integer; const Value: string);
    begin
      ItemsFromIndex[Index].FValue := Value;
    end;
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetValuesFromCode(const HashCode: string): string;
    begin
      Result := ValuesFromIndex[FindCode(HashCode)];
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetValuesFromCode(const HashCode: string; const Value: string);
    var
      Index: Integer;
    begin
      Index := FindCode(HashCode);
      if Index >= 0 then
      begin
         ValuesFromIndex[Index] := Value;
      end
      else
      begin
         FInternalList.AddObject(HashCode, THashStringItem.Create(Value, ''));
      end;
    end;
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetValuesFromTag(Tag: Integer): string;
    begin
       Result := ValuesFromIndex[FindTag(Tag)];
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetValuesFromTag(Tag: Integer; const Value: string);
    begin
       ValuesFromIndex[FindTag(Tag)] := Value;
    end;
     
    { THashStringList - Méthodes Accesseurs - Captions }
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetCaptionsFromIndex(Index: Integer): string;
    begin
      Result := ItemsFromIndex[Index].FCaption;
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetCaptionsFromIndex(Index: Integer; const Caption: string);
    begin
      ItemsFromIndex[Index].FCaption := Caption;
    end;
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetCaptionsFromCode(const HashCode: string): string;
    begin
      Result := CaptionsFromIndex[FindCode(HashCode)];
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetCaptionsFromCode(const HashCode, Caption: string);
    var
      Index: Integer;
    begin
      Index := FindCode(HashCode);
      if Index >= 0 then
      begin
         ItemsFromIndex[Index].FCaption := Caption;
      end
      else
      begin
        FInternalList.AddObject(HashCode, THashStringItem.Create('', Caption));
      end;
    end;
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetCaptionsFromTag(Tag: Integer): string;
    begin
       Result := CaptionsFromIndex[FindTag(Tag)];
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetCaptionsFromTag(Tag: Integer; const Caption: string);
    begin
       CaptionsFromIndex[FindTag(Tag)] := Caption;
    end;
     
    { THashStringList - Méthodes Accesseurs - Objects }
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetObjectsFromIndex(Index: Integer): TObject;
    begin
      Result := ItemsFromIndex[Index].FSubObject;
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetObjectsFromIndex(Index: Integer; Obj: TObject);
    begin
      ItemsFromIndex[Index].FSubObject := Obj;
    end;
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetObjectsFromCode(const HashCode: string): TObject;
    begin
      Result := ObjectsFromIndex[FindCode(HashCode)];
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetObjectsFromCode(const HashCode: string; Obj: TObject);
    begin
      ObjectsFromIndex[FindCode(HashCode)] := Obj;
    end;
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetObjectsFromTag(Tag: Integer): TObject;
    begin
      Result := ObjectsFromIndex[FindTag(Tag)];
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetObjectsFromTag(Tag: Integer; Obj: TObject);
    begin
      ObjectsFromIndex[FindTag(Tag)] := Obj;
    end;
     
    { THashStringList - Méthodes Accesseurs - Data }
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetDataFromIndex(Index: Integer): Pointer;
    begin
      Result := ItemsFromIndex[Index].FData;
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetDataFromIndex(Index: Integer; Obj: Pointer);
    begin
      ItemsFromIndex[Index].FData := Obj;
    end;
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetDataFromCode(const HashCode: string): Pointer;
    begin
      Result := DataFromIndex[FindCode(HashCode)];
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetDataFromCode(const HashCode: string; Obj: Pointer);
    begin
      DataFromIndex[FindCode(HashCode)] := Obj;
    end;
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetDataFromTag(Tag: Integer): Pointer;
    begin
      Result := DataFromIndex[FindTag(Tag)];
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetDataFromTag(Tag: Integer; Obj: Pointer);
    begin
      DataFromIndex[FindTag(Tag)] := Obj;
    end;
     
    { THashStringList - Méthodes Accesseurs - Count }
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetCount(): Integer;
    begin
      Result := FInternalList.Count;
    end;
     
    { THashStringList - Méthodes Privés }
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.FindCode(const HashCode: string): Integer;
    begin
      Result := FInternalList.IndexOf(HashCode);
    end;
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.FindTag(Tag: Integer): Integer;
    begin
      if not FUniqueTags then
        raise EHashStringListError.Create(SNotUniqueTag);
     
      for Result := 0 to Count - 1 do
        if ItemsFromIndex[Result].FTag = Tag then
          Exit;
     
      Result := -1;
    end;
     
    { THashStringList - Accesseurs Internes }
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetItemsFromIndex(Index: Integer): THashStringItem;
    begin
      Result := THashStringItem(FInternalList.Objects[Index]);
    end;
     
    {* ----------------------------------------------------------------------------}
    function THashStringList.GetCodeSorted: Boolean;
    begin
      Result := FInternalList.Sorted;
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure THashStringList.SetCodeSorted(const Value: Boolean);
    begin
      FInternalList.Sorted := Value;
      // Impossible d'insérer/modifier deux fois le même élément, les accesseurs normalement sécurisent cela !
      if Value then
        FInternalList.Duplicates := dupError;
    end;
     
    { THashStringItem }
     
    { THashStringItem - Constructeurs }
     
    {* ----------------------------------------------------------------------------}
    constructor THashStringItem.Create(const AValue: string; const ACaption: string);
    begin
      inherited Create();
     
      FValue := AValue;
      FCaption := ACaption;
    end;
     
    {* ----------------------------------------------------------------------------}
    constructor THashStringItem.Create(ATag: Integer);
    begin
      inherited Create();
     
      FValue := '';
      FCaption := '';
      FTag := ATag;
    end;
     
    { TTreeHashingObjectList }
     
    { TTreeHashingObjectList - Constructeurs }
     
    {* ----------------------------------------------------------------------------}
    constructor TTreeHashingObjectList.Create();
    begin
      inherited;
    end;
     
    {* ----------------------------------------------------------------------------}
    destructor TTreeHashingObjectList.Destroy;
    begin
      Clear();
     
      inherited;
    end;
     
    { TTreeHashingObjectList - Méthodes Publiques }
     
    {* ----------------------------------------------------------------------------}
    procedure TTreeHashingObjectList.Clear();
    var
      C: Char;
    begin
      if Self <> nil then
      begin
        for C := Low(FIndexArray) to High(FIndexArray) do
          FIndexArray[C].Free();
     
        ZeroMemory(@FIndexArray, SizeOf(FIndexArray)); // Tableau Statique
      end;
    end;
     
    { TTreeHashingObjectList - Méthodes Accesseurs - Objects }
     
    {* ----------------------------------------------------------------------------}
    function TTreeHashingObjectList.GetObjectsFromCode(const HashCode: string): TObject;
    var
      Item: TTreeHashingObjectItem;
      Level: Integer;
    begin
      if FindCode(HashCode, Level, Item) then
        Result := Item.FObject
      else
        Result := nil;
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure TTreeHashingObjectList.SetObjectsFromCode(const HashCode: string; const Obj: TObject);
    var
      Item: TTreeHashingObjectItem;
      Level: Integer;
    begin
      if FindCode(HashCode, Level, Item) then
      begin
        Item.FObject := Obj;
      end
      else
      begin
        AddCode(HashCode, Level, Item, Obj);
      end;
    end;
     
    { TTreeHashingObjectList - Méthodes Privées }
     
    {* ----------------------------------------------------------------------------}
    procedure TTreeHashingObjectList.AddCode(const HashCode: string; Level: Integer; LevelItem: TTreeHashingObjectItem; const Value: TObject);
    var
      LevelChar: Char;
    begin
      if not Assigned(LevelItem) then
      begin
        Level := 1;
        LevelChar := HashCode[Level];
        LevelItem := FIndexArray[LevelChar];
        if not Assigned(LevelItem) then
        begin
          LevelItem := TTreeHashingObjectItem.Create(LevelChar, Value);
          FIndexArray[LevelChar] := LevelItem;
        end;
      end;
     
      if Level < Length(HashCode) then
        LevelItem.AddCode(HashCode, Level + 1, Value);
    end;
     
    {* ----------------------------------------------------------------------------}
    function TTreeHashingObjectList.FindCode(const HashCode: string; out Level: Integer; out LevelItem: TTreeHashingObjectItem): Boolean;
    var
      LenCode: Integer;
      SubLevelItem: TTreeHashingObjectItem;
    begin
      LenCode := Length(HashCode);
      Result := LenCode > 0;
      if Result then
      begin
        Level := 1;
        LevelItem := FIndexArray[HashCode[Level]];
        while (Level < LenCode) and Assigned(LevelItem) do
        begin
          Inc(Level);
          SubLevelItem := LevelItem.FindCode(HashCode[Level]);
          if Assigned(SubLevelItem) then
          begin
            LevelItem := SubLevelItem
          end
          else
          begin
            Result := False;
            Dec(Level);
            Exit;
          end;
        end;
        Result := Assigned(LevelItem);
      end;
    end;
     
    { TTreeHashingObjectItem }
     
    { TTreeHashingObjectItem - Constructeurs }
     
    {* ----------------------------------------------------------------------------}
    constructor TTreeHashingObjectItem.Create(AChar: Char; AObject: TObject);
    begin
      inherited Create();
     
      FChar := AChar;
      FObject := AObject;
    end;
     
    {* ----------------------------------------------------------------------------}
    destructor TTreeHashingObjectItem.Destroy;
    begin
      Clear();
     
      inherited;
    end;
     
    {* ----------------------------------------------------------------------------}
    procedure TTreeHashingObjectItem.Clear;
    var
      I: Integer;
    begin
      for I := Low(FIndexArray) to High(FIndexArray) do
        FIndexArray[I].Free();
     
      SetLength(FIndexArray, 0);
    end;
     
    { TTreeHashingObjectItem - Méthodes Privées }
     
    {* ----------------------------------------------------------------------------}
    procedure TTreeHashingObjectItem.AddCode(const HashCode: string; Level: Integer; const Obj: TObject);
    var
      IdxLen: Integer;
      LenCode: Integer;
    begin
      IdxLen := Length(FIndexArray);
      SetLength(FIndexArray, IdxLen + 1);
     
      LenCode := Length(HashCode);
      if Level < LenCode then
      begin
        FIndexArray[IdxLen] := TTreeHashingObjectItem.Create(HashCode[Level], nil);
        FIndexArray[IdxLen].AddCode(HashCode, Level + 1, Obj);
      end
      else
        FIndexArray[IdxLen] := TTreeHashingObjectItem.Create(HashCode[Level], Obj);
    end;
     
    {* ----------------------------------------------------------------------------}
    function TTreeHashingObjectItem.FindCode(C: Char): TTreeHashingObjectItem;
    var
      I: Integer;
    begin
      for I := Low(FIndexArray) to High(FIndexArray) do
      begin
        Result := FIndexArray[I];
        if Result.FChar = C then
          Exit;
      end;
     
      Result := nil;
    end;
     
    end.
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  14. #54
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    Tu abuses, ça ne prend que 8 sescondes pour 300Mo pour 3M de ligne ... sur mon P4 3Ghz ... ça c'est vraiment un temps négligeable !
    J'avoue ne pas avoir testé.
    Cependant si je fait le test maintenant sur mon Core 2 Quad 2,66 Ghz, 4Go de DDR3 et un disque VelocyRaptor à 10000 tr/min en SATA 2, je met 7s si je relit le fichier de test immédiatement après l'avoir généré.
    Par contre, si je reboote pour vider les caches du disque et que je refais le test, je passe tout de suite à 28s.
    Donc sur une machine moins puissante, tu peux facilement atteindre la minute. Surtout si en plus ton disque est fragmenté...

    Remarque, ça veut quand même dire que le parsing du CSV peut se faire en moins de 10s.

    Bon, j'aurais pas du (j'ai du vrai travail à faire), mais cela me faisait envie, en m'inspirant de ce que j'avais fait pour ma THashStringList que j'avais revisité pour le bureau, j'ai ajouté à l'unité la classe TTreeHashingObjectList, permettant d'indexer un tableau d'objet dans un arbre ... bon j'ai testé vite fait, ça semble fonctionner ... si quelqu'un avait l'ame généreuse pour l'utiliser et me signaler des bugs que j'ai pas vu dans la TTreeHashingObjectList (eh ! je l'ai pondu en 1h30 ).
    D'ailleurs, si quelqu'un connait un objet similaire, cela m'interesse aussi !

    Code Complet de uHashList, comprenant THashStringList et TTreeHashingObjectList, la tache suivante serait d'utiliser la TTreeHashingObjectList comme objet interne de la THashStringList, ce qui aurait améliorait grandement les performances, faut que j'ajoute pour cela un CodeFromIndex dans TTreeHashingObjectList
    Tu m'intéresses. Je vais faire quelques tests.

  15. #55
    Expert confirmé

    Profil pro
    Leader Technique
    Inscrit en
    Juin 2005
    Messages
    1 756
    Détails du profil
    Informations personnelles :
    Âge : 46
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Leader Technique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2005
    Messages : 1 756
    Points : 4 170
    Points
    4 170
    Par défaut
    Bon je viens de jeté un coup d'oeil rapide sur THashStringList.
    Désolé, mais je ne vois pas en quoi ta classe constitue une liste hashcodée.
    C'est un vulgaire dictionnaire basé sur un TStringList pour indexer les clés.
    Lorsque tu veux ajouter une valeur, tu appelles FindCode pour connaitre la position de la clé dans la liste, qui se contente de faire un IndexOf sur le TStringList.
    Une table de hashage ce n'est pas ça du tout.

    C'est plutôt un truc du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    const CAPACITE = 1000;
    var 
      tbHash : array[0..CAPACITE-1] of string;
      hashCode : cardinal;
     
    begin
      // ---- Ajout de la chaine dans la table
      hashCode := hash(MaCleAAjouter) mod CAPACITE;
      tbHash[hashCode] := LaValeurAssocieALaCle;
     
      // ----- recherche d'une chaine dans la table :
      hashCode := hash(MaCleAAjouter) mod CAPACITE;
      result := tbHash[hashCode];
    Le principe d'une table de hashage, c'est que tu calcules un hash rapide à partir de la valeur de la clé (par exemple, un CRC32 (mais il y a mieu)), qui te donneras directement la position à laquelle tu vas stocker la valeur dans ton tableau.
    Tu appliques le même principe lorsque tu recherches une clé : La fonction de hashage te dit à quelle position la clé doit se trouver, et donc tu va la chercher à cette position directement.
    Comme la fonction de hashage te donne directement la position de stockage, la recherche est indépendante du nombre d'éléments déjà stockés dans la table...

    La où les choses se compliquent, c'est que dans la pratique, on obtient nécéssairement des collisions dans les valeurs des hashs, donc la table hashcodée doit définir une stratégie pour les gérers.
    La technique utilisée par TStringHash dans IniFiles, consiste à stocker toutes les clés qui donnent le même hash, à la même position dans la table, dans une liste chaînée.
    Mais il existe d'autres stratégies.

  16. #56
    Membre chevronné Avatar de chaplin
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 215
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 215
    Points : 1 819
    Points
    1 819
    Par défaut
    if (copy(TempWork.GiveOccurence(3), 1, 6) = '200808') or
    (copy(TempWork.GiveOccurence(4), 1, 6) = '200808') or
    (copy(TempWork.GiveOccurence(4), 1, 6) = '200809') or
    (copy(TempWork.GiveOccurence(4), 1, 6) = '200809')


    C'est ni plus ni moins du bricolage, il faut utiliser des champs au format date. Il t'entraîne à faire des conneries. Tes classes doivent être paramétrables.
    Tu dois être professionnel jusqu'au bout. Chaque fois qu'il te propose un truc que te sens hasardeux, tu fais la contre position et tu le notes dans tes fiches de travail, tu les photocopies.

    Ne cherche pas à le convaincre, contente toi de faire des propositions par des petits programmes pour argumenter
    tes dires que tu enregistres sur une clé. Il faut arriver à obtenir des traces écrites de ses demandes.
    Nous t'avons donné les moyens de réussir, à toi d'en faire bon usage.

    En voulant, optimiser tes programmes à fond, tu vas inconsciement négliger les contrôles (consommation de ressource), du coup commettre des erreurs.
    Dis toi bien que ce sont des méthodes qui n'ont pour unique but de te planter à ton insu. Je parle par experience.
    Autrement dit on te fait faire des projets sans te donner les moyens de les réussir correctement.
    Seulement, si tu prouves que c'est lui qui t'as donner les moyen pour échouer, il a tout faux.

  17. #57
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 455
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 455
    Points : 24 867
    Points
    24 867
    Par défaut
    Citation Envoyé par Franck SORIANO Voir le message
    Bon je viens de jeté un coup d'oeil rapide sur THashStringList.
    C'est con mais il fallait regarder la TTreeHashingObjectList, que j'ai mis à la fin de l'unité (la syntaxe d'utilisation étant similaire et l'objectif proche, mise à part les performances améliorées grace à l'arbre)

    Citation Envoyé par Franck SORIANO Voir le message
    Désolé, mais je ne vois pas en quoi ta classe constitue une liste hashcodée.
    c'est une association clé-valeur, ça y ressemble mais
    effectivement je n'ai pas fait d'index numérique car il existe la bien nommé THashedStringList (dont tu expliques très bien le principe ), il n'y avait aucun intéret de refaire la même chose ... mon but était d'approcher la syntaxe PHP des tableaux associatifs ... le nom de la classe est peut-être mal choisi, mais cela m'a inspiré à ce moment ! Si tu as un autre nom, je suis preneur ... TAssociateStringList ??? non c'est moche !

    Maintenant, si l'on en revenait au sujet de Qays, où la TTreeHashingObjectList pourrait faire ses preuves en performances


    Sinon, chaplin, bonnes remarques
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  18. #58
    Membre actif
    Profil pro
    Inscrit en
    Août 2004
    Messages
    696
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2004
    Messages : 696
    Points : 225
    Points
    225
    Par défaut
    merci a toutes et à tous pour vos conseils et soutient.

    JE clos ce post en résolu je vais utiliser une bdd intermédiaire (voir un dataset) je vais étudier la question.

    A tout de suite pour de nouvelles aventures (questions)

+ Répondre à la discussion
Cette discussion est résolue.
Page 3 sur 3 PremièrePremière 123

Discussions similaires

  1. [JDOM] Problème pour parser un fichier XML [débutant]
    Par adn013 dans le forum Format d'échange (XML, JSON...)
    Réponses: 2
    Dernier message: 16/05/2007, 12h00
  2. fonction pour parser un fichier
    Par Dirty Harry dans le forum Langage
    Réponses: 7
    Dernier message: 30/01/2007, 05h13
  3. Réponses: 2
    Dernier message: 14/09/2006, 15h22
  4. problème pour parser un fichier xml avec XML::Simple
    Par black_code dans le forum Modules
    Réponses: 3
    Dernier message: 30/01/2006, 19h32
  5. [SAX] Probleme pour parser un fichier
    Par aymron dans le forum Format d'échange (XML, JSON...)
    Réponses: 2
    Dernier message: 25/11/2005, 15h48

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo