将打包记录数组保存到磁盘

Save array of packed record to disk

我快疯了。 我正在尝试将一组打包记录保存到磁盘以供稍后读取。

以下单元主要包含两个程序。

  1. 第一个InitSaveAndReLoad(),初始化一个打包的记录数组,将其保存到磁盘并从磁盘重新加载到一个新的打包记录数组中,然后通过加载数组并打印第 20 个值。完美运行。
  2. 第二个LoadFromFile(),只是从磁盘重新加载数组。它甚至可以由 InitSaveAndReload() 调用,并且一旦文件先前已由 应用程序的相同实例 创建,它就会完美运行。我的意思是,如果我退出应用程序并重新启动,仅将文件重新加载到记录数组中的 LoadFromFile() 过程将不再起作用。我不明白为什么。

有线索吗?

感谢您的帮助。已经在这个问题上花了一整天并且发疯了!

unit Unit4;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.DateUtils,
  System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Menus, Vcl.StdCtrls;

type
  TRate = packed record
    time        : int64;
    open        : double;
    low         : double;
    high        : double;
    close       : double;
    tick_volume : int64;
    spread      : integer;
    real_volume : int64;
  end;

  PRate = ^TRate;

  TForm4 = class(TForm)
    MemoLogs: TMemo;
    SaveDialog1: TSaveDialog;
    edFile: TEdit;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    procedure InitSaveAndReload(Sender: TObject);
    procedure Reload(Sender: TObject);
    procedure SelectFile(Sender: TObject);
    procedure LoadFromFile();
    procedure Button4Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form4: TForm4;

implementation

{$R *.dfm}

function TimeElapsedToString(time : int64; show_ms : boolean = false) : string;
var
  TmpVal:real;
  TmpStr:string;
begin
  TmpVal := time;
  TmpStr := '';

  TmpVal := TmpVal / 3600000;
  TmpStr := inttostr(trunc(TmpVal));
  if Length(TmpStr) = 1 then TmpStr := '0' + TmpStr;


  TmpVal := (TmpVal-trunc(TmpVal))* 3600000;
  TmpVal := TmpVal / 60000;
  if TmpVal<10 then
     TmpStr := TmpStr + ':0' + inttostr(trunc(TmpVal))
  else
     TmpStr := TmpStr + ':' + inttostr(trunc(TmpVal));

  TmpVal := (TmpVal-trunc(TmpVal))*60000;
  TmpVal := TmpVal / 1000;
  if TmpVal<10 then
     TmpStr := TmpStr + ':0' + inttostr(trunc(TmpVal))
  else
     TmpStr := TmpStr + ':' + inttostr(trunc(TmpVal));

  if show_ms then
  begin
    TmpVal := (TmpVal-trunc(TmpVal))*1000;
    TmpVal := TmpVal;
    if TmpVal<10 then
      TmpStr := TmpStr + ':00' + inttostr(trunc(TmpVal))
    else if TmpVal<100 then
      TmpStr := TmpStr + ':0' + inttostr(trunc(TmpVal))
    else
      TmpStr := TmpStr + ':' + inttostr(trunc(TmpVal));

  end;

  Result := TmpStr;
end;

procedure TForm4.SelectFile(Sender: TObject);
begin
  if SaveDialog1.Execute then
     edFile.Text := SaveDialog1.FileName;
end;

procedure TForm4.Button4Click(Sender: TObject);
begin
  MemoLogs.Lines.Clear;
end;

procedure TForm4.InitSaveAndReload(Sender: TObject);
var
  _start   : TDatetime;
  ARate    : PRate;
  filename : string;
  Stream   : TFileStream;
  i,L      : integer;
  rates_M1 : array of PRate;
  //rates    : array of PRate;
begin
  filename := edFile.Text;

  MemoLogs.Lines.Add('Initialization of 7 million array of records... Please wait.');
  Refresh;

  // init array
  _start := Now;
  SetLength(rates_M1, 7000000);
  for i:= 0 to 6999999 do
  begin
    New(ARate);
    ARate.time        := DateTimeToUnix(IncMinute(Now, i));
    ARate.open        := 1.25698;
    ARate.low         := 1.2574;
    ARate.high        := 1.2547;
    ARate.close       := 1.65874;
    ARate.tick_volume := 154;
    ARate.spread      := 5;
    ARate.real_volume := 15741;
    rates_M1[i]       := ARate;
  end;
  MemoLogs.Lines.Add(IntToStr(Length(rates_M1)) + ' array of records initialized ' + TimeElapsedToString(MilliSecondsBetween(Now, _start), true));

  // save array
  _start:= Now;
  Stream:= TFileStream.Create(filename , fmCreate);
  try
    L:= Length(rates_M1);
    Stream.WriteBuffer(L, SizeOf(L));
    Stream.WriteBuffer(Pointer(rates_M1)^, L * SizeOf(ARate));
  finally
    Stream.Free;
    MemoLogs.Lines.Add(IntToStr(Length(rates_M1)) + ' records saved to disk in ' + TimeElapsedToString(MilliSecondsBetween(Now, _start), true));
  end;

  LoadFromFile();

end;

procedure TForm4.LoadFromFile;
var
  _start   : TDatetime;
  ARate    : PRate;
  filename : string;
  Stream   : TFileStream;
  i,L      : integer;
  rates    : array of PRate;
begin
  // reload array
  _start := Now;
  filename := edFile.Text;
  Stream:= TFileStream.Create(filename , fmOpenRead);
  try
    Stream.Read(L, SizeOf(L));
    //SetLength(rates_M1, L);
    // even use another empty array of ARate to be sure I am not using the same filled array!
    SetLength(rates, L);
    // I don't want to parse all records...
//    for i := 0 to L-1 do
//      begin
//        Stream.Read(rates_M1[i].AID, SizeOf(ARecord.AID));
//        Stream.Read(rates_M1[i].time, SizeOf(ARecord.time));
//      end;
    Stream.Read(Pointer(rates)^, L * SizeOf(ARate));
  finally
    Stream.Free;
    MemoLogs.Lines.Add(IntToStr(Length(rates)) + ' records loaded from disk in ' + TimeElapsedToString(MilliSecondsBetween(Now, _start), true));
  end;

  // Print 20 first records just reloaded!
  MemoLogs.Lines.Add('Print 20 first records just reloaded in another array of records!' + TimeElapsedToString(MilliSecondsBetween(Now, _start), true));
  for i := 0 to 20 do
     MemoLogs.Lines.Add('i=' + IntToStr(i) + #9
                      + IntToStr(rates[i].time) + #9
                      + FloatToStr(rates[i].open) + #9
                       );

end;

procedure TForm4.Reload(Sender: TObject);
begin
  LoadFromFile();
end;

end.

Result

当我说 'Does not works anymore' 时,我的意思是一旦您调用了 InitSaveAndReload() 过程,您可以根据需要多次调用 LoadFromFile(),但是如果您在启动应用程序后立即调用此过程,尝试使用由 InitSaveAndReload 过程创建的旧文件,然后它无法正常工作!

单位提供越简单越好。只需创建一个新项目,添加 3 个按钮,一个 TMemo 和一个 TEdit。如果我能加入一个 .rar 我会附上这个项目...

您保存的是每条记录的地址,而不是记录的内容。您有一个指针数组,并将该数组保存到文件中。使用您当前的数据结构,您需要单独保存每条记录,因为实际数据在内存中并不连续。