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 :

Comment faire un APPEND d'un enregistrement dans un fichier binaire ?


Sujet :

Langage Delphi

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 368
    Billets dans le blog
    1
    Par défaut Comment faire un APPEND d'un enregistrement dans un fichier binaire ?
    Je suis sous Windows 10 avec Delphi 6 PE.

    Je suis en train de travailler avec des fichiers binaires organisés en enregistrements de longueur fixe.
    Pour cela, j'utilise AssignFile pour associer un fichier à une variable de type FILE,
    puis ReWrite pour la création ou Reset pour la lecture/mise à jour, en spécifiant chaque fois la longueur d'enregistrement.
    Puis, je me positionne dans le fichier par SEEK en spécifiant le numéro d'enregistrement.*

    Cela marche très bien en mode lecture/mise à jour utilisant Reset.

    Mais je n'arrive pas à ajouter un nouvel enregistrement en fin de fichier, ni après un Reset, ni après un ReWrite.
    Chaque fois, il me jette avec une erreur IO...

    J'ai essayé la commande Append, mais elle ne s'applique que sur les fichiers texte (TextFile).

    Je voudrais faire quelque chise comme ceci:
    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
    unit Unit1;
     
    interface
     
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;
     
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        { Déclarations privées }
      public
        { Déclarations publiques }
      end;
     
    var
      Form1: TForm1;
      bf: File;
      buffer: array of byte;
     
    implementation
     
    {$R *.dfm}
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
      s: string;
      w: integer;
    begin
      AssignFile(bf,'testBF.bin');
      Rewrite(bf,24);
      SetLength(buffer,24);
      s := '12345678901234567890ABCD';
      Move(s,buffer,24);
      BlockWrite(bf,buffer,w);          // <========== IO errir ici !
      showmessage(inttostr(w));
      CloseFile(bf);
    end;
     
    end.
    A l'évidence, je n'ai pas compris un truc élémenaire. Pourriez-vous l'aider svp .
    Merci !

  2. #2
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 086
    Par défaut
    Rewrite ?
    Cela vide le fichier !



    Je trouve qu'il te manque pas mal de [0] et [1] sur les Move et BlockWrite
    En D6, tu n'as pas besoin de Buffer pour écrire la string, c'est Ansi et donc simple à écrire dans un fichier ansi


    Franchement le RecSize, tu devrais le mettre à 1, cela évite la confusion !
    Car me bloc entier transféré occupe au plus Count*RecSize donc attention
    Tu as mis que 3 paramètre à BlockWrite et il devrait être à 1 car 1 x 24, et le 4ème paramètre sera ton w (lui contiendra 24)

    Je n'ai pas Delphi sous la main mais je pense que c'est déjà une bonne piste de correction
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    procedure TForm1.Button1Click(Sender: TObject);
    var
      s: string;
      w: integer;
    begin
      AssignFile(bf, 'testBF.bin');
      Rewrite(bf, 24);
      s := '12345678901234567890ABCD';
      BlockWrite(bf, s[1], 1, w);        // écrit 1 x RecSize depuis s[1]
      showmessage(inttostr(w));
      CloseFile(bf);
    end;
    Le code ci-dessous est plus récent et unicode mais note bien les [1]
    Même si c'est du texte, cette fonction utilise un mode binaire pour écrire c'est rarissime que cela soit en file, j'ai tout migré en TFileStream depuis 15 ans.


    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
    class function TSLTSimpleLogger.WriteLog(const LogName: TFileName; const SectionName, BufferLog: string; AddDate: Boolean = True): TFileName;
    var
      LogFile: file;
      NameFile: TFileName;
      TimeLog, Buf: string;
      BOMUnicodeLittleEndian: Char;
    const
      DATE_TIME_FMT = 'yyyy-mm-dd' + Tabulator + 'hh:nn:ss:zzz'; // Ne pas confondre avec l'ISO8601 'yyyy-mm-ddThh:nn:ss' même si c'est proche
    begin
      try
        if AddDate then
          TimeLog := FormatDateTime(DATE_TIME_FMT, Now());
     
        if not SimpleDirectoryExists(FLogDirectory) then
          SimpleForceDirectories(FLogDirectory);
     
        NameFile := FLogPath + FLogPrefix + LogName + FLogExtension;
        try
          AssignFile(LogFile, NameFile);
          try
            try
              if FileExists(NameFile) then
              begin
                Reset(LogFile, 1);
                Seek(LogFile, FileSize(LogFile));
              end
              else
              begin
                ReWrite(LogFile, 1);
                if SizeOf(Char) <> SizeOf(AnsiChar) then
                begin
                  BOMUnicodeLittleEndian := BOM_LSB_FIRST;
                  BlockWrite(LogFile, BOMUnicodeLittleEndian, SizeOf(BOMUnicodeLittleEndian));
                end;
              end;
            except
              on E: Exception do
              begin
                {$IFDEF DEBUG} {$WARN SYMBOL_PLATFORM OFF}
                if DebugHook <> 0 then
                  TSLTDebugLogger.OutputDebugString('[!!!]WriteLog/Reset-ReWrite : ', E.Message);
                {$WARN SYMBOL_PLATFORM ON} {$ENDIF DEBUG}
     
                Exit;
              end;
            end;
     
            try
              if SectionName <> '' then
                Buf := SectionName + Tabulator + BufferLog + sLineBreak
              else
                Buf := BufferLog + sLineBreak;
     
              if AddDate then
                Buf := TimeLog + Tabulator + Buf;
     
              // Buf[1] car BlockWrite écrit n octets à partir de la position Buf[1] !
              BlockWrite(LogFile, Buf[1], Length(Buf) * SizeOf(Char));
     
              {$IFDEF DEBUG} {$WARN SYMBOL_PLATFORM OFF}
              if DebugHook <> 0 then
                TSLTDebugLogger.OutputDebugString(SectionName, BufferLog, AddDate);
              {$WARN SYMBOL_PLATFORM ON} {$ENDIF DEBUG}
            except
              on E: Exception do
              begin
                {$IFDEF DEBUG} {$WARN SYMBOL_PLATFORM OFF}
                if DebugHook <> 0 then
                  TSLTDebugLogger.OutputDebugString('[!!!]WriteLog/BlockWrite : ', E.Message);
                {$WARN SYMBOL_PLATFORM ON} {$ENDIF DEBUG}
     
                Exit;
              end;
            end;
          finally
            CloseFile(LogFile);
          end;
        except
          on E: Exception do
          begin
            {$IFDEF DEBUG} {$WARN SYMBOL_PLATFORM OFF}
            if DebugHook <> 0 then
              TSLTDebugLogger.OutputDebugString('[!!!]WriteLog/AssignFile-CloseFile : ', E.Message);
            {$WARN SYMBOL_PLATFORM ON} {$ENDIF DEBUG}
     
            Exit;
          end;
        end;
      except
        on E: Exception do
        begin
          {$IFDEF DEBUG} {$WARN SYMBOL_PLATFORM OFF}
          if DebugHook <> 0 then
            TSLTDebugLogger.OutputDebugString('[!!!]WriteLog : ', E.Message);
          {$WARN SYMBOL_PLATFORM ON} {$ENDIF DEBUG}
     
          Exit;
        end;
      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

  3. #3
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 368
    Billets dans le blog
    1
    Par défaut
    Merci pour ton commentaire, ShaiLeTroll ! Ceci m'a permis de faire fonctionner mon code, de la manière suivante:
    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
    unit Unit1;
     
    interface
     
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;
     
    type
      TForm1 = class(TForm)
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        { Déclarations privées }
      public
        { Déclarations publiques }
      end;
     
    var
      Form1: TForm1;
      bf: File;
      buffer: array of byte;
     
    implementation
     
    {$R *.dfm}
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
      s: string;
      w: integer;
    begin
      AssignFile(bf,'testBF.bin');
    //  Rewrite(bf,24);      // juste pour la première fois, en création de fichier
      Reset(bf,24);          // en ajout à un fichier existant
      seek(bf,FileSize(bf));
      if length(buffer)=0 then SetLength(buffer,24);
      // les deux lignes suivantes sont huste une simulation d'une constitution olus complexe
      // des données, à partir de champs de natures différentes défiinis ailleurs
        s := '12345678901234567890ABCD';
        Move(s[1],buffer,24);
      // ceci explique donc l'utilisation d'un buffer global en array of byte.
      // En réalité, de buffeur est déclarée comme propriété d'un composant que j'ai créé.
      BlockWrite(bf,buffer,1,w);
      showmessage(inttostr(w)+'   '+inttostr(FileSize(bf)));
      CloseFile(bf);
    end;
     
    end.
    Comme je l'ai dit dans les commentaires, je développe un système avec un paramétrage de champs de différentes natures.
    Pour chaque ficier, je crée une définition de l'enregistrement, et ces définitions sont ensuite mémorisés dans une classe propre à chaque application.
    Ainsi, pour chaque application, je peux avoir une bibliothèque des descriptions des enregistrements des fichiers binaires;
    Bien sûr, je peux sauvegarder ces classes dans un fichier texte et les recharger, ce qui permet de les distribuer aisément.
    Donc, j'ai mon buffer en array of byte dans ma classe associée à ma définition d'enregistrement.
    Les données sont chargées dans ce buffeur ou extraites de ce buffeur par des méthodes spécifiques, travaillant globalement sur tous les champs en les prenant dans une TSTringList ou en les restituant dans cette dernière. Puis, avec une collection de méthodes de positionnement, lecture et éciriture, je gère mes entrées/sorties physiques. Il y a également des méthodes et propriétés permettant de connaître toutes sortes d'informations sur les fichiers concernés (longueyr et nombre des enregistrements, numéro de l'enregistrement actuel, etc. Tout cela en gérant le mode d'accès (lecture uniquement, écriture uniquement, ou lecture et écriture).

    Voilà un bref aperçu de mon projet. Maintenant, il est pratiquement complet et opérationnel.

    Juste un petit problème que mon code ci-dessus peut parfaitement mettre en évidence:
    - je lance le programme une première fois, en activant la ligne REWRITE. Tout se passe bien, le fichier est créé avec une longueur de 1 enregistrement.
    - j'arrête le programme, je désactive REWRITE et active RESET.
    - je relance le programme, et tout se passe bien - la longueur du fichier passe à 2.
    - Si, après chaque ajout d'enregistrement, j'arrête le programme et je le relance, j'obtiens chaque fois un ajout dans le fichier.

    MAIS: si je ne sors pas du programme et je clique à nouveau sur mon bouton, il y a une violation de mémoire ! Pourquoi ?
    Et ceci même si je fais ceci:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if length(buffer)=0 then SetLength(buffer,24);

  4. #4
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 086
    Par défaut
    Pourtant j'avais dit qu'il manquait des [0]

    Le coupable est là !

    C'est l'emplacement mémoire de la variable buffer qui est modifié mais pas le tableau
    Du coup le second if Length plante puisque le pointeur n'est plus un pointeur sur le tableau mais le début de '12345678 ...'

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Move(s[1],buffer[0],24);
    BlockWrite(bf,buffer[0],1,w);
    C'est le défaut d'utiliser des variables globales
    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

  5. #5
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 368
    Billets dans le blog
    1
    Par défaut
    Alors là, tu m'as largué en rase campagne...

    Je ne cmprends absolument pas comment la ligne
    peut modifier l'emplacement de la variable buffer ! C'était précisément le but de l'emploi de la commande Move pour remplacer le contenu de buffer par d'autres données, SANS intervenir sur son allocation !

    L'idée était d'avvoir un tampon de lecture/écriture fixe, qui ne change pas, dont on remplace juste les données. C'est pourquoi je n'utilise pas les strings.

    En réalité, buffer est déclaré comme ceci:
    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
    type
      TBinaryFileHost = class
        private
          fBuffer: array of byte;
    ...
        published
         constructor Create;
         procedure SetBufferLength(aLength: integer);
    ...
       end;
    ...
    implementation
     
    constructor TBinaryFileHost.Create:
      inherited;
      SetLength(Buffer,0);
      ..
    end;
     
    procedure TBinaryFileHost.SetBufferLength(aLength: integer);
    begin
    ... quelques tests de vraissemblance...
      SetLength(Buffer,aLength);
    end;
    J'utilise la méthode SetBufferLength une seule fois, lors du choix de la définition de l'enregistrement et de l'ouverture du fichier, pour dimensionner le buffer à la taille de l'enregistrement. Et j'imaginais (bêtement, peut-être...) que ceci cinstituait un espace figé.

    Donc, pour moi, dans la séquence
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    Move(s[1],buffer[0],24);
    BlockWrite(bf,buffer[0],1,w);
    la commande MOVE remplace les données du buffer SANS changer l'allocation de cet espace. Et la commande BLOCKWRITE fait de toutes façons référence à buffer tel qu'il est à cet insant, avec les nouvelles données...

    Décidément, il y a uelque chose qui m'échappe.

    En admettant que la logique n'est pas bonne, alors comment réaliser cela ?
    En fait, cela revient à faire successivement plusieurs paires de chargement/écriture de ce buffer:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Move(s[1],buffer[0],24);
    BlockWrite(bf,buffer[0],1,w);
    ...
    Move(s[1],buffer[0],24);
    BlockWrite(bf,buffer[0],1,w);
    ...
    Move(s[1],buffer[0],24);
    BlockWrite(bf,buffer[0],1,w);
    ...

  6. #6
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 086
    Par défaut
    Tu confonds l'emplacement du pointer soit buffer l'emplacement mémoire pointé par le pointeur soit buffer[0].

    C'est différent parce que c'est un tableau dynamique ... cela aurait été un tableau statique genre array[0..10] dans ce cas buffer et buffer[0] désigne le même emplacement, ce n'est pas un pointeur
    Là comme tu utilises un tableau dynamique, tu aurais fait un PChar et un GetMem cela aurait la même chose que ton array of Char
    Et je n'ai pigé la fin de ton message

    Montre nous un code concret, tu te galères pour rien
    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

  7. #7
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 368
    Billets dans le blog
    1
    Par défaut
    Et je n'ai pigé la fin de ton message
    Oh, c'était juste pour symbolier une succesion de remplissages et d'écritures de buffer, avant de refermer le fichier et éventuellement le réouvrir.
    Juste l'utilisation intuitive (ouverture->x fois remplissage/écriture->fermeture.

    Quant au source réel, ce serait compliqué. Tour est implémenté dans une unité faisant environ 2200 lignes de code Delphi, avec sûrement mes maladresses de codage.
    Ce module contient essentiellement deux groupes de composants:
    - la définition d'eenregistrements de longueur fixe, composés de champs de nature variée et de longueur fixe
    - les outils de créer de telles structures
    - la définition d'un objet encapsulant un fichier binaire formaté selon de tels enregistrements
    - l'accès en lecture et écriture de ces enregistrements

    Tout fonctionne, sauf, à l'évidence, mon incompréhension de la nature de l'allocation d'un array of byte dynamiaue.
    En fait, la question importante est:
    comment trouver l'adresse du premier élément d'un array dynamique ?
    Je pensais que
    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
     
    var
      buffer: array of byte
    ...
    // réservation de l'espace
    SetLength(buffer,24)
    ...
    // détermination de l'adresse de base bu buffer (adresse de la donnée du premier élément
    var
      pb: pByte;
    ...
      pb := pByte(@buffer[1]);
    // détermination de l'adresse de début des données d'une variable de l'enregistrement:
    var
      pelem: pbyte;
    ...
    pelem := pByte(integer(pb)+StartOfElement);
    // écriture dans ce buffer d'un élément string
      Move(s[1],pelem^,LengthOfElement);
    En fait, lorsque j'inspecte la mémoire pointée par pb (adresse de base (dans mon idée !) du buffer), les données sont bien rangées et dans le bon format, à la bonne place.

    Mais lorsque j'écris de buffer par BlockWrite, il écrit n'importe quoi, et donc il prend les données ailleurs. Mon pointage n'est pas bon.
    J'écris de la manière suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    BlockWrite(BinaryFile,Buffer,1);
    J'ai modifié comme suit;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    var
      p1: pByte;
    ...
    p1 := pByte(integer(@Buffer[0]));
    BlockWrite(BinaryFile,p1,1);   // 1 seul enregistrement de la longueur fixe (24 dans cet exemple)
    Résultat: mes données sont bien dans le fichier, dans le bon ordre. Tout semble normal, maintenant.

    Question: avaisèje mal compris le passage de paramètres à BlockWrite ?
    Dans la doc, il y avait un exemple avec un array of byte dynamique et qui était passé simplement par le nom du array. J'avais suivi bêtement cet exemple.
    Est-ce que la méthode pb^ est la bonne, ou est-ce ue j'écris et lis n'importe où ?

  8. #8
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 086
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     pb := pByte(@buffer[1]);
    Cela devrait [0] pour avoir le premier
    donc
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    var
      buffer: array of byte
    ...
    // réservation de l'espace
    SetLength(buffer,24)
    ...
    // détermination de l'adresse de base bu buffer (adresse de la donnée du premier élément
    var
      pb: pByte;
    ...
      pb := pByte(@buffer[0]);

    Par contre, je ne comprends pas le code après
    Il faut impérativement que StartOfElement + LengthOfElement <= 24

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    // détermination de l'adresse de début des données d'une variable de l'enregistrement:
    var
      pelem: pbyte;
    ...
    pelem := pByte(integer(pb)+StartOfElement);
    // écriture dans ce buffer d'un élément string
      Move(s[1],pelem^,LengthOfElement);

    mais encore le code ci-dessus est potentiellement correct


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    BlockWrite(BinaryFile,Buffer,1);
    Clairement cela est faux à corriger en

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    BlockWrite(BinaryFile,Buffer[0],1);
    BlockWrite et Move utilise tous les deux un Buffer non typé, donc c'est pareil pour les deux [0] pour les array dynamic et [1] pour les string
    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

  9. #9
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 086
    Par défaut
    Citation Envoyé par KlausGunther Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    var
      p1: pByte;
    ...
    p1 := pByte(integer(@Buffer[0]));
    BlockWrite(BinaryFile,p1,1);
    Et pour ce code, ce n'est pas possible que cela fonctionne !

    c'est p1^ ... et si ça fonctionne c'est que c'est que tu nous caches un Move sur p1 qui a écrit n'importe où en mémoire



    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
    var
      bf: File;
      buffer: array of byte;
      s: AnsiString;
      w: integer;
      p1, p2: pByte;
     
      s1, s2, s3: AnsiString;
      K: Integer;
    begin
      AssignFile(bf, 'testBF.bin');
      Rewrite(bf, 24);
      s := '12345678901234567890ABCD';
      SetLength(buffer, Length(s));
      p1 := pByte(integer(@Buffer[0])); // synonyme de // p1 := @buffer[0];
      Move(s[1], p1^, Length(buffer));       // p1^ c'est le buffer non typé attendu par Move
      BlockWrite(bf, p1^, 1, w);       // p1^ c'est le buffer non typé attendu par BlockWrite
      showmessage(inttostr(w));
      CloseFile(bf);
     
      //--
      AssignFile(bf, 'testBF.bin');
      reset(bf, 24);
      Seek(bf, 1);
      s1 := #13#10;
      s2 := 'KlausGunther';
      s3 := '0123456789';
     
      SetLength(buffer, Length(s));
      p1 := pByte(integer(@Buffer[0]));
      p2 := p1;
     
      Move(s1[1], p2^, Length(s1));
      p2 := pByte(integer(p2) + Length(s1));
     
      Move(s2[1], p2^, Length(s2));
      p2 := pByte(integer(p2) + Length(s2));
     
      Move(s3[1], p2^, Length(s3));
      // Pas besoin de déplacer p2
     
      BlockWrite(bf, p1^, 1, w);
    //  showmessage(inttostr(w));
      CloseFile(bf);
     
      for K := 3 to 10 do
      begin
     
        //--
        AssignFile(bf, 'testBF.bin');
        reset(bf, 24);
        Seek(bf, K - 1);
        s1 := #13#10;
     
        s2 := Format('Ligne N°%d ..', [K]);
        s3 := Format('Fin de N°%d', [K]);
     
        SetLength(buffer, Length(s));
        p1 := pByte(integer(@Buffer[0]));
        p2 := p1;
     
        Move(s1[1], p2^, Length(s1));
        p2 := pByte(integer(p2) + Length(s1));
     
        Move(s2[1], p2^, Length(s2));
        p2 := pByte(integer(p2) + Length(s2));
     
        Move(s3[1], p2^, Length(s3));
        // Pas besoin de déplacer p2
     
        BlockWrite(bf, p1^, 1, w);
        CloseFile(bf);
      end;
    end;
    on obtient ceci

    Code bin : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    12345678901234567890ABCD
    KlausGunther0123456789
    Ligne N°3 ..Fin de N°3
    Ligne N°4 ..Fin de N°4
    Ligne N°5 ..Fin de N°5
    Ligne N°6 ..Fin de N°6
    Ligne N°7 ..Fin de N°7
    Ligne N°8 ..Fin de N°8
    Ligne N°9 ..Fin de N°9
    Ligne N°10 ..Fin de N°

    le Dernier est tronqué pour rester à 24
    Dans l'exemple
    C'est 24 chars pour la première ligne
    Puis CR LF + 22 chars pour les suivantes
    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

  10. #10
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 086
    Par défaut
    Mais je confirme que tout cela c'est TROP compliqué pour Rien !

    Tu veux manipuler des propriétés, prenons Name et ClassName

    J'ai une fiche avec

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
        Button1: TButton;
        Memo1: TMemo;
        Edit1: TEdit;
        ProgressBar1: TProgressBar;
        Button2: TButton;
        Button3: TButton;
        Button4: TButton;

    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
     
    var
      bf: File;
      buffer: array of byte;
      w: integer;
      p1, p2: pByte;
      p3: PAnsiChar;
      s: AnsiString;
     
      s1, sp, s2, s3: AnsiString;
      K: Integer;
    begin
      AssignFile(bf, 'testBF.bin');
      Rewrite(bf, 24);
      CloseFile(bf);
     
      SetLength(buffer, 24);
      p1 := pByte(integer(@Buffer[0]));
     
      for K := 0 to ControlCount - 1 do
      begin
        s1 := Controls[K].Name;
        sp := ': ';
        s2 := Controls[K].ClassName();
        s3 := ';' + StringOfChar(' ', 21 - Length(s1) - Length(sp) - Length(s2)) + #13#10;
     
        AssignFile(bf, 'testBF.bin');
        reset(bf, 24);
        Seek(bf, K);
     
        p2 := p1;
        p3 := PAnsiChar(p1);
     
        Move(s1[1], p2^, Length(s1));
        p2 := pByte(integer(p2) + Length(s1));
     
        Move(sp[1], p2^, Length(sp));
        p2 := pByte(integer(p2) + Length(sp));
     
        Move(s2[1], p2^, Length(s2));
        // p2 := pByte(integer(p2) + Length(s2));
     
        p2 := pByte(integer(p1) + 24 - Length(s3));
        Move(s3[1], p2^, Length(s3));
     
        BlockWrite(bf, p1^, 1, w);
        CloseFile(bf);
     
        SetLength(s, 24);
        Move(p3^, s[1], 24);
        Memo1.Lines.Text := Memo1.Lines.Text + s;
      end;
    end;
    j'obtiens


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    Button1: TButton;     
    Memo1: TMemo;         
    Edit1: TEdit;         
    ProgressBar1: TProgre;
    Button2: TButton;     
    Button3: TButton;     
    Button4: TButton;
    mais je peux le simplifier en

    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
    var
      bf: File;
      buffer: AnsiString;
      w: integer;
     
      s1, sp, s2, s3: AnsiString;
      K: Integer;
    begin
      AssignFile(bf, 'testBF.bin');
      Rewrite(bf, 24);
      CloseFile(bf);
     
      SetLength(buffer, 24);
     
      for K := 0 to ControlCount - 1 do
      begin
        s1 := Controls[K].Name;
        sp := ': ';
        s2 := Controls[K].ClassName();
     
        s3 := ';' + StringOfChar(' ', 21 - Length(s1) - Length(sp) - Length(s2)) + #13#10;
     
        buffer := Copy(s1 + sp + s2, 1, 21) + s3;
     
        AssignFile(bf, 'testBF.bin');
        reset(bf, 24);
        Seek(bf, K);
     
        BlockWrite(bf, buffer[1], 1, w);
        CloseFile(bf);
     
        Memo1.Lines.Text := Memo1.Lines.Text + buffer;
      end;
    end;


    Et je peux te faire ma version préférée qui donne un résultat différent mais tu devines la puissance du code :

    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
    type
      TMyRecord = packed record
        MyName: array[0..8] of AnsiChar;
        Separator: array[0..1] of AnsiChar;
        MyClassName: array[0..9] of AnsiChar;
        EndOfLine: array[0..2] of AnsiChar;
      end;
    var
      MyRecord: TMyRecord;
      bf: File;
      w: integer;
      K: Integer;
      s: AnsiString;
    begin
      AssignFile(bf, 'testBF.bin');
      Rewrite(bf, SizeOf(MyRecord)); // C'est 24 !
      CloseFile(bf);
     
      for K := 0 to ControlCount - 1 do
      begin
        FillChar(MyRecord, SizeOf(MyRecord), ' ');
     
        s := Controls[K].Name;
        Move(s[1], MyRecord.MyName, Min(Length(MyRecord.MyName), Length(s)));
     
        MyRecord.Separator := ': ';
     
        s := Controls[K].ClassName();
        Move(s[1], MyRecord.MyClassName, Min(Length(MyRecord.MyClassName), Length(s)));
     
        MyRecord.EndOfLine := ';'#13#10;
     
        AssignFile(bf, 'testBF.bin');
        reset(bf, 24);
        Seek(bf, K);
     
        BlockWrite(bf, MyRecord, 1, w);
        CloseFile(bf);
     
        Memo1.Lines.Text := Memo1.Lines.Text + MyRecord.MyName + MyRecord.Separator + MyRecord.MyClassName + MyRecord.EndOfLine;
      end;
    end;
    et la sortie dans le fichier

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    Button1  : TButton   ;
    Memo1    : TMemo     ;
    Edit1    : TEdit     ;
    ProgressB: TProgressB;
    Button2  : TButton   ;
    Button3  : TButton   ;
    Button4  : TButton   ;
    Button5  : TButton   ;
    Je dirais que la dernière solution correspond encore plus à ton StartOfElement et LengthOfElement pour sortir un fichier à position fixe
    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

  11. #11
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 368
    Billets dans le blog
    1
    Par défaut
    Voici ce que ça donne chez moi:

    La définition de l'enregistrement (juste pour la démo):
    StructuredRecordDefinitions
    Structured record definitions: 1
    Definition 1: aaa
    Record length: 24
    Field definitions: 2
    Field 1
    Name: bbbbbb
    Type: 0 (String)
    Start: 0
    Length: 20
    Field 2
    Name: ddddddddddddddd
    Type: 1 (Integer)
    Start: 20
    Length: 4
    Les données du premier enregistrement écrit:
    bbbbbb=Martin
    ddddddddddddddd=255
    Voici le dump du fichier après le premier ajout d'enregistrement;
    Nom : aa1.png
Affichages : 104
Taille : 13,0 Ko

    Les données du deuxième enregistrement écrit:
    bbbbbb=MArie-Claude
    ddddddddddddddd=1023
    Voici le dump du fichier après le deuxième ajout d'enregistrement:
    Nom : aa2.PNG
Affichages : 104
Taille : 15,7 Ko

    etc.

    Pour moi, le résultat est bon.


    J'ai inspécté des données écrites dans buffer, en utilisant l'accès par Buffer[0], Buffer[1], Buffer[2].
    Voici les données chargées:
    bbbbbb=$x?
    ddddddddddddddd=15
    Voici le code d'inspection:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    showmessage('Buffer[0]...='+char(Buffer[0])+char(Buffer[1])+char(Buffer[2]));
    Et voici l'affichage:
    Nom : aa3.png
Affichages : 106
Taille : 1,9 Ko

    A la vue de ces résultats, j'ai l'impression que les données sont bien écrites en mémoire là où je voulais les placer (dans le array of byte nommé Buffer).
    Et le dump montre que BlockWrite prend les données bien là où elles se trouvent.

    Pour moi, la conclusion est que BlockWrite ne marche pas avec les arrays dynamiques comme indiqué dans la doc, mais uniquement avec des arrays statiques, si on lui donne simplement le nom du array en paramètre.
    Par contre, si on lui donne PByte(@Buffer[0])^, alors ça marche. Dans ce contexte, le déréférencement est capital, sinon il y a un plantage.

  12. #12
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 086
    Par défaut
    Je ne sais pas où tu as lu que BlockWrite fonctionnait avec des tableaux !
    Cela fonctionne avec un buffer non typé, c'est donc au développeur de maitriser ce qu'il fait, un tableau statique et dynamique n'ont pas grand chose en commun.

    A mon avis, utilise un record voire même un file of record


    PByte(@Buffer[0])^ = Buffer[0] et j'insiste depuis le début que tu omets souvent le [0] d'où un tas de problème
    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. #13
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 086
    Par défaut
    CQFD :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    type TStructuredRecord = packed record
      bbbbbb: array[0..19] of AnsiChar;
      ddddddddddddddd: Integer;
    end;
    Version Sans Padding Espace

    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
     
    procedure TEmployeesDummiesMainForm.Button6Click(Sender: TObject);
    const
      EMPLOYEES: array[0..1] of TStructuredRecord =
        (
          (bbbbbb: 'Martin'; ddddddddddddddd: 255;),
          (bbbbbb: 'MArie-Claude'; ddddddddddddddd: 1023;)
        );
     
    var
      bf: File;
      K: Integer;
    begin
      AssignFile(bf, 'testBF.bin');
      Rewrite(bf, SizeOf(TStructuredRecord)); // C'est 24 !
      CloseFile(bf);
     
      for K := Low(EMPLOYEES) to High(EMPLOYEES) do
      begin
        AssignFile(bf, 'testBF.bin');
        reset(bf, SizeOf(TStructuredRecord));
        Seek(bf, K);
     
        BlockWrite(bf, EMPLOYEES[K], 1);
        CloseFile(bf);
     
        Memo1.Lines.Add(Format('%s: %d', [EMPLOYEES[K].bbbbbb, EMPLOYEES[K].ddddddddddddddd]));
      end;
    end;
    Nom : Sans titre.png
Affichages : 103
Taille : 22,9 Ko


    Version Avec Padding Espace

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    type TBusinessRecord = record
      bbbbbb: AnsiString;
      ddddddddddddddd: Integer;
    end;
    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
    procedure TEmployeesDummiesMainForm.Button7Click(Sender: TObject);
    const
      EMPLOYEES: array[0..1] of TBusinessRecord =
        (
          (bbbbbb: 'Martin'; ddddddddddddddd: 255;),
          (bbbbbb: 'MArie-Claude'; ddddddddddddddd: 1023;)
        );
     
    var
      bf: File;
      K: Integer;
      sr: TStructuredRecord;
    begin
      AssignFile(bf, 'testBF.bin');
      Rewrite(bf, SizeOf(TStructuredRecord)); // C'est 24 !
      CloseFile(bf);
     
      for K := Low(EMPLOYEES) to High(EMPLOYEES) do
      begin
        AssignFile(bf, 'testBF.bin');
        reset(bf, SizeOf(TStructuredRecord));
        Seek(bf, K);
     
        FillChar(sr, SizeOf(sr), ' ');
     
        Move(EMPLOYEES[K].bbbbbb[1], sr.bbbbbb, Length(EMPLOYEES[K].bbbbbb));
        sr.ddddddddddddddd := EMPLOYEES[K].ddddddddddddddd;
     
        BlockWrite(bf, sr, 1);
        CloseFile(bf);
     
        Memo1.Lines.Add(Format('%s: %d', [EMPLOYEES[K].bbbbbb, EMPLOYEES[K].ddddddddddddddd]));
      end;
    end;
    Nom : Sans titre.png
Affichages : 102
Taille : 56,7 Ko



    Evidemment en encapsulant dans une classe TEmployee la conversion de TBusinessRecord <-> TStructuredRecord, on finit par avoir un code ultra élégant

    Et des comme ça, j'en ai des tonnes !



    C'est pour cela que donne l'objectif est très important, tu n'as pas choisi l'approche la plus simple et tu galères alors que Delphi propose pas mal d'astuce surtout pour une mode binaire, on peut faire des choses extrêmement compact

    Un code de 2004 : avec le TStructuredRecord faisant toujours 128 octets mais pouvant être différent sur chaque ligne, les premiers octets indiquant le type de ligne

    un autre sujet Comment écrire dans un fichier qui te montre le "file of"
    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. #14
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 368
    Billets dans le blog
    1
    Par défaut
    Merci pour toutes ces indications, ShaileTroll !

    J'avais vu la technique du "file of" avec un record. Malheureusement, j'ai dû écarter cette technique pourtant simple et élégante, parce que je voulais créer un système dans lequel on pouvait dynamiquement créer et modier la structure d'un enregistrement sans le figer par la compilation.

    J'ai noté les différentes remarques dans ma base dedonnées "conseils" personnelle.

    Merci d'avoir consacré du temps à mon petit problème !

  15. #15
    Expert éminent
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    14 086
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    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 : 14 086
    Par défaut
    Tu peux utiliser un file et un packed record si tu veux plus de généricité avec un fichier avec des record de taille différente, généralement dans ce cas on a un record header qui indique le nombre de chunk

    Si je veux une structure dynamique pure, tu peux utiliser un fichier ini ou un fichier xml pour décrire la structure du fichier,
    Je te conseille de tout faire via des Objets pour encapsuler la sérialisation déserialisation

    Mais je pense que tu devrais utiliser plutôt un GetMem sur un PByte plutôt qu'un array, au moins avec n pointeur, pas de doute
    Et le TFileStream est peut-être moins vicieux que BlockWrite, comme CopyMemory orienté pointeur est moins vicieux que Move orienté var non typé
    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

  16. #16
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 368
    Billets dans le blog
    1
    Par défaut
    Oui, j'ai absolument tout encapsulé dans des composants de mon cru, avec des méthodes SaveToFile et LoadFromFile pour avoir une sauvegarde dans un fichier texte basique.

    Je sais que FileStream offe nettement plus de souplesse (positionnement a l'octet près, read/write de longueyr variable etc. J'utilise cela dans d'autres circonstances ou je préfique quaque "ortion" de données par un type de données sur 1 octet, suivie par une indication de longueur (suivant le cas, en 8, 16 ou 32 bits. Facile à traiter avec FileStream. J'y ai même inclus un type de données indiquant un groupe de données (un record, par exemple) avec sa longueur, de sorte à pouvoir ignorer de vastes partie si je n'ai pas besoin de plonger dans certainz détails. FileStream fait tout cela très facilement.

    Je vais en effet expérimenter GetMem selon ton conseil. Il y a sûrement une expérience positive à en tirer - je ne l'ai jamais fait.

    Juste pour le contexte global de mon post:

    Je me place dans le contexte d'une application de gestion intégrée avec plusieurs parties globalement indépendantes mais avec quelques ponts.
    Chaque partie a ses fichiers, mais toutes les parties peuvent avoir accès à quelques fichiers partagés.

    J'ai créé dont un objet TFieldDefinition décrivant un esul champ, avec pour le moment des champs de type String tlongueur fixe bien sur, space padded), integer, float, DateTime et Option (32 bits de flags binaires).

    J'ai créé un autre composant TStructuredBinaryFileDefinition (je sais, seul différence avec le précédent est l'absence du "s" à la fin...) qui représente le desssin d'enregistrement d'un seul fichier binaire. Une TStringList contient les objets TFieldDefinition avec leur nom.

    J'ai donc créé un composant TStructuredBinaryFileDefinitions qui représente une collection de définitions d'enregistrements de fichiers binaires, propres à une partie de l'application. Une TStringList contient les objets TStructuredBinaryFileDefinition avec leur nom.

    Ainsi, chaque objet TStructuredBinaryFileDefinitions regroupe toutes les définitions des fichiers binaires d'une sous-partie cohérente d'une applicaton iintégrée. Il suffit de faire un LoadFromFile pour avoir accès à l'ensemble des définitions quib sont ensuite gérées par des fonctions de la DLL.

    Une des utilités d'un tel processus est de pouvoir distribuer facikement une nouvelle version, même avec des fichiers modifies. Dans ce cas, je peux facilement include deux définitions d'enregistrements d'un même fichier, l'une pour l'ancienne version et l'autre pour la nouvelle, puis exécuter un programme de converion recopiant l'ancienne version vers la nouvelle en reprenant les données des champs, tout un reformattant ou insérant de nouvelles données. Je sais qu'on peut faire autrement, mais ça m'a amusé de procéder de cette manière. L'avantage d'être à la retraite et de ne pas voir de contraintes de coût ou de rentabilité...

    En tout cas, tout est entièrement basé sur des composants gérées par des méthodes et ça marche très bien.

  17. #17
    Expert confirmé
    Avatar de anapurna
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Mai 2002
    Messages
    3 491
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Mai 2002
    Messages : 3 491
    Par défaut
    Salut

    il existe une multitude de solution
    si tu veux des enregistrements variable
    tu as deux solutions
    soit tu défini un entête qui décrit tes champs et la taille associer
    ce qui fournis donc des enregistrement fixe par ligne
    sinon pour chaque enregistrement tu peut ajouter un indicateur de taille
    avant chaque champs

  18. #18
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    368
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 368
    Billets dans le blog
    1
    Par défaut
    Merci, Anapurna !

    Oui, je sais cela. C'est ce que j'ai dit dans le deuxième paragraphe de mon dernier post:
    Je sais que FileStream offe nettement plus de souplesse (positionnement a l'octet près, read/write de longueyr variable etc. J'utilise cela dans d'autres circonstances ou je préfique quaque "ortion" de données par un type de données sur 1 octet, suivie par une indication de longueur (suivant le cas, en 8, 16 ou 32 bits. Facile à traiter avec FileStream. J'y ai même inclus un type de données indiquant un groupe de données (un record, par exemple) avec sa longueur, de sorte à pouvoir ignorer de vastes partie si je n'ai pas besoin de plonger dans certainz détails. FileStream fait tout cela très facilement.
    Sauf que dans mon projet actuel, le context est un peu différent: pouvoir accéder EN ACCES DIRECT , à n'importe quel enregistrement dans un fichier composé d'enregistrements fixes à travers tout le fichier ce qui exclut la présence d'une entête un d'un préfixe quelconque devant un enregistrement ou devant un champ. Pas question de parcourir séquentiellement pour s'orienter. Bien entendu,la lecture séquentielle reste nécessaire sur le plan des accès logiques, mais également la lecture séquentielle EN ARRIERE ! Donc, forcément, longueurs fixes, uniques à travers tout le fichier. Mais j'utilise l'autre technique dans d'autres circonstances, comme je l'ai indiqué.

    D'ailleurs, cette méthode d'accès (par la définition séparée de la nature et longueur des champs) sera le coeur de mon nouveau moteur de gestioin d'accès à ma base de données relationnelle personnelle (entièrement implémentée par moi, sans code de partie tierce). Là, ça concerne uniquement la partie assemblage/désassemblage des zones de données composites, le mécanisme de lecture/écriture se fera par pages entières de 16 ko par défaut, mais de taille paramétrable. C'est en cours d'écriture et déjà bien avancé...

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Comment faire pour supprimer définitivement un enregistrement dans un fichier
    Par gryffondor8392 dans le forum VB 6 et antérieur
    Réponses: 8
    Dernier message: 03/02/2011, 19h12
  2. [MySQL] Comment faire qu'une BD soit disponible dans un réseau?
    Par dessinateurttuyen dans le forum PHP & Base de données
    Réponses: 5
    Dernier message: 05/07/2006, 14h55
  3. Réponses: 1
    Dernier message: 28/01/2006, 12h36
  4. comment ne pas enregistrer dans le fichier log?
    Par trotters213 dans le forum MS SQL Server
    Réponses: 14
    Dernier message: 21/03/2005, 14h56
  5. Comment faire un retour a la ligne dans un tableaux Word
    Par alexmorel dans le forum API, COM et SDKs
    Réponses: 2
    Dernier message: 17/06/2004, 09h31

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