如何在Tstream中保存TObjectList

时间:2016-04-15 17:41:40

标签: delphi

我需要在TObjectList<TStrings>中保存<TStringList>(或TStream),然后将其重新审核。

要明确指出,如何将SaveToStreamLoadFromStream应用于TObjectList

3 个答案:

答案 0 :(得分:3)

尝试这样的事情:

procedure SaveListOfStringsToStream(List: TObjectList<TStrings>; Stream: TStream);
var
  Count, I: Integer;
  MStrm: TMemoryStream;
  Size: Int64;
begin
  Count := List.Count;
  Stream.WriteBuffer(Count, SizeOf(Count));
  if Count = 0 then Exit;
  MStrm := TMemoryStream.Create;
  try
    for I := 0 to Count-1 do
    begin
      List[I].SaveToStream(MStrm);
      Size := MStrm.Size;
      Stream.WriteBuffer(Size, SizeOf(Size));
      Stream.CopyFrom(MStrm, 0);
      MStrm.Clear;
    end;
  finally
    MStrm.Free;
  end;
end;

procedure LoadListOfStringsFromStream(List: TObjectList<TStrings>; Stream: TStream);
var
  Count, I: Integer;
  MStrm: TMemoryStream;
  Size: Int64;
  SList: TStringList;
begin
  Stream.ReadBuffer(Count, SizeOf(Count));
  if Count <= 0 then Exit;
  MStrm := TMemoryStream.Create;
  try
    for I := 0 to Count-1 do
    begin
      Stream.ReadBuffer(Size, SizeOf(Size));
      SList := TStringList.Create;
      try
        if Size > 0 then
        begin
          MStrm.CopyFrom(Stream, Size);
          MStrm.Position := 0;
          SList.LoadFromStream(MStrm);
          MStrm.Clear;
        end;
        List.Add(SList);
      except
        SList.Free;
        raise;
      end;
    end;
  finally
    MStrm.Free;
  end;
end;

可替换地:

procedure SaveListOfStringsToStream(List: TObjectList<TStrings>; Stream: TStream);
var
  LCount, SCount, Len, I, J: Integer;
  SList: TStrings;
  S: UTF8String;
begin
  LCount := List.Count;
  Stream.WriteBuffer(LCount, SizeOf(LCount));
  if LCount = 0 then Exit;
  for I := 0 to LCount-1 do
  begin
    SList := List[I];
    SCount := SList.Count;
    Stream.WriteBuffer(SCount, SizeOf(SCount));
    for J := 0 to SCount-1 do
    begin
      S := UTF8String(SList[J]);
      // or, if using Delphi 2007 or earlier:
      // S := UTF8Encode(SList[J]);
      Len := Length(S);
      Stream.WriteBuffer(Len, SizeOf(Len));
      Stream.WriteBuffer(PAnsiChar(S)^, Len * SizeOf(AnsiChar));
    end;
  end;
end;

procedure LoadListOfStringsFromStream(List: TObjectList<TStrings>; Stream: TStream);
var
  LCount, SCount, Len, I, J: Integer;
  SList: TStrings;
  S: UTF8String;
begin
  Stream.ReadBuffer(LCount, SizeOf(LCount));
  for I := 0 to LCount-1 do
  begin
    Stream.ReadBuffer(SCount, SizeOf(SCount));
    SList := TStringList.Create;
    try
      for J := 0 to SCount-1 do
      begin
        Stream.ReadBuffer(Len, SizeOf(Len));
        SetLength(S, Len);
        Stream.ReadBuffer(PAnsiChar(S)^, Len * SizeOf(AnsiChar));
        SList.Add(String(S));
        // or, if using Delphi 2007 or earlier:
        // SList.Add(UTF8Decode(S));
      end;
      List.Add(SList);
    except
      SList.Free;
      raise;
    end;
  end;
end;

答案 1 :(得分:1)

列表中的内容是什么?
这取决于您的对象列表中的对象类型 您循环遍历列表并依次保存每个项目。

但是,列表中的对象需要SaveToStream方法 由于未知SaveToStream的原因不是TPersistent的方法,而是在不同的类中独立实现。

测试流支持
如果VCL是在构建时考虑了接口的,那么在较新的版本中已经使用IStreamPersist interface解决了 如果列表中的所有内容都来自具有内置流的基类(例如TComponent),则没有问题,您可以使用TComponent.SaveToStream

type
  TStreamableClass = TStrings;  //just to show that this does not depend on TStrings.

procedure SaveToStream(List: TObjectList; Stream: TStream);
var
  i: integer;
begin
  for i:= 0 to List.Count -1 do begin
    if List[i] is TStreamableClass then begin
      TStreamableClass(List[i]).SaveToStream(Stream);
    end;
  end; {for i}
end;

添加流支持
如果列表中的项目不是来自普通的可流式祖先,那么您必须在循环中进行多次if list[i] is TX测试。

如果对象没有SaveToStream方法,但你有足够的知识来自己实现它,那么你有两个三个选项。

A:实现一个类助手,将SaveToStream添加到该类或B:添加一个实现该选项的后代类。
如果这些是您自己的对象,请参阅下面的选项C:

type 
  TObjectXStreamable = class(TObjectX)
  public
    procedure SaveToStream(Stream: TStream); virtual;
    procedure LoadFromStream(Stream: TStream); virtual;
  end;

procedure SaveToStream(List: TObjectList; Stream: TStream);
...
  if List[i] is TObjectX then TObjectXStreamable(List[i]).SaveToStream(Stream);
...

请注意,如果TObjectX具有附加数据的子类,则此方法将失败。添加的流媒体不会知道这些额外的数据。

选项C:实现System.Classes.IStreamPersist

type
  IStreamPersist = interface
    ['<GUID>']
    procedure SaveToStream(Stream: TStream);
    procedure LoadFromStream(Stream: TStream);
  end;

  //enhance your streamable objects like so:
  TInterfaceBaseObject = TInterfacedObject //or TSingletonImplementation

  TMyObject = class(TInterfaceBaseObject, IStreamPersist)
    procedure SaveToStream(Stream: TStream); virtual;
    procedure LoadFromStream(Stream: TStream); virtual;

请参阅:Bypassing (disabling) Delphi's reference counting for interfaces

您测试IStreamPersist支持using the supports call

if Supports(List[i], IStreamPersist) then (List[i] as IStreamPersist).SaveToStream(Stream);

如果您有更新版本的Delphi,请考虑使用通用的TObjectList,这样您就可以将列表限制为:MyList: TObjectList<TComponent>;
现在你可以调用MyList[i].SaveToStream,因为Delphi知道列表只包含TComponent的(后代)。

答案 2 :(得分:-2)

您需要创建自己的例程来执行此操作:一个用于保存,另一个用于加载。 为了保存,循环遍历列表,将列表的每个指针转换为十六进制(十进制,八进制),然后添加一个分隔符,如&#39;,&#39 ;;完成后将字符串contains包含在流中。 对于加载,循环遍历列表,搜索第一个分隔符,提取值,将其转换回指针,然后将其添加到列表中。

Procedure ObjListToStream(objList: TObjectList; aStream: TStream);
var
  str: String;
  iCnt: Integer;
Begin
  if not assigned(aStream) then exit; {or raise exception}
  for iCnt := 0 to objList.Count - 1 do 
  begin
    str := str + IntToStr(Integer(objList.Items[iCnt])) + ',';
  end;
  aStream.Write(str[1], Length(str));
End;

Procedure StreamToObjList(objList: TObjectList; aList: String);
var
  str: String;
  iCnt: Integer;
  iStart, iStop: Integer;
Begin
  try
  if not assigned(aStream) then exit; {or raise exception}
  iStart := 0;
  Repeat
    iStop := Pos(',', aList, iStart);
    if iStop > 0 then 
    begin
      objList.Add(StrToInt(Copy(sList, iStart, iStop - iStart)));
      iStart := iStop + 1;
    end;
  Until iStop = 0;
  except
    {something want wrong}
  end;
End;

我没有测试它并从内存中写下来。但它应该指出你正确的方向。