menu
  Home  ==>  articles  ==>  colibri_helpers  ==>  u_c_string_file   

u_c_string_file - John COLIBRI.


1 - Introduction

1.1 Historique

Il est de nombreuses circonstances où nous devons stocker sur disques des données de taille variable. Le cas le plus courant est celui des chaînes de caractères:
  • des adresses postales
  • des répertoires disque
  • des listes de mots clés
Le stockage de ces données dans des FILE OF pose le même problème de dimensionnement que les ARRAYs:
  • soit la taille est calculée trop juste, et certaines données ne pourront être stockées
  • soit l'allocation est trop généreuse et une gâchis est fatal.
Pour les chaînes, nous pouvons utiliser un fichier ASCII en plaçant chaque donnée sur une ligne. Les fichiers TEXT permettent l'écriture et la lecture séquentielle mais pas l'accès direct. Les tStringList permettent l'accès direct, à condition de charger tout le fichier en mémoire. De plus les tStringList sont mal adaptés pour les données binaires à cause de la présence éventuelle de caractères 0.

Reste alors le stockage sur disque de la partie variable en conservant dans un autre fichiers les coordonnées de chacune de ces parties. Parmi les formats possibles:

  • stocker dans le fichier fixe la position du début de chaque partie variable, et stocker dans chaque enregistrement variable sa taille et les données
  • stocker dans le fichier fixe la position et la taille, et dans le fichier variable les parties variables uniquement.
  • une autre solution serait de ne placer dans le fichier fixe que les positions de départ, la taille étant calculée par différence entre deux enregistrements successifs
Suite à quelques problèmes rencontrés sur des structures trop optimisées, nous souhaitions un format dans lequel:
  • le fichier variable peut continuer à être lu séquentiellement (sans avoir besoin du fichier fixe)
  • les modifications sont possibles (en plaçant une partie variable à un autre endroit que son emplacement séquentiel)

1.2 Le format du fichier variable

Pour la solution retenue, le format du fichier variable est la suivante:
  • pour chaque partie variable:
    • un identificateur Integer permettant de retrouver au besoin le RECORD du fichier fixe auquel appartient cette partie variable
    • la taille de la partie variable, sous forme d'Integer
    • les octets de la partie variable
  • le début du fichier contient 4 octets permettant de gérer une FreeList:
    • si une partie variable est remplacée par une partie de taille plus grande, nous plaçons la nouvelle valeur en fin de fichier. Le trou rendu disponible pourrait éventuellement être récupéré par un nouvel enregistrement. Notez aussi que les 8 octets de prologue permettraient éventuellement le "pinning": si certains fichier ont mémorisé la position de l'enregistrement, nous pouvons remplacer l'enregistrement par la position du nouvel enregistrement et les programmes ayant une référence vers l'ancienne position ne seraient pas affectés.
    • le fait que les 4 premier octets soient utilisés pour la gestion des trous permet aussi d'utiliser la position 0 pour indiquer une partie variable vide, si c'était nécessaire (le NIL de notre fichier)
    Mentionnons que si les 4 octets sont alloués, l'unité actuelle ne gère pas la liste de trous. Nous ajouterons cette partie lorsque cela deviendra utile.
Ce fichier contenant des parties variables est utilisé par d'autres structures:
  • des structures mémoire (ARRAY OF Integer, tList etc) qui contiendraient les positions de chaque partie variable. Le fichier aurait une durée de vie limitée à l'application
  • un fichier fixe (uniquement utilisé pour cela, ou un champ dans un RECORD d'un FILE OF contenant, entre autres, des parties variables) contenant les positions des parties variables
  • un fichier c_file_of qui encapsule les FILE OF

2 - Utilisation

2.1 - Interface

L'interface de u_c_string_file est la suivante:

    type c_string_fileclass(c_basic_file)
                            public
                              m_first_free_positionInteger;
                              m_idInteger;

                              Constructor create_string_file(p_namep_file_nameString); Virtual;

                              function f_force_createBooleanOverride;
(*
                              function f_open: Boolean; Override;
                              procedure close_file; Override;
*)

                              function f_append_string(p_idInteger;
                                  p_stringString): Integer;
                              function f_read_string(p_string_positionInteger): String;

                              Destructor DestroyOverride;
                          end;

La classe hérite de c_basic_file (gestion du nom du fichier, copies, recopies, ouvertures etc.)

Pour les données:

  • m_first_free_position: contient la position du premier trou dans le fichier (inutilisé dans la version actuelle)
  • m_id: champ pour stocker temporairement l'identificateur en mémoire
Et pour les méthodes:
  • le constructeur appelle le constructeur de c_basic_file
  • f_force_create: créé le fichier et écrit les 4 octets du début le la liste des trous
  • f_append_string: écrit l'enregistrement variable (l'identificateur, la taille de la chaîne et les caractères de la chaîne). La fonction retourne la position du PREMIER octet écrit sur disque
  • f_read_string: retourne la chaîne lorsque nous fournissons la position du premier octet de l'enregistrement
Notez que:
  • la gestion de la liste des trous n'est pas mise en oeuvre dans cette version
  • seules les chaînes de caractère sont traitées. D'autres types de données de taille variable sont utilisables mais les primitives ne sont pas écrites. L'utilisation de f_block_read et f_block_write rend cette mise en oeuvre aisée
Pour illustrer l'emploi de cette classe, nous allons présenter trois exemples.

2.2 - Un exemple en mémoire

Voici un premier exemple où les positions des chaînes sont stockées en mémoire dans un tableau:

    procedure TForm1.test_in_memory_Click(SenderTObject);
      var l_positionsarray[0..2] of Integer;
          l_recordInteger;
      begin
        with c_string_file.create_string_file('string_file''memory.str'do
        begin
          f_force_create;
          close_file;

          f_open;
          l_positions[0]:= f_append_string(1, 'pascal');
          l_positions[1]:= f_append_string(2, 'sql');
          l_positions[2]:= f_append_string(2, 'midas');
          close_file;

          f_open;
          for l_record:= 0 to 2 do
            display(f_read_string(l_positions[l_record]));
          close_file;

          Free;
        end// with c_string_file
      end// test_in_memory_Click


2.3 - Exemple avec un FILE OF

Voici un exemple dans lequel nous stockons un identificateur et une chaîne:

    procedure TForm1.file_of_Click(SenderTObject);
      type t_recordrecord
                       m_idInteger;
                       m_postal_address_positionInteger;
                     end;
      var l_file_ofFile Of t_record;
          l_recordt_record;
          l_c_string_filec_string_file;

      procedure write_address(p_idIntegerp_addressString);
        begin
          l_record.m_id:= p_id;
          l_record.m_postal_address_position:= l_c_string_file.f_append_string(p_idp_address);
          write(l_file_ofl_record);
        end// write_address

      begin // file_of_Click
        AssignFile(l_file_of'postal.bin');
        Rewrite(l_file_of);
        CloseFile(l_file_of);

        l_c_string_file:= c_string_file.create_string_file('string_file''postal.str');
        l_c_string_file.f_force_create;
        l_c_string_file.close_file;

        Reset(l_file_of);
        l_c_string_file.f_open;
        write_address(11, 'Dupon|5 rue de la Paix|75005 PARIS');
        write_address(22, 'Smith|2 Hyde Park|3zP9wU2r London');
        write_address(33, 'Malooney|Down Under 33|87z Sydney');
        l_c_string_file.close_file;
        CloseFile(l_file_of);

        Reset(l_file_of);
        l_c_string_file.f_open;
        while not Eof(l_file_ofdo
        begin
          read(l_file_ofl_record);
          with l_record do
            display(IntToStr(m_id)+ ' 'l_c_string_file.f_read_string(m_postal_address_position));
        end// while
        l_c_string_file.close_file;
        CloseFile(l_file_of);
      end// file_of_Click


2.4 - Exemple avec un c_file_of

Si nous souhaitons encapsuler la gestion du fichier précédent dans une classe, nous pouvons utiliser un descendant des c_file_of dont nous créons le RECORD contenant l'entier et la position de la chaîne. Il faut de plus réécrire les procédures d'ouverture, fermeture etc. pour que ces opérations soient réalisées sur les deux fichiers.

Voici la classe encapsulant le FILE OF et le c_string_file:

    type t_address_recordRecord
                             m_integerInteger;
                             m_address_positionInteger;
                           end;

         c_file_of_addressclass(c_file_of)
                                public
                                  m_address_recordt_address_record;
                                  m_c_string_filec_string_file;

                                  Constructor create_file_of_address(p_namep_file_nameString); Virtual;

                                  function f_force_createBooleanOverride;
                                  function f_openBooleanOverride;

                                  procedure write_address(p_integerIntegerp_postal_addressString);
                                  procedure read_address(var pv_integerIntegervar pv_postal_addressString);

                                  function f_display_recordStringOverride;

                                  procedure close_fileOverride;

                                  Destructor DestroyOverride;
                                end;

Notez la présence du champ m_c_string_file qui correspondant au c_string_file

Les procédures de gestion opèrent sur les deux fichiers. A titre d'exemple:

  • voici la procédure d'ouverture:

        function c_file_of_address.f_openBoolean;
          begin
            Result:= InHerited f_open and m_c_string_file.f_open;
          end// f_open

  • et voici la procédure qui écrit les données

        procedure c_file_of_address.write_address(p_integerIntegerp_postal_addressString);
          begin
            with m_address_record do
            begin
              m_integer:= p_integer;
              m_address_position:= m_c_string_file.f_append_string(p_integerp_postal_address);
            end// with m_address_record
            write_record;
          end// write_address

Muni de cette classe, nous pouvons écrire le programme principal qui créé, écrit et relit les adresses:

    procedure TForm1.c_file_of_Click(SenderTObject);
      begin
        with c_file_of_address.create_file_of_address('address''address_2.bin'do
        begin
          f_force_create;
          close_file;

          f_open;
          write_address(11, 'Dupon|5 rue de la Paix|75005 PARIS');
          write_address(22, 'Smith|2 Hyde Park|3zP9wU2r London');
          write_address(33, 'Malooney|Down Under 33|87z Sydney');
          close_file;

          f_open;
          while not f_eof do
          begin
            read_record;
            display(f_display_record);
          end;
          close_file;
        end// with c_file_of_address.
      end// c_file_of_Click


2.5 - Répertoires et Directives de Compilation

L'unité est prévue pour être placée dans:

C:
  programs
    colibri_helpers
      classes

Vous pouvez naturellement changer cette organisation par Projet | Options | Directories

Les directives de compilation sont:

  • R+ (vérification des intervalles)
  • S+ (vérification de la pile)
  • pas d'optimisation

3 - Programmation

Rien de particulier. La classe s'appuie essentiellement sur la classe u_c_basic_file.


4 - Améliorations

Il reste essentiellement à terminer l'implémentation des données autres que les String, et la gestion de la liste des trous.


5 - Télécharger le source

Vous pouvez télécharger: Avec les mentions d'usage:
  • j'apprécie tous les commentaires, remarques ou critiques
  • signalez-moi les bugs que vous trouverez.

L'auteur

John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le conseil (composants, architecture, test) et la formation. Son site contient des articles avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client.
Created: jan-04. Last updated: mar-2020 - 250 articles, 620 .ZIP sources, 3303 figures
Contact : John COLIBRI - Tel: 01.42.83.69.36 / 06.87.88.23.91 - email:jcolibri@jcolibri.com
Copyright © J.Colibri   http://www.jcolibri.com - 2001 - 2020
Retour:  Home  Articles  Formations  Développement Delphi  Livres  Pascalissime  Liens  Download
l'Institut Pascal

John COLIBRI

+ Home
  + articles_avec_sources
    + bases_de_donnees
    + web_internet_sockets
    + services_web_
    + prog_objet_composants
    + office_com_automation
    + colibri_utilities
    + uml_design_patterns
    + graphique
    + delphi
    + outils
    + firemonkey
    + vcl_rtl
    + colibri_helpers
      – u_types_constants
      – u_strings
      – u_loaded
      – u_c_basic_object
      – u_c_display
      – u_dir
      – u_file
      – u_display_hex
      – u_c_file_name
      – u_c_basic_file
      – u_c_log
      – u_c_line
      – handle_files
      – u_c_path_segments
      – u_c_text_file
      – u_c_direct_acccess
      – u_c_string_file
      – u_c_file_of
    + colibri_skelettons
    + admin
  + formations
  + developpement_delphi
  + présentations
  + pascalissime
  + livres
  + entre_nous
  – télécharger

contacts
plan_du_site
– chercher :

RSS feed  
Blog