Lecture de fichier ligne à ligne

Partagez votre expérience de PureBasic avec les autres utilisateurs.
Ollivier
Messages : 4197
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Message par Ollivier »

Mhmh... Le "bordel" à l'intérieur de la procédure c'est principalement un ensemble de sécurités. Si tu fais une DLL, tu t'en fous de ce "bordel". La seule chose à savoir ce sont les 2 structures nécessaire (tu connais la deuxième) :


_________________________
1ère structure

Code : Tout sélectionner

Structure BuildArrayInfo

   FileName.S
   ExecStatus.I

   *SeqBegin
   *SeqEnd
   SeqSize.I ; En octet

   *ArrayTable
   *ArrayTableEnd
   ArrayTableSize.I ; (nombre de pointeurs)

   LineMeanLength.I ; Taille moyenne d'une ligne
   
EndStructure 
Cette structure je l'ai rajouté parce que tu voulais spécialement une DLL. Or pour qu'une fonction DLL te rende PLUSIEURS infos, ben t'es bien obligé de créer une structure!

Avant d'exécuter LoadStringArray(), tu lui cuisines cette structure comme dans l'exemple (allocation + réglages), puis tu exécutes la fonctions. Après cela tu as toutes les infos nécessaires pour gérer et libérer l'espace alloué par la fonction.

La structure BuildArrayInfo contient 9 champs:
1 champ d'entrée
7 champs de sortie
1 champ mixte

\FileName.S (ENTREE) Nom du fichier texte à charger

\LineMeanLength (ENTREE/SORTIE) Fixe la longueur moyenne d'une ligne en caractères. Ceci est dû au fait que l'on charge un fichier texte sans savoir combien de lignes de textes sont stockées dans ce fichier. Si = 0 en entrée, la fonction fixe une valeur par défaut de 10 caractères. (Donc, en gros, tu t'en fous de cette valeur, le "bordel interne" s'en charge pour toi)

\ExecStatus (SORTIE) Retourne l'état d'exécution de la fonction
= 0 : a échoué, n'a pas pu ouvrir le fichier texte demandé
= 1 : a échoué, n'a pas pu allouer le bloc mémoire RAM de la taille du fichier
= 3 : a échoué, n'a pas pu charger le fichier en mémoire
= 7 : a échoué, n'a pas pu créer la table des pointeurs de chaîne
= 15 : a réussi
Ces valeurs sont tirées des 4 constantes suivantes (convention perso) :
#FileOpened = 1
#MemAllocated = 2
#FileLoaded = 4
#TableCreated = 8

\SeqBegin (SORTIE) Adresse du bloc mémoire (="séquence") contenant toutes les chaînes

\SeqEnd (SORTIE) Adresse de fin de la séquence

\SeqSize (SORTIE) Taille en octets de la séquence

\ArrayTable (SORTIE) Adresse du bloc mémoire (="table") contenant tous les pointeurs de chacune des chaînes

\ArrayTableEnd (SORTIE) Adresse de la fin de la table

\ArrayTableSize (SORTIE) Quantité totale de chaînes (= quantité totale de pointeurs de chaîne)


_____________________________
2ème structure

Code : Tout sélectionner

Structure TextLineInfo
   TextLine.S[1 << 24]
EndStructure
Celle-là, tu la connais, du moins je l'utilise dans le code d'avant. Elle te permet d'accéder aux chaînes chargées.

Code : Tout sélectionner

Define *Chaine.TextLineInfo
Define ArrayInfo.BuildArrayInfo
ArrayInfo\FileName = "MONFICHIERTEXTE.TXT"
LoadArrayString(ArrayInfo)
*Chaine = ArrayInfo\ArrayTable

*Chaine\TextLine[67891] = "Salut" ; Accès à l'une des chaînes
____________________________


D'après ta dernière phrase, ce type de fonctionnement est parfaitement adapté à ton besoin (tu ne veux pas modifier la taille des chaînes mais seulement chercher dedans)

Je ne fais que répondre à ta demande!

Aussi, si tu persévères avec ce procédé, je te présente cette procédure ASM que j'avais posté pour Kwaï il y a un moment.

Elle se moque des caractères système. Elle n'a pas de frontières de recherche dans la mémoire, donc il faut la limiter (Limit = \SeqEnd, dans ton cas précis, c'est la fin du bloc contenant toutes les chaîne).

Code : Tout sélectionner

Procedure.L FindChar(*Index, Ascii.I, Limit.I)
; Retourne l'adresse mémoire du caractère Ascii recherché
; dans la zoné mémoire entre (*Index) et (*Index + Limit)
   ! mov   eax, [p.v_Ascii]             
   ! mov   edi, [p.p_Index]
   ! mov   ecx, [p.v_Limit]
   ! mov   ebx, edi             
   ! cld                       
   ! repne scasb
   ! mov   eax, edi
   ! dec   eax
   ProcedureReturn
EndProcedure 

;Exemple
X.S = "Bonjour, voici une chaîne"
Debug Chr(PeekC(FindChar(@X, Asc("î"), @X + Len(X) - 1) ) )
Par exemple, pour chercher le mot "macro", il te faut trouver le "m" ou le "M" puis le "a" ou le "A", etc...

C'est aussi cette séquence ASM qui sert de départ pour créer la fonction. Aussi, si ma procédure LoadStringArray() te semble trop usine à gaz, ce qui peut se comprendre, ben fais la même à partir de ce bout de code-ci! C'est vrai que chacun a ses manières de développer son code...

Maintenant, si tu veux plus performant, ben il y a les les tables associatives. Sujet récent ici. Mais dans tous les cas, il y a des difficultés.

Ce qu'il faut c'est ne pas désespérer. Tu n'as pas appelé ton projet Moëbius pour rien. Le jeu en vaut la chandelle!

Ollivier
Avatar de l’utilisateur
Progi1984
Messages : 2659
Inscription : mar. 14/déc./2004 13:56
Localisation : France > Rennes
Contact :

Message par Progi1984 »

Merci Ollivier, je n'ai finalement pas désespérer :p Et je pense que avec ton aide, je peux grave aider à optimiser Moebius.

Pour la première fonction, (180 000 lignes à parser), suis passé de 996713 ms à 160 ms.

Par contre tite question, comment savoir à quelle valeur mettre \LineMeanLength quand on ne sait pas quel fichier et quel contenu on va recevoir ?
Avatar de l’utilisateur
Progi1984
Messages : 2659
Inscription : mar. 14/déc./2004 13:56
Localisation : France > Rennes
Contact :

Message par Progi1984 »

Je confirme que ce code est une pure merveille.

Pour un code ASM de 188 282 lignes, je suis passé de 2h05 de building à seulement 19min de compilation.

Mais bon, il y a encore de quoi l'optimiser.

Sur ces 19 minutes,
10 sont passés sur cette fonction
9 sur cette partie de code : ICI à LA

Donc si ca ne t'ennuie pas de me filer un coup de main pour optimiser ce code, je suis tout ouïe. :)
Ollivier
Messages : 4197
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Message par Ollivier »

@Progi1984

Quel est le nom de la procédure du tout dernier code publié par ma maudite personne sur cette page?

Ollivier
Avatar de l’utilisateur
Progi1984
Messages : 2659
Inscription : mar. 14/déc./2004 13:56
Localisation : France > Rennes
Contact :

Message par Progi1984 »

Ouh là, pose pas des questions trop durs ! lol J'ai à peine dormi que quelques heures...

J'aurais pas dit maudite, j'aurais dit généreuse et sympathique.

Ta dernière fonction aprés relecture complète du post est FindChar.
[EDIT 10:30] Mais par contre, elle retourne simplement le caractère que l'on recherche ? Donc je ne vois aps comment l'utiliser dans mon code.
Ollivier
Messages : 4197
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Message par Ollivier »

Bonne réponse! C'est FindChar du latin FindCharacter qui signifie « trouve le caractère »

Et là, tu as 3 arguments:

*Index
Ascii.L
*Limit

Le but ce n'est pas que je t'offre une procédure toute faite et que tu me dises merci à sa réception, même si c'est déjà un très bon système social.

Le but ultime et beaucoup plus constructif est que si le Ciel m'emporte, tu puisses utiliser à bonne joie et sans aucune frustration, voire toi-même transmettre ce que j'aurai pu te faire découvrir.

Alors, laissons de côté ton projet un temps soit peu et arrêtons-nous sur ces quelques lignes si peu nombreuses qui font des jolies choses. Je vais te les commenter et te poser une autre question. Prends ton temps pour y répondre exactement...

Code : Tout sélectionner

Procedure.L FindChar(*Index, Ascii.I, Limit.I)

; Retourne l'adresse mémoire du caractère Ascii recherché
; dans la zoné mémoire entre (*Index) et (*Index + Limit)

   ! mov   eax, [p.v_Ascii]  ; Charge le caractère recherché             

   ! mov   edi, [p.p_Index] ; Charge l'adresse de départ

   ! mov   ecx, [p.v_Limit] ; Charge la quantité maximale
                            ; d'octets à fouiller

   ! mov   ebx, edi ; Sauvegarde l'adresse de départ             

   ! cld   ; Donne le sens de recherche
           ; Cld = ordre croissant (par défaut)
           ; Std = ordre décroissant (dans ce cas,
           ; nécessite de remettre Cld
           ; en fin de procédure sinon crash dans les
           ; instructions PureBasic suivantes)

   ! repne scasb ; Démarre la mitrailleuse automatique 
                 ; qui ne s'arrête
                 ; QUE
       ; 1) Lorsque le caractère recherché (stocké dans eax)
       ;     est trouvé
       ; OU BIEN
       ; 2) Lorsque la fouille a échoué et on a atteint
       ;    la limite fixée

   ! mov   eax, edi ; Mets l'adresse dans eax pour que
                    ; ProcedureReturn retourne cette adresse
   ! dec   eax      ; Décrémente cette valeur car notre
                    ; mitrailleuse ne nous renvoie pas
                    ; exactement l'adresse du caractère
                    ; trouvé mais l'adresse +1 (héhé, elle
                    ; est capricieuse!!!)
                    
   ProcedureReturn  ; Retourne eax pour notre plus grand
                    ; bonheur
EndProcedure
Alors, moi je suis un vicieux!! Pour bien que tu comprennes que ces huit
lignes asm ne pas sorcières et qu'elle sont à la portée de ta compréhension, j'ai glissé une ligne qui n'a rien à foutre ici!!!

Seules sept lignes sont suffisantes pour obtenir exactement le même résultat.

Ma question est (et prends ton temps pour réfléchir et éventuellement me poser des questions s'il te manque des précisions):

Quelle est la ligne qui n'a strictement rien d'utile dans cette proédure???
1? 2? 3? 4? 5? 6? 7? Ou 8?

(Un indice, ce n'est pas la 5 : Cld est très bien ici...)
Avatar de l’utilisateur
Progi1984
Messages : 2659
Inscription : mar. 14/déc./2004 13:56
Localisation : France > Rennes
Contact :

Message par Progi1984 »

La ligne inutile est la 4. Car à quoi sert de sauvegarder une valeur si elle est inutilisée aprés.

Et aprés quelques recherches, j'ai trouvé l'instruction ADD, j'en ai déduis que l'instruction SUB devait exister.

J'ai remplacé "!dec eax" par "! sub eax, ebx" et il me retourne la position du curseur si je ne me trompe pas.

Code : Tout sélectionner

Procedure.L FindChar(*Index, Ascii.I, Limit.I)

; Retourne l'adresse mémoire du caractère Ascii recherché
; dans la zoné mémoire entre (*Index) et (*Index + Limit)

   ! mov   eax, [p.v_Ascii]  ; Charge le caractère recherché             
   ! mov   edi, [p.p_Index] ; Charge l'adresse de départ
   ! mov   ecx, [p.v_Limit] ; Charge la quantité maximale
                            ; d'octets à fouiller
   ! mov   ebx, edi ; Sauvegarde l'adresse de départ             
   ! cld   ; Donne le sens de recherche
           ; Cld = ordre croissant (par défaut)
           ; Std = ordre décroissant (dans ce cas,
           ; nécessite de remettre Cld
           ; en fin de procédure sinon crash dans les
           ; instructions PureBasic suivantes)
   ! repne scasb ; Démarre la mitrailleuse automatique
                 ; qui ne s'arrête
                 ; QUE
       ; 1) Lorsque le caractère recherché (stocké dans eax)
       ;     est trouvé
       ; OU BIEN
       ; 2) Lorsque la fouille a échoué et on a atteint
       ;    la limite fixée
   ! mov   eax, edi ; Mets l'adresse dans eax pour que
                    ; ProcedureReturn retourne cette adresse
   ! sub eax, ebx
   ProcedureReturn; Retourne eax pour notre plus grand bonheur
EndProcedure 

;Exemple
X.S = "Bonjour, voici une chaîne"
ReturnVal.l = FindChar(@X, Asc("î"), @X + Len(X) - 1)
Debug ReturnVal
Debug Mid(X, ReturnVal, 1)
Ollivier
Messages : 4197
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Message par Ollivier »

Progi1984 a écrit :La ligne inutile est la 4. Car à quoi sert de sauvegarder une valeur si elle est inutilisée aprés.
Bonne réponse! Excellente même.
Progi1984 a écrit :Et aprés quelques recherches, j'ai trouvé l'instruction ADD, j'en ai déduis que l'instruction SUB devait exister.

J'ai remplacé "!dec eax" par "! sub eax, ebx" et il me retourne la position du curseur si je ne me trompe pas.
Là, tu commences à saisir le côté "simple" et carré de l'Assembleur!!

Par contre, cette "position" là t'es actuellement inutile (mais pas pour longtemps). En gros, il te faut une macro asm faisant la recherche (avec la "mitrailleuse") et te retournant deux choses:
1) Le numéro de ligne
2) L'adresse mémoire

Pour le (2) c'est bon : c'est le registre corrigé EDI.

Pour le (1), c'est une autre paire de manche! Mon avis est qu'il va falloir profiter du fait que le chargement en mémoire du tableau mette moins d'une seconde pour lui rajouter un système de référencement de ligne.

Il faut pouvoir obtenir, à partir d'une adresse mémoire, "le" numéro de ligne qui contient cette adresse. Pour ça, ben on ne va pas se faire chier! Vu qu'on a de la mémoire vive à revendre, un tableau contenant tous les numéros de lignes pour chaque caractère, ben ça n'est pas du luxe, mais une nécessité.

Qu'est-ce que tu en penses? Est-ce que tu saisis la dernière technique?

Ollivier
Avatar de l’utilisateur
Progi1984
Messages : 2659
Inscription : mar. 14/déc./2004 13:56
Localisation : France > Rennes
Contact :

Message par Progi1984 »

Ouh là, j'ai rien compris à partir du deuxieme quote.

Par contre, quelle est la direction où l'on va ?
Ollivier
Messages : 4197
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Message par Ollivier »

@Progi1984

Je vais te lâcher jusqu'à samedi soir!

On va vers la création d'un tampon de référencement.

Code : Tout sélectionner

;Pour y voir plus clair à ma ptite idée...

A$(0) = "Hou"
A$(1) = "le"
A$(2) = "bordel"

;Pseudo-référencement

R$(0) = "000"
R$(1) = "11"
R$(2) = "222222"
Si ces 2 tableaux étaient statiques et si chacune de leurs chaînes étaient collées les unes aux autres, on voit que le deuxième n'est qu'une copie du premier sauf que chaque caractère contient son numéro de ligne au lieu de son contenu initial.

Ceci évitera une recherche dichotomique.

Ollivier
Avatar de l’utilisateur
Progi1984
Messages : 2659
Inscription : mar. 14/déc./2004 13:56
Localisation : France > Rennes
Contact :

Message par Progi1984 »

Ouh là là ! Excuse moi, je n'arrive pas à voir l'intérêt que cela va m'apporter dans l'optimisation de mon code.
Ollivier
Messages : 4197
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Message par Ollivier »

Ben... Comme d'hab : un chouilla plus rapide! Si on ne peut plus s'amuser...

Ollivier
Avatar de l’utilisateur
Progi1984
Messages : 2659
Inscription : mar. 14/déc./2004 13:56
Localisation : France > Rennes
Contact :

Message par Progi1984 »

Pas de pb pour rendre plus rapide, et de m'apprendre quelques morceaux d'ASM :), mais je me posais la question : "Quel partie de mon code cela pourrait il remplacer ?"
Ollivier
Messages : 4197
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Message par Ollivier »

Ben... Euh... Mh... Comment dirais-je... Au moins un des trois liens que tu m'as mis. J'en ai lu qu'un et... Ben... Au moins dans celui-là, tout est à refaire... C'est pour ça que j'ai reporté parce que, une simple lecture de fichier, ça va. Mais là, on rentre dans du dur. Et je risque de faire un code bien "trash" en Asm que, même moi j'aurai du mal à modifier par la suite.

Tu utilise la méthode de base pour faire un programme. Seulement pour aller vite, il faut se mettre "à la place" du CPU et organiser au max les instructions. Par exemple, là où je vois des If a$="macro" puis plus loin If a$="germaine", ben en Asm, c'est plutôt "Qu'est-ce que je fais si je vois un m ou un g" (les initiales de macro et germaine) bref, on entre dans le monde féérique du "moins tu comprends et plus tout va bien!".

Ollivier
Ollivier
Messages : 4197
Inscription : ven. 29/juin/2007 17:50
Localisation : Encore ?
Contact :

Message par Ollivier »

@Progi1984

Ici Oxo. Je ne sais pas comment je vais faire mais je compte bien continuer le sujet comme prévu dans le créneau horaire beuvresco-copulatoire.

A vous.

Ollivier
Répondre