Arrays 设置数组(记录数)长度时出现Delphi堆栈溢出和访问冲突错误

Arrays 设置数组(记录数)长度时出现Delphi堆栈溢出和访问冲突错误,arrays,delphi,stack-overflow,Arrays,Delphi,Stack Overflow,我正忙于构建一个应用程序,在这个应用程序中,我从两个以上的“记录”文件中读取数据。我有一个非常奇怪的错误,它会根据我打开文件的顺序弹出(见下面的代码) 如果我点击按钮1,然后点击按钮2,这样调用“天气数据记录”文件,然后调用“参数记录”文件,一切正常。如果以另一种方式执行此操作,则会出现“堆栈溢出”错误,后跟“0x7c90e898处的访问冲突:地址写入”错误。在Button1Click中调用数组的SetLength时会发生这种情况 天气数据文件约有550条记录,参数文件约有45条记录 有人能看到

我正忙于构建一个应用程序,在这个应用程序中,我从两个以上的“记录”文件中读取数据。我有一个非常奇怪的错误,它会根据我打开文件的顺序弹出(见下面的代码)

如果我点击按钮1,然后点击按钮2,这样调用“天气数据记录”文件,然后调用“参数记录”文件,一切正常。如果以另一种方式执行此操作,则会出现“堆栈溢出”错误,后跟“0x7c90e898处的访问冲突:地址写入”错误。在Button1Click中调用数组的SetLength时会发生这种情况

天气数据文件约有550条记录,参数文件约有45条记录

有人能看到我的代码有什么明显的错误吗?我不知道如何附加文件,或使他们可用,如果有人想用他们来测试

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, Buttons, ExtCtrls, Grids,FileCtrl,Contnrs;

type  
    TWeatherData = record  
    MyDate : TDate;  
    Rainfall : Double;  
    Temperature : Double;  

  end;

  TParameters = record
    Species : string[50];
    ParameterName: string[50];
    ParameterValue : double;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
Var
  WeatherDataFile : file of TWeatherData;
  j : integer;
  WeatherDataArray : array of TWeatherData;
  MyFileSize : Integer;

begin


  AssignFile(WeatherDataFile,'C:\Test5.cmbwthr') ;
  Reset(WeatherDataFile);
  MyFileSize := FileSize(WeatherDataFile);

  SetLength(WeatherDataArray,MyFileSize);

  j := 0;

  try
   while not Eof(WeatherDataFile) do begin
    j := j + 1;
    Read (WeatherDataFile, WeatherDataArray[j]) ;
   end;
  finally
   CloseFile(WeatherDataFile) ;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  ParametersFile : file of TParameters;
  j : integer;
  CurrentParameters : array of TParameters;
  MyFileSize : Integer;

begin
  AssignFile(ParametersFile,'C:\Test5.cmbpara') ;
  Reset(ParametersFile);

  Reset(ParametersFile);
  MyFileSize := FileSize(ParametersFile);

  SetLength(CurrentParameters,MyFileSize);

  j := 0;

  try
   while not Eof(ParametersFile) do begin
    j := j + 1;
    Read (ParametersFile, CurrentParameters[j]) ;
   end;
  finally
   CloseFile(ParametersFile) ;
  end;
end;

end. 

通过在写入数组之前而不是之后增加索引,可以写入数组的末尾。由于您正在写入不属于数组的内存,因此可能会出现任何数量的问题

AssignFile(ParametersFile, 'C:\Test5.cmbpara');
Reset(ParametersFile);
try // Enter "try" block as soon as the file is opened.
  MyFileSize := FileSize(ParametersFile);
  SetLength(CurrentParameters, MyFileSize);

  j := 0;
  while not Eof(ParametersFile) do begin
    Read(ParametersFile, CurrentParameters[j]);
    Inc(j);
  end;
finally
  CloseFile(ParametersFile);
end;

if j <> MyFileSize then
  raise Exception.CreateFmt('Parameter count mismatch: expected %d but got %d instead.',
    [MyFileSize, j]);
AssignFile(参数文件'C:\Test5.cmbpara');
重置(参数文件);
try//打开文件后立即输入“try”块。
MyFileSize:=文件大小(参数文件);
SetLength(CurrentParameters,MyFileSize);
j:=0;
而不是Eof(参数文件)开始
读取(参数文件,当前参数[j]);
公司(j);
结束;
最后
关闭文件(参数文件);
结束;
如果j MyFileSize那么
引发异常。CreateFmt('参数计数不匹配:预期为%d,但实际为%d',
[MyFileSize,j]);

您需要打包记录才能保存到文件中

type  
  TWeatherData = packed record  
    MyDate : TDate;  
    Rainfall : Double;  
    Temperature : Double;  
  end;

  TParameters = packed record
    Species : string[50];
    ParameterName: string[50];
    ParameterValue : double;
  end;

请查看我们单元中可用的
TDynArray
包装器。其中包含序列化功能

您可以将常规字符串放在记录中,而不是短字符串:它将占用更少的磁盘空间,并且自Delphi 2009起就可以使用Unicode

type  
  TWeatherData = record  
    MyDate : TDate;  
    Rainfall : Double;  
    Temperature : Double;  
  end;
  TWeatherDatas = array of TWeatherData;

  TParameter = record
    Species : string;
    ParameterName: string;
    ParameterValue : double;
  end;
  TParameters = array of TParameter;

var
  Stream: TMemoryStream;
  Params: TParameters;
  Weather: TWeatherDatas;
begin
  Stream := TMemoryStream.Create;
  try
    Stream.LoadFromFile('C:\Test5.cmbpara');
    DynArray(TypeInfo(TParameters),Params).LoadFromStream(Stream));
    Stream.LoadFromFile('C:\Test5.cmbwthr');
    DynArray(TypeInfo(TWeatherDatas),Weather).LoadFromStream(Stream));
  finally
    Stream.Free;
  end;
end;
使用
TDynArray
,您可以使用类似
TList
的属性和方法访问任何动态数组,例如
Count、Add、Insert、Delete、Clear、IndexOf、Find、Sort
,以及一些新方法,如
LoadFromStream、SaveToStream、,LoadFrom
SaveTo
允许任何动态数组的快速二进制序列化,甚至包含字符串或记录-还可以使用
CreateOrderedIndex
方法根据动态数组内容创建单个索引。如果愿意,还可以将数组内容序列化为JSON


对于。

未通过结尾,但如果未通过记录大小重置,则无法按文件大小获取记录数(除非WeatherData为128字节)。请参阅这两个函数的文档。@Sertac Akyuz:使用Reset(File,SizeOf(Record))将此作为答案发布,我将投赞成票。获取文件大小似乎不是问题。FileSize函数返回的文件大小在这两种情况下都是准确的。不管我将长度设置为什么大小,SetLength上的错误都会返回…@David,你能发布一个指向示例文件的链接吗?塞塔克的回答是有道理的,如果它是正确的,你可能会在某处覆盖内存。另外,您是否在编译器选项中启用了范围检查?@David-我的错!根据Rob的评论,“RecSize参数只允许用于非类型化的文件。如果文件类型如问题中所述,则记录大小是隐式的,因为编译器已经知道记录类型。”谢谢Rob!这就是问题所在。令人惊讶的是,这个愚蠢的错误产生了多么戏剧性的效果!需要是一个强有力的词。事实上,太强了。使用压缩记录而不是“普通”记录是否有好处?@David-它们对编译器的“记录字段对齐”设置“免疫”。如果解包,编译器将使用{$An}指定的或在编译器选项中设置的任何值来布局记录。如果打包,它们将有效地用{$A1}编译。其效果是:1。最低磁盘使用率(不是大问题)2。更改编译器设置或使用不同版本的Delphi不会更改程序所依赖的内部布局。