关于Stream存储一个record结构的问题(100分)

  • 主题发起人 主题发起人 闲清
  • 开始时间 开始时间

闲清

Unregistered / Unconfirmed
GUEST, unregistred user!
STream存储一个record类型时,我怎么样才能知道record具体长度?
比如
cc = record
ab:integer;
name:string;
data:pointer;
end;
var c:cc;
不管name长度为多少,用sizeof(c)长度一直不变,
MemoryStream.write(c,sizeof(c)) 这样写有没有问题?包括name字符串以及
data所指向的一个缓冲区数据是不是都会写到MemoryStream中去?
或者不行的话是不是name换成shortString 就可以了?
请大家帮帮忙,第一次来这里。前一段时间在csdn,都没什么人回答问题,真不爽。
 
指针部分是无法保存的,要保存该纪录,ab的长度为sizeof(ab),name部分的长度为
Length(name),二者相加为总长度。
 
由于可能会有很多结构体,而且字段大小不一,
那这样请问行不行:将cc改为一个类
Tcc = class(Tobject)
private
...
name:string;
public/published
property ...
end;
这样的话我是不是可以调用instanceSize来获得长度而不需要再加上length(name)?
还有关于public或published中定义的属性长度会不会也包括在instanceSize中?
这样的话还可不可以加一个指针?
 
你可以通过去的数据类型来判断长度,具体用
function VarType(const V: Variant): Integer;方法,可查帮助。

至于你说的方法,我觉得有一定的问题,InstanceSize是用来获得该实例所需的内存空间
的大小数的,对于String类型的成员变量,由于是动态管理的,长度不定,还有就是各方
法指针,我觉得InstanceSize是无法满足你的要求的,不过你可以具体写程序是一下。
祝好运
 
如果要保存的话,可以从TPersistent继承,数据设为published property,用
WriteComponent方法可以写到流文件中,比较简单,参考dfm文件格式。
 
>>bluebridge
WriteComponent保存得是组件,与具体的对象无关
而楼顶的主要是想保存一些具体的、运行期当中的数值,这是WriteComponent无法办到的
 
我用awl的方法试啦,sizeof(ab)+length(name),从cc开始读,ab可以读取到,而name
只是那个字符串的指针,其后面并不一定紧跟着name的字符串值啊,都是些其它的数据.
我现在有个解决的办法,但不知道是否可行,就是有没有办法可以遍历一个record结构
里的所有成员,如果可以的话就好判断某个成员是什么类型的变量,如果是字符串类型
就去该指针所指的内存取字符串,将整个record形成一个所有成员值连续的缓冲。
如果能这样就好了,不知道有没有办法遍历所有成员
 
>>闲清
name不是String类型吗?怎么又变成字符串指针了?
你说的遍厉Rocord结构中的所有成员是什么意思??既然是结构中的成员自然能够访问
到了!你能说得清楚一点吗?
 
不用这么麻烦的,将String改成定长的 array[0..n]of Char 就可以了。要不然就用Class
进行Save/Load的封装。
 
name是字符串啊, 我用stream.writebuffer(cc,sizeof(ab)+sizeof(name)),再写到文件中
用二进制编辑器查看,前四个字节是ab的值,而后面根本就不是name里的字符串值,
我取出四个字节作为指针,打印成%s,发现是name的字符串值。
也就是说,Pchar(@cc)[0]..Pchar(@cc)[3]是ab的值,Pchar(@cc)[4]..Pchar(@cc)[7]
却是一个指向name的指针,这样一来用上面的方法好像不能把这个cc里的变量值都拷到流里去。
可能是我还没搞清楚

我说的遍历意思是说在未知record里有哪些成员时可否通过程序实现对全部成员的访问。
这在VB里可以实现,在DELPHI里我不知道怎么弄。
 
>>creation-zy
能不能讲详细一点,我对这个不懂,麻烦你
 
正因为Name是字符串——一种特殊的数据类型,所以不好用常规的方法来处理。在结构体
中,String类型的变量实际上只用了一个指针的空间,数据并不在结构体内部。如果你将它
至于一个定长的Char数组,就不会出现这个问题(定长的String亦可)。

type
TMyRec=record
a:Integer;
c:array[1..16]of Char; //******
end;
TMyRec_1=record
a:Integer;
c:String[16]; //******
end;
TMyRec_2=record
a:Integer;
c:String; //******
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(SizeOf(TMyRec))+#13#10+IntToStr(SizeOf(TMyRec_1))
+#13#10+IntToStr(SizeOf(TMyRec_2))); //20,24(包括长度信息),8(仅包含指针)
end;

如果不用定长结构的话,即使采用类封装,读取也会比较麻烦(要动态分配String的空间)。
 
stream.writebuffer(cc,sizeof(ab)+sizeof(name))
^^^^^^^^^^^^
前面不是说了吗?应该是
Length(name)
具体的写法是:
sstream.writebuffer(cc.ab,sizeof(ab))
stream.writebuffer(cc.name[1],Length(name)),
^^^^^^^^^^
cc.name的首指针

注意:你应该在写入string类型的变量name之前写入name的长度以便于读出的时候
能后得到这个name的长度!
 
事实上我不想用定长的字符串,因为数据在网络上传输,有时候只需要传几个字节,而
有时候则很长,可能定义的长度造成浪费,或者长度不够。
我是这样想:
  将一个record类型变量的值通过一个函数将所有成员的值按顺序存放在一个buffer中,
之间用标识符隔开。传送到网络另一端就可以根据标识符判断哪个区域属于哪个变量。
这就需要能遍历record中所有成员。(因为可能有好多record类型,所以对于这个函数来
说该record变量的成员是未知的),比如VB中有这样的语句可以遍历:
for each thing in cc
... //(thing 即是cc中成员,通过next跳到下一个成员)
next;
如果实在不行,就只能使用定长或者每个record都写一个函数将其值存为buffer了。
 
awl你说的这个方法可行!!谢谢指点。

也谢谢 creation-zy 讲解各个类型的长度,刚开始我也迷惑的。

难道真的没有办法遍历record的所有成员么?[:(]
 
to awl:
你的方法写入的确看不出任何问题,但是,你又如何正确的读取存入的数据呢?——在流
中没有包含String的长度信息,在读取时,也就无法得到String的实际长度。我认为应该这
样补救:

type
cc = record
ab:integer;
name:string;
data:pointer;
end;

//write
writebuffer(cc.ab,sizeof(integer));
writebuffer(Length(cc.name),sizeof(integer)); //*** Length of Name
writebuffer(cc.name[1],Length(name));
writebuffer(cc.data,sizeof(pointer));

//read 只写出了大致流程 readbuffer比较麻烦的说
read(cc.ab);
read(NameLen); //*** Length of Name
SetLength(cc.name,NameLen); //分配空间
read(cc.name[1],NameLen);
read(cc.data);


to 闲清:
VB是解释型语言,在这个方面是方便一些。
 
闲清:
具体传值的方法,awl和creation-zy都已经说了,就是对于不定长的数据要先传它的长度。
>>难道真的没有办法遍历record的所有成员么?
完全没有必要嘛!你可以这样做:
由于数据类型总共就那么几种,那么你可以自己做个约定,比如:数字1代表Integer,数字2
代表Boolean,数字3代表string...... 你在发送端将要传送的所有数据的情况做一个统计,
放在整个要发送的流的最前面,比如:先向流中写入一个Integer,假设是3,告诉接收方你
要发送3个数据;接下来,再写入一个Integer,比如是2,代表要发送的第1个数据是Boolean
类型的,然后,写入这个Boolean类型数据的长度(如果你在接收方判断,也可不写入,应为
某些定长的数据类型,长度是一定的);接下来,再写入一个Integer,比如是3,代表字符
串,紧接着马上写入一个Integer,写入这个字符串的长度,依此类推。接着,马上写入具体
的数据。
 
昨天说错了,你将数据定义在一个类中,如果从TComponent继承下来,所有数据都定义为
published property,那么用WriteComponent就可以自动写下所有数据,类似dfm文件格式
,对于每一项,都是先些长度,后写值,所以读取用ReadComponent就可以了。
如果嫌TComponent太大,也可以从TPersitent继承,这样就得自己覆盖方法DefineProperties
方法,需要自己定义读写数据的方法,也可以做到。
 
小笨苯 的方法我考虑过,读数据没什么问题,就是写数据的时候,我得为每一个要发送的
record写一个函数存放到流中,太麻烦,不过还是要谢谢你。bluebridge的方法不错,
我试试,昨天也找到了关于这方面的例子。回头给你们分。由于我刚注册,总分
只有200分,现在还剩下50分了,不知道怎么可以赚分。只能少一点了,各位不好意思。
 
后退
顶部