saving a records containing a member of type string to a file (Delphi, Windows)

后端 未结 5 1710
情书的邮戳
情书的邮戳 2020-12-22 06:13

I have a record that looks similar to:

type
  TNote = record
    Title : string;
    Note  : string;
    Index : integer;
  end;

Simple. Th

相关标签:
5条回答
  • 2020-12-22 06:52
      TNote = record
        Title : string;
        Note  : string;
        Index : integer;
      end;
    

    could be translated as

      TNote = record
        Title : string[255];
        Note  : string[255];
        Index : integer;
      end;
    

    and use Stream.writebuffer(ANodeVariable, sizeof(TNode), but you said that strings get go over 255 chars in this case IF a string goes over 65535 chars then change WORD to INTEGER

    type
    TNodeHeader=Record
      TitleLen,
      NoteLen: Word;
    end;
    
    (* this is for writing a TNode *)
    procedure saveNodetoStream(theNode: TNode; AStream: TStream);
    var
      header: TNodeHeader;
      pStr: PChar;
    begin
      ...
      (* writing to AStream which should be initialized before this *)
      Header.TitleLen := Length(theNode.Title);
      header.NodeLen := Length(theNode.Note);
      AStream.WriteBuffer(Header, sizeof(TNodeHeader);
      (* save strings *)
      PStr := PChar(theNode.Title);
      AStream.writeBuffer(PStr^, Header.TitleLen);
      PStr := PChar(theNode.Note);
      AStream.writebuffer(PStr^, Header.NoteLen);
      (* save index *)
      AStream.writebuffer(theNode.Index, sizeof(Integer));
    end;
    (* this is for reading a TNode *)
    function readNode(AStream: TStream): TNode;
    var
      header: THeader
      PStr: PChar;
    begin
      AStream.ReadBuffer(Header, sizeof(TNodeHeader);
      SetLength(Result.Title, Header.TitleLen);
      PStr := PChar(Result.Title);
      AStream.ReadBuffer(PStr^, Header.TitleLen);
      SetLength(Result.Note, Header.NoteLen);
      PStr := PChar(Result.Note);
      AStream.ReadBuffer(PStr^, Header.NoteLen);
      AStream.ReadBuffer(REsult.Index, sizeof(Integer)(* 4 bytes *);
    end;
    
    0 讨论(0)
  • 2020-12-22 06:59

    You could work with two different files, one that just stores the strings in some convenient way, the other stores the records with a reference to the strings. That way you will still have a file of records for easy access even though you don't know the size of the actual content.

    (Sorry no code.)

    0 讨论(0)
  • 2020-12-22 06:59

    You can use the functions available in this Open Source unit.

    It allows you to serialize any record content into binary, including even dynamic arrays within:

    type
      TNote = record
        Title : string;
        Note  : string;
        Index : integer;
      end;
    
    var
      aSave: TRawByteString;
      aNote, aNew: TNote;
    begin
      // create some content
      aNote.Title := 'Title';
      aNote.Note := 'Note';
      aNote.Index := 10;
      // serialize the content
      aSave := RecordSave(aNote,TypeInfo(TNote));
      // unserialize the content
      RecordLoad(aNew,pointer(aSave),TypeInfo(TNote));
      // check the content
      assert(aNew.Title = 'Title');
      assert(aNew.Note = 'Note');
      assert(aNew.Index = 10);
    end;
    
    0 讨论(0)
  • 2020-12-22 07:04

    You can't do it with a typed file. Try something like this, with a TFileStream:

    type
       TStreamEx = class helper for TStream
       public
          procedure writeString(const data: string);
          function readString: string;
          procedure writeInt(data: integer);
          function readInt: integer;
      end;
    
    function TStreamEx.readString: string;
    var
       len: integer;
       iString: UTF8String;
    begin
       self.readBuffer(len, 4);
       if len > 0 then
       begin
          setLength(iString, len);
          self.ReadBuffer(iString[1], len);
          result := string(iString);
       end;
    end;
    
    procedure TStreamEx.writeString(const data: string);
    var
       len: cardinal;
       oString: UTF8String;
    begin
       oString := UTF8String(data);
       len := length(oString);
       self.WriteBuffer(len, 4);
       if len > 0 then
          self.WriteBuffer(oString[1], len);
    end;
    
    function TStreamEx.readInt: integer;
    begin
       self.readBuffer(result, 4);
    end;
    
    procedure TStreamEx.writeInt(data: integer);
    begin
       self.WriteBuffer(data, 4);
    end;
    
    type
      TNote = record
        Title : string;
        Note  : string;
        Index : integer;
        procedure Save(stream: TStream);
      end;
    
    procedure TNote.Save(stream: TStream);
    var
       temp: TMemoryStream;
    begin
       temp := TMemoryStream.Create;
       try
          temp.writeString(Title);
          temp.writeString(Note);
          temp.writeInt(Index);
          temp.seek(0, soFromBeginning);
          stream.writeInt(temp.size);
          stream.copyFrom(temp, temp.size);
       finally
          temp.Free;
       end;
    end;
    

    I'll leave the Load procedure to you. Same basic idea, but it shouldn't need a temp stream. With the record size in front of each entry, you can read it and know how far to skip if you're looking for a certain record # instead of reading the whole thing.

    EDIT: This was written specifically for versions of Delphi that use Unicode strings. On older versions, you could simplify it quite a bit.

    0 讨论(0)
  • 2020-12-22 07:14

    Why not write this out as XML? See my session "Practical XML with Delphi" on how to get started with this.

    Another possibility would be to make your records into classes descending form TComponent and store/retreive your data in DFM files.

    This Stackoverflow entry shows you how to do that.

    --jeroen

    PS: Sorry my XML answer was a bit dense; I'm actually on the road for two conferences (BASTA! and DelphiLive! Germany).

    Basically what you need to do is very simple: create a sample XML file, then start the Delphi XML Data Binding Wizard (available in Delphi since version 6).

    This wizard will generate a unit for you that has the interfaces and classes mapping XML to Delphi objects, and a few helper functions for reading them from file, creating a new object, etc. My session (see the first link above) actually contains most of the details for this process.

    The above link is a video demonstrating the usage of the Delphi XML Data Binding Wizard.

    0 讨论(0)
提交回复
热议问题