4

过去,我看到过这项工作,但我从来没有真正理解它应该如何完成。
假设我们有一个已知数据类型长度未知的文件,比如一个动态数组TSomething,其中

type
  TSomething = class
    Name: String;
    Var1: Integer;
    Var2: boolean;
  end;

但是,问题是这种对象类型将来可能会扩展,添加更多变量(例如Var3: String)。
然后,使用旧版本保存的文件将不包含最新的变量
文件读取过程应该以某种方式识别中的数据,其算法如下:

procedure Read(Path: String)
begin
  // Read Array Size
  //   Read TSomething --> where does this record end? May not contain Var3!
  //   --> how to know that the next data block I read is not a new object?
end;

我已经看到与BlockReadand一起工作BlockWrite,并且我假设每个对象可能应该在将其自身写入文件之前写入其大小,但我希望有一个示例(不一定是代码),以了解我正在朝着正确的方向思考。

我发现的相关阅读材料:
SO - Delphi 2010:如何将整个记录保存到文件中?
Delphi Basics - BlockRead
SO - 将对象的动态数组读/写到文件 - Delphi
SO - 如何在 Delphi 中将动态数组保存到 FileStream?

4

4 回答 4

7

为了使这项工作,您需要将元素大小写入文件。然后,当您读取文件时,您会读取允许您读取每个完整元素的元素长度,即使您的程序不知道如何理解所有元素。

如果您的记录仅包含简单类型,那么将您的记录与磁盘记录匹配就很容易了。在这种情况下,您可以从文件Min(ElementLength, YourRecordSize)字节中读取到您的记录中。

但看起来您实际上并没有这种情况。您的记录实际上是一个类,因此不适合内存复制。更重要的是,它的第一个成员是一个绝对不是简单类型的字符串。

回到过去(比如 1970 年代),您描述的技术是文件的读取方式。但是这些天,编程已经开始了。将结构化数据保存到文件通常意味着使用更灵活和适应性更强的序列化格式。您应该考虑使用 JSON、XML、YAML 或类似方法来完成此类任务。

于 2013-10-03T06:50:07.277 回答
5

我会说您需要一种对文件进行版本控制的方法。这样您就知道文件中包含什么版本的记录。写在文件的开头,然后在读取时,先读取版本标识符,然后使用相应的结构读取其余部分。

于 2013-10-03T06:57:23.677 回答
1

如果我对您的理解正确,您的主要问题是是否发生了TSomething变化。最重要的是您需要将版本信息添加到文件中,这是您无法避免的。

至于使用Sqlite的实际存储很可能会解决您的所有问题,但根据您的情况,这可能是矫枉过正。

除了特殊情况,我真的不会担心过多地扩展类。如果在文件的开头添加版本号,则可以在类更改后轻松转换文件。您需要做的就是实施您的解决方案,以便添加转换尽可能简单合理。

为了读/写文件,我更喜欢流/XML/JSON(取决于情况)而不是 blockread/blockwrite,因为您不必实施 hack 来存储版本号。

从理论上讲,您还可以为每条记录使用未使用的空间,因此如果类更改到某个点(直到您有足够的未使用空间),您可以避免重新创建整个文件。如果TSomething经常更改并且文件很大,这可能会有所帮助,但我很可能不会走那条路。

于 2013-10-03T06:56:21.273 回答
1

我会这样做:在标题中包含一个简单的版本号。这可以是任何字符串、整数或其他。

读取和写入文件非常容易(我使用的是伪代码):

Procedure Read (MyFile : TFile);
Var
  reader : IMyFileReader;

begin
  versionInfo = MyFile.ReadVersionInfo();
  reader = ReaderFactory.CreateFromVersion(versionInfo);
  reader.Read(MyFile);
end;


Type
  ReaderFactory = Class
  public 
    class function CreateFromVersion(VersionInfo : TVersionInfo) : IMyFileReader;
  end;

function ReaderFactory.CreateFromVersion(VersionInfo : TVersionInfo) : IMyFileReader;
begin
  if VersionInfo = '0.9-Alpha' then
    result := TVersion_0_9_Alpha_Reader.Create()
  else if VersionInfo = '1.0' then
    result := TVersion1_0_Reader.Create()
  else ....
end;

这可以很容易地永久维护和扩展。您将永远不必触摸读取程序,而只需添加一个新的读取器并增强工厂。通过简单的注册方法和一个TDictionary<TVersionInfo,TMyFileReaderClass>,您甚至可以避免修改工厂。

于 2013-10-03T08:28:50.980 回答