将含有不定长的数组的记录写入文件?(100分)

  • 主题发起人 主题发起人 ych2071
  • 开始时间 开始时间
Y

ych2071

Unregistered / Unconfirmed
GUEST, unregistred user!
如:
type a=packed record
id:string[6];
num:integer;
adr:array of integer; //动态数组
end;
如何写入文件中?最好用文件流形式,
也可blockread,blockwrite
 
写入:
var
FStr: TFileStream;
X: A;
begin
FStr:=TFileStream.Create('E:/abc.dat', fmCreate);
SetLength(x.adr, 2);
x.adr[1]:=1;
x.adr[2]:=2;
try
FStr.Write(x, sizeof(x));
finally
FStr.Free;
end;
end.

读出:
FStr:=TFileStream.Create('E:/abc.dat', fmOpenRead);

FStr.Read(x, Sizeof(X));
ShowMessage(IntToStr( Length(x.adr) ));

FStr.Free;

注意,Length(X.adr) 为动态数组的元素个数。
 
to hlsl:
读出不正确。
请继续解答!
十分感谢!
 
是不是定长的数组你就会写,数组有两个函数
High(数组名) 和 Low(数组名) 分别返回一个数组的上下边界。

 
我的测试结果是正确的。
例如在读出过程里面
ShowMessage(IntToStr (X.Adr[2]) );

显示结果是2, 应该是正确的。
不知道你那里有什么问题?

另外对于X.Adr可以这样操作:

for i := Low(X.Adr) to High(X.Adr) do
begin
X.Adr......
end;

我的OICQ 8596677 直接问我。
 
我解释一下:
写文件:
var
FStr: TFileStream;
X: A;
begin
FStr:=TFileStream.Create('E:/abc.dat', fmCreate);
x.id:='sdfds';
x.num:=3;
SetLength(x.adr, x.num);
x.adr[1]:=1;
x.adr[2]:=2;
try
FStr.Write(x, sizeof(x));
finally
FStr.Free;
end;
end.
读文件:
Fstr:=Tfilestream.create('e:/abc.dat',fmopenread);
Fstr.read(x.id,sizeof(x.id));
Fstr.read(x.num,sizeof(x.num));
Fstr.read(x.adr,x.num*sizeof(integer)); //结果不正确
Fstr.free;
为什么读出的结果不正确? 写入是不是也有问题?
 
或者,如果这种方法真的不能够将动态数组写入流的话,
我看可能要override TFileStream的write方法了--继承
一个流对象,写一个针对于动态数组的流对象。

如果有其它更好的方法,请赐教。
 
我在C++ Builder下也试过,用动态数组写进去正确,读出来就不行了,于是我用了C++的
动态分配数组的办法,于是就行了。
不过我看上面这位的代码里的问题是:没有设对写入和读出的Count值。关键问题是出在
动态数组与静态数组的差异里,假设你定义一个结构:
type AC = record
Name: String[6];
ID: Integer;
end;

var
StaticArray1: array[0..9] of AC;
DynamicArray1: array of AC;
begin
SetLength(DynamicArray1,10); //设置动态数组长度
ShowMessage('静态数组的尺寸为:'+IntToStr(SizeOf(StaticArray1))+','+#13+
'动态数组的尺寸为:'+IntToStr(SizeOf(DynamicArray1)));
DynamicArray1 := Nil; //释放动态数组
end;

程序运行结果:它们的尺寸分别是120和4,也就是说,通过SizeOf()并不能获取动态数组的
真实尺寸,不管该动态数组里是否已经有数据,所以,照前面那一段程序,写入一个动态的
Count值就是4,实际上并没有把该有的东西写进文件里,而读出该动态数组时的Count也只
能是4,也是什么东西都读不出来!所以你应该自己指定Count值:
var StructSize: Integer;
begin
...
//动态数组的真实尺寸为组成数组的数据类型的尺寸乘以数组长度
StructSize := SizeOf(AC)*Length(DynamicArray1);
FStr.Write(DynamicArray1, StructSize);
...
FStr.Read(DynamicArray1, StructSize);
...
end;
 
我这里刷新比较慢,所以才写了上面那段看起来像马后炮的话。:(
 
刚才我又测试了一下,发现只有最后一个元素读出错误,不知道是为什么?
代码在这里,只有小部分改动:
写入:
FStr:=TFileStream.Create('E:/abc.dat', fmCreate);
SetLength(x.adr, 10);

for I:=1 to Length(X.Adr) do
begin
x.adr:=I*2;
ShowMessage(IntToStr(X.Adr));
end;

try
FStr.Write(x, sizeof(x));
finally
FStr.Free;
end;

读出:

FStr:=TFileStream.Create('E:/abc.dat', fmOpenRead);
FStr.Read(x, Sizeof(X));
SetLength(X.adr, Length(X.Adr));

for I:= 1 to Length(X.Adr) do
ShowMessage(IntToStr (x.adr) );//最后一个元素是错的
FStr.Free;

 
注意读出的时候有一个SetLength的过程,否则全都是错的。
 
写入时:
x.id:='sdf';
x.num:=3;
setlength(x.adr,x.num);
x.adr[0]:=1;
x.adr[1]:=2;
x.adr[2]:=3;
Fstr:=Tfilestream.create('d.dat',fmcreate);
Fstr.write(x.id,sizeof(x.id));
Fstr.write(x.num,sizeof(x.num));
Fstr.write(x.adr,x.num*sizeof(integer));
Fstr.free;
读出时:
fstr:=Tfilestream.create('d.dat',fmopenread);
fstr.seek(0,sofrombeginning);
fstr.read(x.id,sizeof(x.id));
fstr.read(x.num,sizeof(x.num));
fstr.read(x.adr,x.num*sizeof(integer));//与实际不符
fstr.free;
不定长的字符串,可以用指针。
不定长的数组,怎么办?

 
写入直接用 Write(X, SizeOf(X)) 就可以了,
SizeOf可以得到真实长度,包括动态数组。

另外,读出的时候,按照你的想法,使用
Read(X, SizeOf(X));
SetLength(X.Adr, X.Num);

这样应该就可以了。不需要将每个元素单独写入,
可以一次写入一个记录。

而且,你如果用X.Num*SizeOf(Integer)的话,少了一个元素。
因为如果 SetLength(X.Adr, 5) , 那么 X.Adr 是有6个元素的。
从X.Adr[0]~~X.Adr[5],X.Adr[0]是系统用的,比如记录长度之类的。
 
to hlsl:
实际上,sizeof()的不到真实大小,
动态数组的sizeof总是4,你可以测试一下。
再者,setlength后得到的是真实长度,没有少元素。
感谢你的参与!
 
哦,是啊,我刚才对于一些基本问题犯了错误,例如SizeOf和X.Adr[0]。

SizeOf仅仅只取回了数组的第一个元素的大小,因为SizeOf(Integer)=4,
所以返回的值都是4。但是编译器将动态数组的大小保存在哪里呢?--如果
不象字符串一样的话。我知道它不会作溢出检查。

动态数组通常都是以0为基准的,SetLength(A,5),A的元素是从0~4。
这个和字符串是不同的。这一点我犯了错误啦。
Dynamic arrays are always integer-indexed, always starting from 0.
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
D
回复
0
查看
1K
DelphiTeacher的专栏
D
后退
顶部