Un code de FWeil - Analyse d'un fichier texte

Partagez votre expérience de PureBasic avec les autres utilisateurs.
comtois
Messages : 5186
Inscription : mer. 21/janv./2004 17:48
Contact :

Un code de FWeil - Analyse d'un fichier texte

Message par comtois »

J'ai reçu un petit message très sympa , je vous laisse le découvrir à votre tour .

Je vais étudier ce code très bien commenté , un bon petit tut :)

PS:
Je vois que François est maintenant parmi nous , alors je vais le laisser mettre lui même en place le deuxième code qu'il m'avait envoyé :)

Fweil a écrit :Bonsoir,

Voici mon petit travail du jour spécialement pour le forum FR. Je n'arrive pas à me pluger dessus, donc je te le passe par email depuis là.

Je te propose de le poster en mon nom + tard si tu veux bien.

Dis-moi si cela répond un peu aux attentes des gens sur le forum FR.

Je te laisse découvrir le source, ce qu'il fait, et surtout tous les commentaires et les noms de variables ... j'ai fait un effort !

Code : Tout sélectionner

#Longueur_chaine_mot = 25 ; Vous connaissez des mots de plus de 25 caractères vous ? 

Structure Octets_de_chaine ; Cette structure est utilisée pour l'équivalence des octets d'une chaine avec un tableau d'octets 
  Octets.b[#Longueur_chaine_mot] 
EndStructure 

Mot.s = Space(#Longueur_chaine_mot) ; On définit une zone mémoire suffisante pour placer des mots 

*Mot.Octets_de_chaine = @Mot ; *Mot\Octets[i] est équivalent à Mid(Mot, i - 1, 1) 

NewList Tous_les_mots.s() ; Deux listes : l'une contenant tous les mots du fichier d'entrée 
NewList Mots_uniques.s() ; l'autre prévue pour la liste unique des mots (c'est à dire sans doublons) 

Dim Table_ASCII_translatee.b(255) ; La table des caractères ASCII est reprise position par position pour traduire tous les caractères non significatifs du texte (voir section Data plus bas) 
; 
; Programme principal 
; 
; Le principe est de lire un fichier texte et d'en extraire les mots. Pour ce faire et dans les meilleures conditions de performance, on 
; réserve une zone mémoire dans laquelle on place la totalité du fichier (il n'y pas de limite autre que celle de la mémoire vive + virtuelle 
; Ce qui signifie que l'on peut analyser un fichier de plusieurs méga-octets si l'on veut. 
; 
; Comme l'analyse des mots consiste en premier lieu à retirer les caractères non-significatifs, on va placer deux pointeurs dans la zone mémoire. 
; Le premier est le pointeur source, qui scrute la totalité de la zone mémoire. Chaque caractère pointé par l'adresse source est vérifié pour savoir si il est significatif. 
; Si tel est le cas, le caractère est copié à l'adresse destination, sinon le pointeur destination n'est simplement pas incrémenté et on passe au caractère suivant avec le pointeur source. 
; 
; Le second pointeur est le pointeur de destination qui permet de savoir où placer la copie translatée du caractère source. A la fin de la lecture de la zone mémoire, 
; on aura donc lu la totalité des caractères du fichier placés en méoire, mais on aura copié un peu moins de caractères, puisque l'on ne retient que les caractères 
; significatifs. 
; 
; Le pointeur d'adresse destination sera donc à la fin de la lecture la longueur 'utile' de la zone mémoire. 
; 
; Ce système est extrêment rapide et efficace. Et il permet sirtout de pnredre en charge des fichiers de longueur quelconque sans limitation de longueur de texte. 
; 
  Bible_fichier_source.s = "C:\RECUP\Frw\Bible\BibleJNDdoc-Bible.txt" ; Fichier source à adapter : j'ai pris http://www.bibliquest.org/Bible/BibleJNDdoc-Bible.doc et converti en texte. 
  ; 
  ; Un commentaire : je prends la Bible sans provocation ni intention particulière. C'est un texte qui a l'avantage d'être facile à trouver sur le Web et qui est d'une longueur biblique ! 
  ; Le fichier texte ici fait 4,6 MO chez moi. il y a environ 850K mots dont 21,5K mots uniques. 
  ; 
  ; L'ensemble de l'analyse m'a pris moins de 10 minutes d'exécution sur mon 1,2GHz. 
  ; Cela peut paraître un peu long si l'on regarde le temps de première phase qui est de l'ordre de une à quelques secondes, mais ce qui coûte 
  ; le plus cher est l'analyse des mots unqiues. 
  ; 
  ; Si vous ne souhaitez faire que la liste des mots tout confondus, le temps d'exécution est vraiment impressionnant. 
  ; 
  ; Je propose de lancer un concours, en fait c'est un sujet de potache mais il est toujours bon à prendre pour apprendre. 
  ; "Quelles optimisations peut on apporter avec Purebasic ?" 
  ; 
  ; Il y a plusieurs voies d'exploration pour rendre le résultat plus rapide, mais là je vous laisse un peu mariner quand même. 
  ; 
  ; Ah, et si vous trouvez des erreurs, je ne suis pas Dieu, la Bible ce n'est pas moi l'auteur ! 
  ; 
  ; Au fait j'ai fait un gros effort pour vous écrire un tut en français. Je suis un peu désolé de ne pas être trop présent dans ma langue maternelle 
  ; mais j'ai tellement de choses à dire et échanger que je suis plus souvent sur le forum WW. 
  ; 
  ; Je dédie ce clin d'oeil de petit programme tout mignon à tous les codeurs Purebasic en général et surtout à Ced en particulier. 
  ; 
  
  Bible_fichier_tous_les_mots.s = "C:\RECUP\Frw\Bible\Bible_tous_les_mots.txt" ; Fichier de sortie des tous les mots 
  Bible_fichier_mots_uniques.s = "C:\RECUP\Frw\Bible\Bible_mots_uniques.txt" ; Fichier de sortie des mots sans doublons 
  For i = 0 To 255 
    Read Table_ASCII_translatee(i) ; Chargement de la table ASCII translatée depuis la zone Data 
  Next 
  Code_precedent.b = ' ' ; On initialise une variable pour stocker la valeur du caractère précédent 
  Adresse2 = -1 ; On initialise l'adresse destination avant le début de la zone mémoire 
  Compteur_de_tous_les_mots = 0 ; On initialise un compteur de tous les mots 
  Compteur_de_mots_uniques = 0 ; On initialise un compteur des mots sans doublons 
  If ReadFile(0, Bible_fichier_source) ; On ouvre le fichier source 
      Longueur_fichier = Lof() ; On regarde sa longueur 
      *Zone_memoire = AllocateMemory(Longueur_fichier) ; On réserve une zone mémoire 
      ReadData(*Zone_memoire, Longueur_fichier) ; On charge la totalité du fichier en mémoire 
      CloseFile(0) ; On ferme le fichier 
      Longueur_fichier - 1 ; La longueur du fichier est bien Longueur_fichier, mais on décrémente de 1 car la première position en zone mémoire est 0 
      For Adresse1 = 0 To Longueur_fichier 
        Code.b = PeekB(*Zone_memoire + Adresse1) ; On prend le code du caractère courant 
        lCode.l = Code.b ; On transforme en octet standard (attention le mode .b Purebasic ne donne pas une valeur 0 - 255 mais -127 - 128 
        If lCode < 0 
            lCode + 256 
        EndIf 
        Code = Table_ASCII_translatee(lCode) ; On change le code par la valeur translatée pour ne retenir que les caractères significatifs 
        If Code <> ' ' Or Code_precedent <> ' ' ; Si le caractère courant ou le caractère précédent est significatif 
            Adresse2 + 1 ; On déplace le pointeur d'adresse de destination 
        EndIf 
        PokeB(*Zone_memoire + Adresse2, Code) ; On copie le caractère depuis l'adresse source vers l'adresse destination 
        Code_precedent = Code ; On retient le code du caractère qui vient d'être traité 
      Next ; Et on boucle jusqu'à la fin de la zone mémoire 
  EndIf 
; 
; La zone mémoire est maintenant totalement analysée et l'on a copié uniquement la partie significative du texte pour l'analyse de mots. 
; 
; On place deux variables pour pointer la zone mémoire résultant de la première partie : 
; Adresse est un pointeur variable qui va scruter la totalité utile de la zone mémoire 
; Adresse2 est l'adresse du dernier caractère utile de la zone 
; 
  Adresse = *Zone_memoire 
  Adresse2 + *Zone_memoire 
  Caracteres_mot = 0 ; compteur de caractères est initialisé pour compter les caractères de chaque mot 
  While Adresse < Adresse2 ; Tant que l'adresse courante n'est pas la fin de la zone 
    Code.b = PeekB(Adresse) ; On prend le code de l'octet pointé 
    If Code = ' ' ; Si ce code est un blanc 
        Compteur_de_tous_les_mots + 1 ; On vient de lire un mot 
        AddElement(Tous_les_mots()) ; On ajoute ce mot à la liste de tous les mots 
        Tous_les_mots() = Trim(Mot) 
        ResetList(Mots_uniques()) ; On initialise la liste des mots sans doublon 
        Deja_trouve = #FALSE ; On va scruter cette liste pour savoir si le mot que l'on vient de trouver est déjà dans la liste sans doublon 
        While NextElement(Mots_uniques()) 
          If Mots_uniques() = Tous_les_mots() 
              Deja_trouve = #TRUE 
              Break 
          EndIf 
        Wend 
        If Deja_trouve = #FALSE ; Si le mot n'a pas été trouvé dans la liste sans doublon on l'ajoute 
            AddElement(Mots_uniques()) 
            Mots_uniques() = Trim(Mot) 
            Compteur_de_mots_uniques + 1 ; On incrémente le compteur de mots uniques 
        EndIf 
        Mot = Space(#Longueur_chaine_mot) ; On ré-initialise la chaine de stockage d'un mot 
        Caracteres_mot = 0 ; On ré-initialise le compteur de caractères d'un mot 
      Else ; Si on arrive ici c'est que le caractère courant est un caractère du mot courant 
        ; 
        ; Attention les yeux 
        ; La ligne ci-dessous est équivalente à PokeB(*Mot + Caracteres_mot, Code) 
        ; Mais c'est beaucoup plus rapide. C'est rendu possible par *Mot.Octets_de_chaine = @Mot placé au début du source 
        ; 
        ; Ici on ne fait que copier un octet d'une adresse mémoire vers une autre, alosr qu'avec un PokeB on appelle une commande Purebasic qui va charger la pile 
        ; du processeur, placer les variables, décharger la pile et revenir vers le programme principal. 
        ; 
        ; La différence de performances est très significative. On exécute ici trois instructions machine, contre au moins trente en faisant un PokeB 
        ; 
        *Mot\Octets[Caracteres_mot] = Code 
        Caracteres_mot + 1 ; Et comme on est en train de lire un mot, on incrémente le compteur de caractères du mot. 
    EndIf 
    Adresse + 1 ; Et on déplace le pointeur d'adresse d'une case 
  Wend 

  If CreateFile(0, Bible_fichier_tous_les_mots) ; On sauvegarde les données dans un fichier pour tous les mots et un second fichier pour la liste sans doublon 
      ResetList(Tous_les_mots()) 
      While NextElement(Tous_les_mots()) 
        WriteStringN(Tous_les_mots()) 
      Wend 
      CloseFile(0) 
  EndIf 

  If CreateFile(0, Bible_fichier_mots_uniques) 
      ResetList(Mots_uniques()) 
      While NextElement(Mots_uniques()) 
        WriteStringN(Mots_uniques()) 
      Wend 
      CloseFile(0) 
  EndIf 
  
End 

; 
; Et voici la section data qui contient les 256 positions de la table ASCII avec l'octet équivalent à chaque code. 
; En fait il s'agit de traduire les caractères du fichier source en caractères significatifs. Donc on remplacera chaque caractère non significatif par 
; un blanc et tout caractère significatif par lui-même ou un caractère choisi. Cela permet de traduire les e accentués en e tout court par exemple. 
; 
; A noter que la table translatée présentée ici est un choix de programmeur et peut être adaptée différement selon les besoins et la langue analysée. 
; 
; J'ai par exemple laissé les minuscules, par choix, mais on pourrait décider de traduire les minuscules en majuscules pour n'avoir que l'écriture la plus 
; simple des mots. 
; 
DataSection 
  Data.b ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' 
  Data.b ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' 
  Data.b ' ', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', ' ', ' ', ' ', ' ' 
  Data.b ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ' ', ' ', ' ', ' ', ' ' 
  Data.b ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'S', ' ', 'E', ' ', 'Z', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 's', ' ', 'e', ' ', 'z', 'Y' 
  Data.b ' ', 'i', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'a', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' 
  Data.b 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I', ' ', 'N', 'O', 'O', 'O', 'O', 'O', ' ', 'O', 'U', 'U', 'U', 'U', 'Y', ' ', ' ' 
  Data.b 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i', ' ', 'n', 'o', 'o', 'o', 'o', 'o', ' ', 'o', 'u', 'u', 'u', 'u', 'y', ' ', 'y' 
EndDataSection 
fweil
Messages : 505
Inscription : dim. 16/mai/2004 17:50
Localisation : Bayonne (64)
Contact :

Message par fweil »

Arf,

Ayé, j'ai eu la validation de mon compte, donc voili voila, les prochains codes que je commenterai en français seront postés + vite

:lol:
Répondre