将对象的动态数组读取/写入文件-Delphi [英] Reading/writing dynamic arrays of objects to a file - Delphi

查看:194
本文介绍了将对象的动态数组读取/写入文件-Delphi的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一些代码,该代码会将动态对象数组读/写到文件中。这些对象表示Java源代码的结构。我需要能够扫描整个源代码并收集有关字段,方法和类的信息。我有一个算法可以做到这一点,结果保存在TFieldStruc,TMethodStruc和TClassStruc的结构中,它们是TCoreStruc的所有后代(TObject的后代)。 Java源代码需要花费几分钟才能被扫描并生成虚拟结构。因此,我的应用程序扫描了所有源代码一次,并将其保存为更易于访问的格式,该格式可在IDE启动时加载。



有没有办法(其他而不是将对象导出为字符串,然后在加载时重新创建它们)以将TFieldStruc,TMethodStruc和TClassStruc的整个三个数组流式传输到一个文件中,以便以后可以读取它们?



我尝试读写 TFieldStruc ..的文件和TFileStream来将对象保存到文件中并读回,但是在两种情况下,我都获得了不可访问的值再次访问对象后,调试器将出现访问冲突错误。如果有人对如何解决此问题有想法,将不胜感激。以下是TCodeStruc的代码(如果其任何字段/方法可能引起问题):

  type 
TCoreStruc = class (TObject)
public
LowerPointer:整数;
HigherPointer:整数;
Line:整数;
Word:整数;
Char:整数;
CoreType:ansistring;
IsPublic:布尔值;
IsPrivate:布尔值;
IsStatic:布尔值;
IsFinal:布尔值;
名称:ansistring;
NestedStruc:TCoreStruc;
构造函数Create(Name,CoreType:ansistring; NestedStruc:TCoreStruc; IsPublic,IsPrivate,IsStatic,IsFinal:布尔值);
过程UpdateValues(NestedStruc:TCoreStruc; IsPublic,IsPrivate,IsStatic,IsFinal:布尔值);
过程SetPosition(Line,Word,Char:integer);
结尾;


解决方案

以下是使用您的结构的示例。



关于此的一些注意事项:




  • 有很多不同的方法可以去做这个。采纳David Heffernan的建议,并进行一些搜索以进行序列化。我在一个应用程序中使用了以下方法,但其他方法包括使用RTTI / Persistent对象迭代对象的已发布属性。有些库会为您迭代对象并完成所有工作。


  • 您实际上需要在字符串中写出动态对象的大小。这包括数组和字符串之类的东西。


  • 在我的示例中,每个对象都知道如何向流中读取和写入自身。


  • <我对对象的固定长度部分使用结构。这使我不必单独编写每个数据元素。


  • 字符串需要自行编写以包含其大小(您可以使用固定长度的缓冲区(例如Delphi短字符串),但要花费很多时间编写一个常规字符串。您需要确定要写入的字符串数据的格式类型。我为应用程序选择了UTF8。


  • 对于其他数组您可以在第一个数组写完后写它们的数据(包括长度),有时会有一个标头部分在顶部包含所有动态部分的长度,其他部分将在结构开始之前写长度写。部分是始终以相同的顺序编写事物,并在其中包含可以重新读取多少事物的地方。


  • 在其中没有错误检查或验证下面的文件结构。如果读取和写入之间有任何不同,它将炸毁-可能会出现流读取错误。


  • 对结构的任何更改都会造成老可能无法正确阅读。有多种方法可以对文件进行版本控制,以确保您仍可以阅读旧格式。不包括在这里。


  • 在您的应用程序中,您需要将TFileStream传递给读写功能。我喜欢仅使用TStream编写实际的读/写功能。然后,该对象将不在乎数据来自何处。可以是文件,也可以已经在内存中。


  • 如果将以下单元放到控制台应用程序中,则应该可以添加对




 
个单位CoreStruct;

接口

使用类,类型;

类型

TCoreStructData =打包记录
LowerPointer:整数;
HigherPointer:整数;
Line:整数;
Word:整数;
Char:整数;
IsPublic:布尔值;
IsPrivate:布尔值;
IsStatic:布尔值;
IsFinal:布尔值;
HasNested:布尔值;
结尾;

TCoreStruc = class(TObject)
private
FCoreData:TCoreStructData;
FNestedStruc:TCoreStruc;

过程SetNestedStruc(AValue:TCoreStruc);
public
CoreType:字符串;
名称:字符串;


构造函数Create();超载;
过程WriteToStream(Stream:TStream);
过程ReadFromStream(Stream:TStream);

//构造函数Create(Name,CoreType:ansistring; NestedStruc:TCoreStruc; IsPublic,IsPrivate,IsStatic,IsFinal:boolean);超载;
//过程UpdateValues(NestedStruc:TCoreStruc; IsPublic,IsPrivate,IsStatic,IsFinal:布尔值);
//过程SetPosition(Line,Word,Char:integer);

属性LowerPointer:整数读取FCoreData.LowerPointer写入FCoreData.LowerPointer;
属性HigherPointer:读取FCoreData.HigherPointer的整数,写入FCoreData.HigherPointer;
属性Line:读取FCoreData.Line的整数,写入FCoreData.Line;
属性Word:整数读取FCoreData.Word写FCoreData.Word;
属性Char:整数读取FCoreData.Char;写入FCoreData.Char;

属性NestedStruc:TCoreStruc读取FNestedStruc写入SetNestedStruc;
结尾;

过程Main();

实现


函数ReadUTF8StringFromStream(const Stream:TStream):String;
var
n:整数;
Buffer8:Utf8String;
开始
结果:=’’;

Stream.ReadBuffer(n,SizeOf(n));

如果n = 0,则
退出;

SetLength(Buffer8,n);
Stream.ReadBuffer(Pointer(Buffer8)^,n);

结果:= String(Buffer8);

结尾;


过程WriteUtf8StringToStream(const Data:String; Stream:TStream);
var
Buffer8:Utf8String;
n:整数;
开始
//编写字符串时,我们需要确保
//字符串的长度写到流中。首先,所以
//读者知道缓冲区有多长时间。
//
//您可以根据
//的需要以不同的格式写入文件。我喜欢在写入文件时使用UTF8,但在将
转换回本机
// Delphi unicode字符串时,确实需要
//需要额外的缓冲区副本。
Buffer8:= Utf8String(Data);
n:= Length(Buffer8);

Stream.WriteBuffer(n,SizeOf(n));

Stream.WriteBuffer(Pointer(Buffer8)^,n);

结尾;


过程Main();
var
结构:TCoreStruc的数组;

ArraySize:整数;
DataStream:TMemoryStream;

ArraySize_A:整数;
Structs_A:TCoreStruc数组;
i:整数;
begin
//创建和写入一些数据
SetLength(Structs,3);
Structs [0]:= TCoreStruc.Create();
Structs [0] .HigherPointer:= 1;
结构[0]。名称:=正在测试;

Structs [0] .NestedStruc:= TCoreStruc.Create();
Structs [0] .NestedStruc.HigherPointer:= 100;

Structs [1]:= TCoreStruc.Create();
Structs [1] .HigherPointer:= 2;

Structs [2]:= TCoreStruc.Create();
Structs [2] .HigherPointer:= 3;


DataStream:= TMemoryStream.Create();

//我们需要从要写的计数开始,因此
//读者知道要循环多少次。
ArraySize:=长度(结构);
DataStream.WriteBuffer(ArraySize,SizeOf(integer));

for i:= 0到ArraySize-1
开始
Structs [i] .WriteToStream(DataStream);
结尾;


//将数据读入一组新对象
DataStream.Position:= 0;

DataStream.ReadBuffer(ArraySize_A,SizeOf(integer));
SetLength(Structs_A,ArraySize_A);

for i:= 0到ArraySize_A-1做
开始
Structs_A [i]:= TCoreStruc.Create();
Structs_A [i] .ReadFromStream(DataStream);
结尾;

结尾;

{TCoreStruc}

构造函数TCoreStruc.Create;
开始
Self.LowerPointer:= 0;
Self.HigherPointer:= 0;
Self.Line:= 0;
Self.Word:= 0;
Self.Char:= 0;

Self.NestedStruc:=无;

结尾;

过程TCoreStruc.WriteToStream(Stream:TStream);
开始
Stream.WriteBuffer(FCoreData,SizeOf(TCoreStructData));
WriteUtf8StringToStream(Name,Stream);
WriteUtf8StringToStream(CoreType,Stream);

如果FCoreData.HasNested = true,则
开始
FNestedStruc.WriteToStream(Stream)
结尾;

结尾;

过程TCoreStruc.ReadFromStream(Stream:TStream);
开始

Stream.ReadBuffer(FCoreData,SizeOf(TCoreStructData));
名称:= ReadUtf8StringFromStream(Stream);
名称:= ReadUtf8StringFromStream(Stream);

如果FCoreData.HasNested = true,则
开始
FNestedStruc:= TCoreStruc.Create();
FNestedStruc.ReadFromStream(Stream);
结尾;

结尾;

过程TCoreStruc.SetNestedStruc(AValue:TCoreStruc);
开始
FNestedStruc:= AValue;

如果AValue = nil,则
FCoreData.HasNested:=假
否则
FCoreData.HasNested:= true;

结尾;



结尾。


Im trying to write some code which will read/write a dynamic array of objects to a file. The objects represent the structure of the java source code. I need to be able to scan the whole source code and gather information on Fields, Methods and Classes. I have an algorithm which does this and the result is kept in a structure of TFieldStruc, TMethodStruc and TClassStruc, all descendants of the TCoreStruc (a descendant of TObject). The java source code takes a couple of minutes to be scanned and have the virtual structure generated. Because of this my application scans all the source code once and saves it into a much more accessible format which is loaded when ever the IDE launches.

Is there a way (other than exporting the objects 'to string' and then re-creating them again when they are loaded) to stream the entire three arrays of TFieldStruc, TMethodStruc and TClassStruc, to a file so they can be read later?

I have tried reading and writing to a 'File of TFieldStruc..' and the TFileStream to save the objects to a file and read them back, but in both cases I get 'inaccessible value' in the debugger followed by an 'Access Violation' error as soon as the object is accessed again. If anyone has ideas on how to solve this problem it would be appreciated. Below is the code to TCodeStruc if any of its fields/methods may be causing issues:

type
  TCoreStruc = class(TObject)
    public
      LowerPointer : integer;
      HigherPointer : integer;
      Line : integer;
      Word : integer;
      Char : integer;
      CoreType : ansistring;
      IsPublic : boolean;
      IsPrivate : boolean;
      IsStatic : boolean;
      IsFinal : boolean;
      Name : ansistring;
      NestedStruc : TCoreStruc;
      constructor Create(Name, CoreType : ansistring; NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean);
      procedure UpdateValues(NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean);
      procedure SetPosition(Line, Word, Char : integer);
  end;

解决方案

Here is an example using your structure.

A few notes about this:

  • There are lots of different ways to go about this. Take David Heffernan's advice and do some searches for serialization. I use the approach include below in one of my applications but other include using RTTI/Persistent objects to iterate the published properties of an object. There are libraries that will iterate object for you and do all the work.

  • You need to actually write out to the string the sizes of dynamic objects. That includes things like arrays and strings.

  • In my example each object knows how to read and write itself to the stream.

  • I use a struct for the fixed length parts of the object. That saves me from needing to write each data element individually.

  • Strings need to be written on their own to include their size (you could used a fixed length buffer like Delphi short strings but it is not that much work to write out a regular string. You need to decide what type of format you want string data written in. I picked UTF8 for my application.

  • For your other arrays you can write their data (including length) after the first array is written out. Sometime there will be a header section that includes the lengths for all the dynamic sections at the top, others will write the length write before the structure starts. The key part is to always write things in the same order and included somewhere it can be re-read how many there are.

  • There is no error checking or verification in the file structure below. If anything is different between the read and write it will blow up - probably with a stream read error.

  • Any change to the structures will cause old files to not be read properly. There are a number of ways to version a file to ensure you can still read old formats. Not included here.

  • In your application you would pass a TFileStream to the read and write function. I like to write the actual read/write functions with just a TStream. Then the object does not care where the data is coming from. It could be file, or it could already be in memory.

  • If you drop the following unit into a console application you should be able to add a call to Main and step through the example.

    
    unit CoreStruct;

    interface

    uses Classes, Types;

    type

      TCoreStructData = packed record
        LowerPointer : integer;
        HigherPointer : integer;
        Line : integer;
        Word : integer;
        Char : integer;
        IsPublic : boolean;
        IsPrivate : boolean;
        IsStatic : boolean;
        IsFinal : boolean;
        HasNested: boolean;
      end;

      TCoreStruc = class(TObject)
        private
          FCoreData: TCoreStructData;
          FNestedStruc : TCoreStruc;

          procedure SetNestedStruc(AValue: TCoreStruc);
        public
          CoreType : String;
          Name : String;


          constructor Create(); overload;
          procedure WriteToStream(Stream: TStream);
          procedure ReadFromStream(Stream: TStream);

          //constructor Create(Name, CoreType : ansistring; NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean); overload;
          //procedure UpdateValues(NestedStruc : TCoreStruc; IsPublic, IsPrivate, IsStatic, IsFinal : boolean);
          //procedure SetPosition(Line, Word, Char : integer);

          property LowerPointer: integer read FCoreData.LowerPointer write FCoreData.LowerPointer;
          property HigherPointer: integer read FCoreData.HigherPointer write FCoreData.HigherPointer;
          property Line: integer read FCoreData.Line write FCoreData.Line;
          property Word: integer read FCoreData.Word write FCoreData.Word;
          property Char: integer read FCoreData.Char write FCoreData.Char;

          property NestedStruc: TCoreStruc read FNestedStruc write SetNestedStruc;
      end;

      procedure Main();

    implementation


    function ReadUTF8StringFromStream(const Stream: TStream): String;
    var
      n: Integer;
      Buffer8: Utf8String;
    begin
      Result := '';

      Stream.ReadBuffer(n, SizeOf(n));

      if n = 0 then
        Exit;

      SetLength(Buffer8, n);
      Stream.ReadBuffer(Pointer(Buffer8)^, n);

      Result := String(Buffer8);

    end;


    procedure WriteUtf8StringToStream(const Data: String; Stream: TStream);
    var
      Buffer8: Utf8String;
      n: Integer;
    begin
      // When writing strings we need to make sure the length of the
      // string is written out to the stream.  That goes first so the
      // reader knows how long the buffer is.
      //
      // You could you different formats to write to the file depending on
      // needs.  I like using UTF8 when writing out to file, but it does
      // require an extra buffer copy when turning it back into a native
      // Delphi unicode string.
      Buffer8 := Utf8String(Data);
      n := Length(Buffer8);

      Stream.WriteBuffer(n, SizeOf(n));

      Stream.WriteBuffer(Pointer(Buffer8)^, n);

    end;


    procedure Main();
    var
      Structs: array of TCoreStruc;

      ArraySize: integer;
      DataStream: TMemoryStream;

      ArraySize_A: integer;
      Structs_A: array of TCoreStruc;
      i: integer;
    begin
      // Create and write some data
      SetLength(Structs, 3);
      Structs[0] := TCoreStruc.Create();
      Structs[0].HigherPointer := 1;
      Structs[0].Name := 'Testing';

      Structs[0].NestedStruc := TCoreStruc.Create();
      Structs[0].NestedStruc.HigherPointer := 100;

      Structs[1] := TCoreStruc.Create();
      Structs[1].HigherPointer := 2;

      Structs[2] := TCoreStruc.Create();
      Structs[2].HigherPointer := 3;


      DataStream := TMemoryStream.Create();

      // We need to start with the count we are writing out so
      // the reader knows how many times to loop.
      ArraySize := Length(Structs);
      DataStream.WriteBuffer(ArraySize, SizeOf(integer));

      for i := 0 to ArraySize - 1 do
      begin
        Structs[i].WriteToStream(DataStream);
      end;


      // Read the data into a new set of objects
      DataStream.Position := 0;

      DataStream.ReadBuffer(ArraySize_A, SizeOf(integer));
      SetLength(Structs_A, ArraySize_A);

      for i := 0 to ArraySize_A - 1 do
      begin
        Structs_A[i] := TCoreStruc.Create();
        Structs_A[i].ReadFromStream(DataStream);
      end;

    end;

    { TCoreStruc }

    constructor TCoreStruc.Create;
    begin
      Self.LowerPointer := 0;
      Self.HigherPointer := 0;
      Self.Line := 0;
      Self.Word := 0;
      Self.Char := 0;

      Self.NestedStruc := nil;

    end;

    procedure TCoreStruc.WriteToStream(Stream: TStream);
    begin
      Stream.WriteBuffer(FCoreData, SizeOf(TCoreStructData));
      WriteUtf8StringToStream(Name, Stream);
      WriteUtf8StringToStream(CoreType, Stream);

      if FCoreData.HasNested = true then
      begin
        FNestedStruc.WriteToStream(Stream)
      end;

    end;

    procedure TCoreStruc.ReadFromStream(Stream: TStream);
    begin

      Stream.ReadBuffer(FCoreData, SizeOf(TCoreStructData));
      Name := ReadUtf8StringFromStream(Stream);
      Name := ReadUtf8StringFromStream(Stream);

      if FCoreData.HasNested = true then
      begin
        FNestedStruc := TCoreStruc.Create();
        FNestedStruc.ReadFromStream(Stream);
      end;

    end;

    procedure TCoreStruc.SetNestedStruc(AValue: TCoreStruc);
    begin
      FNestedStruc := AValue;

      if AValue = nil then
        FCoreData.HasNested := false
      else
        FCoreData.HasNested := true;

    end;



    end.

这篇关于将对象的动态数组读取/写入文件-Delphi的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆