【问题标题】:How do I save an "array of record" to a TMemoryStream (to then store in a sqlite BLOB field)?如何将“记录数组”保存到 TMemoryStream(然后存储在 sqlite BLOB 字段中)?
【发布时间】:2011-03-14 17:37:38
【问题描述】:

最终我的目标是能够将记录数据结构数组保存在 sqlite BLOB 字段中,但在此期间,我正在尝试序列化记录数组并将其存储在 TMemoryStream 中。如果可能相关,我将Tim Anderson's sqlite wrapper (unicode) 与 Delphi 2010 一起使用。

这是我的记录声明数组:

type
  TPerson = array of packed record
    sCountry: string[50];
    sFullName: string[100];
    sAddress: string[100];
    sCity: string[30];
    sEmployer: string[100];
  end;

var
  MyPeople       : TPerson;

这是我目前用来将此记录数组序列化为 TMemoryStream 的代码。问题是它不起作用:

var
  i : integer;
  ms : TMemoryStream:
  ms2 : TMemoryStream;    
  TestPeople : TPerson;
  sldb              : TSQLiteDatabase;
begin
    ms := TMemoryStream.Create;
    ms2 := TMemoryStream.Create;
    sldb := TSQLiteDatabase.Create(slDBPath); //sldBPath is a global variable with path to sqlite db
    try
        i := Length(MyPeople);
        ms.Write(i, 4);
        ms.Write(pointer(MyPeople)^, i * sizeOf(MyPeople));
        ///
        ms2.Read(i, 4);  
        SetLength(ms2, i);
        ms2.Read(pointer(TestPeople)^, i * sizeOf(TestPeople));

        WriteLn('#############' + ms2[0].sFullName); //check if we can read data back

        sQuery := 'UPDATE PersonDirectory SET fldPersonBlob = ? WHERE id = "' + 42 + '";';
        sldb.UpdateBlob(sQuery, ms);

    finally
      ms.Free;
      ms2.Free;
      sqldb.Free;
    end;
end;

关于如何将记录数组序列化为 TMemoryStream 或将记录数组存储在 sqlite BLOB 字段中的更好方法的任何想法?

【问题讨论】:

    标签: delphi sqlite delphi-2010


    【解决方案1】:

    使TPerson成为打包记录,并将TPersons声明为TPerson数组。

    type
      TPerson = packed record
        sCountry: string[50];
        sFullName: string[100];
        sAddress: string[100];
        sCity: string[30];
        sEmployer: string[100];
      end;
      TPersons = array of TPerson;
    var
      MyPeople: TPersons;
    

    这使您可以通过 SizeOf(TPerson) 获得正确的记录大小,而 Length(MyPeople) 将为您提供数组的长度。将它们相乘以获得总字节大小。这应该让你开始。

    还请注意,一些读写操作从流的开头开始,而其他操作则从当前的position 继续。有时需要在将流的内容复制到另一个流之前将 position 设置为 0。文档会提到这一点。

    【讨论】:

    • 仍然可以查到元素的大小:SizeOf(MyPeople[0]).
    • 是的,你是对的,但是如果你像这样拆分类型,代码会感觉更清晰。命名也更好,因为 TPerson 可以包含多个人。 :)
    • 同意,特别是命名!
    【解决方案2】:

    也许我错了,但你是在 ms 中写作,而在 ms2 中阅读。

    ms2 是一个内存流,但不要指向 ms 所指向的相同内存内容。如果您不更改代码,我认为 ms2 不会从 ms 读取内容。

    如果需要将ms的内容放入ms2,试试

    ms2.LoadFromStream(ms);
    

    在您开始阅读内容之前。

    当然,您需要知道单个记录的大小才能进行正确的数学运算。

    【讨论】:

    • 我认为 Mick 没有发布他所有的代码,可能在 /// 部分中有代码这样做。
    • 还要注意,如果你是对的,你需要在调用ms2.LoadFromStream之前设置ms.Position := 0。如果 Mick 确实 发布了整个代码(而 Sertac 是错误的),那么您就成功了。 :)
    【解决方案3】:

    您可以使用带有纯字符串的记录数组,然后使用我们的 TDynArray 包装器将它们保存到流中。

    它将比短字符串使用更少的空间,因为它只会写入字符串使用的字符。例如,定义为 string[100] 的 sEmployer 在直接存储时将始终使用 101 个字节。而对于我们的 TDynArray,它只会使用字符数加一。

    适用于 Delphi 6 到 XE。

    type
      TPerson = packed record
        sCountry: string;
        sFullName: string;
        sAddress: string;
        sCity: string;
        sEmployer: string;
      end;
      TPersons = array of TPerson;
    
    var
      MyPeople: TPersons;
    
    (...)
    procedure SavePeopleToStream(Stream: TMemoryStream);
    begin
      DynArray(TypeInfo(TPersons),MyPeople).SaveToStream(Stream);
    end;
    

    在 TDynArray 中,您可以使用反向 LoadFromStream 方法,等等。 见this blog entry to get this wrapper

    附加说明: 有趣的是,这与I'm about to add to our ORM 完全相同的功能:将动态数组内容存储在 BLOB 字段中,编码为二进制。但在我们的例子中,由于 Delphi RTTI,一切都将实现自动化。并且还有一个客户端/服务器层,使用 JSON 进行传输,以使其更符合 n-Tier。

    【讨论】:

    • 就是这样:我在我们的框架中添加了动态数组作为有效的发布属性。还有TPersistent, TStringsTCollection。动态数组存储在 BLOB 中,TPersistent, TStringsTCollection 存储在 TEXT 字段中,JSON 编码(作为 JSON 数组和/或对象)。
    猜你喜欢
    • 2013-02-13
    • 1970-01-01
    • 2011-02-22
    • 2014-04-07
    • 2013-02-08
    • 2022-08-12
    • 2018-06-11
    • 1970-01-01
    • 2011-12-14
    相关资源
    最近更新 更多