TWriter对象提供了许多往流中写各种类型数据的方法,这对于程序员来说是很重要的功能。TWrite对象往流中写数据是依据不同的数据采取不同的格式的。因此要掌握TWriter对象的实现和应用方法,必须了解Writer对象存储数据的格式。
首先要说明的是,每个Filer对象的流中都包含有Filer对象标签。该标签占四个字节其值为“TPF0”。Filer对象为WriteSignature和ReadSignature方法存取该标签。该标签主要用于Reader对象读数据(部件等)时,指导读操作。
其次,Writer对象在存储数据前都要留一个字节的标志位,以指出后面存放的是什么类型的数据。该字节为TValueType类型的值。TValueType是枚举类型,占一个字节空间,其定义如下:
TValueType = (VaNull, VaList, VaInt8, VaInt16, VaInt32,VaEntended, VaString, VaIdent,
VaFalse, VaTrue, VaBinary, VaSet, VaLString, VaNil, VaCollection);
因此,对Writer对象的每一个写数据方法,在实现上,都要先写标志位再写相应的数据;而Reader对象的每一个读数据方法都要先读标志位进行判断,如果符合就读数据,否则产生一个读数据无效的异常事件。VaList标志有着特殊的用途,它是用来标识后面将有一连串类型相同的项目,而标识连续项目结束的标志是VaNull。因此,在Writer对象写连续若干个相同项目时,先用WriteListBegin写入VaList标志,写完数据项目后,再写出VaNull标志;而读这些数据时,以ReadListBegin开始,ReadListEnd结束,中间用EndofList函数判断是否有VaNull标志。
下面就介绍它们的实现。
1. TWriter对象属性的实现
TWriter对象直接从TFiler对象继承,它只增加了Position和RootAncestor属性。
RootAncestor属性在private部分有数据域FRootAncestor存入其值。在属性定义的读与控制上都是直接读取该值。
Position属性的定义中包含了两个读写控制方法:GetPosition和SetPosition:
TWriter = class(TFiler)
private
FRootAncestor: TComponent;
…
function GetPosition: Longint;
procedure SetPosition(Value: Longint);
public
…
property Position: Longint read GetPosition writeSetPosition;
property RootAncestor: TComponent read FRootAncestor write FRootAncestor;
end;
GetPosition和SetPosition方法实现如下:
function TWriter.GetPosition: Longint;
begin
Result := FStream.Position + FBufPos;
end;
procedure TWriter.SetPosition(Value: Longint);
var
StreamPosition: Longint;
begin
StreamPosition := FStream.Position;
{ 只清除越界的缓冲区 }
if (Value < StreamPosition) or (Value > StreamPosition + FBufPos) then
begin
WriteBuffer;
FStream.Position := Value;
end
else FBufPos := Value - StreamPosition;
end;
WriteBuffer是TWriter对象定义的私有方法,它的作用是将Writer 对象内部缓冲区中的有效数据写入流中,并将FBufPos置为0。Writer对象的FlushBuffer对象就是用WriteBuffer方法刷新缓冲区。
在SetPosition方法中,如果Value值超出了边界(FStream.Position,FStream.Position + FBufPos),就将缓冲区中的内容写入流,重新设置缓冲区在流中的相对位置;否则,就只是移动FBufPos指针。
2. TWriter方法的实现
⑴ WriteListBegin和WriteListEnd的实现
这两个方法都是用于写连续若干个相同类型的值。WriteListBegin写入VaList标志,WriteListEnd写入VaNull标志。
procedure TWriter.WriteListBegin;
begin
WriteValue(vaList);
end;
procedure TWriter.WriteListEnd;
begin
WriteValue(vaNull);
end;
这两个方法都调用TWriter对象的WriteValue方法,该方法主要用于写入TValueType类型的值。
procedure TWriter.WriteValue(Value: TValueType);
begin
Write(Value, SizeOf(Value));
end;
⑵ 简单数据类型的写入
简单数据类型指的是整型、字符型、字符串型、浮点型、布尔型等。TWriter对象都定义了相应的写入方法。
WriteInteger方法用于写入整型数据。
procedure TWriter.WriteInteger(Value: Longint);
begin
if (Value >= -128) and (Value <= 127) then
begin
WriteValue(vaInt8);
Write(Value, SizeOf(Shortint));
end else
if (Value >= -32768) and (Value <= 32767) then
begin
WriteValue(vaInt16);
Write(Value, SizeOf(Smallint));
end else
begin
WriteValue(vaInt32);
Write(Value, SizeOf(Longint));
end;
end;
WriteInteger方法将整型数据分为8位、16位和32位三种,并分别用vaInt8、vaInt16和VaInt32。
WriteBoolean用于写入布尔型数据:
procedure TWriter.WriteBoolean(Value: Boolean);
begin
if Value then
WriteValue(vaTrue) else
WriteValue(vaFalse);
end;
与其它数据类型不同的是布尔型数据只使用了标志位是存储布尔值,在标志位后没有数据。
WriteFloat方法用于写入浮点型数据。
procedure TWriter.WriteFloat(Value: Extended);
begin
WriteValue(vaExtended);
Write(Value, SizeOf(Extended));
end;
字符串“True”、“False”和“nil”作为标识符传入是由于Delphi的特殊需要。如果是“True”、“False”和“nil”则写入VaTrue、VaFalse和VaNil,否则写入VaIdent标志,接着以字符串形式写入标识符。
WriteString方法用于写入字符串
procedure TWriter.WriteString(const Value: string);
var
L: Integer;
begin
L := Length(Value);
if L <= 255 then
begin
WriteValue(vaString);
Write(L, SizeOf(Byte));
end else
begin
WriteValue(vaLString);
Write(L, SizeOf(Integer));
end;
Write(Pointer(Value)^, L);
end;
Delphi的字符串类型有两种。一种长度小于256个字节,另一种长度小于65536 个字节。WriteString方法区分这两类情况存储字符串,一种设置VaStirng标志,另一种设置VaLString。然后存储字符串的长度值,最后存储字符串数据。
WriteChar方法用于写入字符。
procedure TWriter.WriteChar(Value: Char);
begin
WriteString(Value);
end;
字符类型的读写是用读写字符串的方法,在读的时候,判断字节数为1时,则为字符型。
⑶ 部件的写入
TWriter对象中与写入部件有关的方法有WriteSignature、WritePrefix、WriteComponent、WriteDescendant和WriteRootComponent。
WriteSignature方法用于往流中写入Filer对象标签。
procedure TWriter.WriteSignature;
begin
Write(FilerSignature, SizeOf(FilerSignature));
end;
FilerStgnature是字符串常量,其值为“TPF0”,代表对象标签。
WritePrefix方法用于在写入部件前写入ffInherited和ffChildPos标志,这些标志表示部件的继承特征和创建序值特征。
procedure TWriter.WritePrefix(Flags: TFilerFlags; AChildPos:Integer);
var
Prefix: Byte;
begin
if Flags <> [] then
begin
Prefix := $F0 or Byte(Flags);
Write(Prefix, SizeOf(Prefix));
if ffChildPos in Flags then WriteInteger(AChildPos);
end;
end;
如果ffChildPos置位,则存入部件在Owner中的创建序值。更详细的信息请参阅TReader的ReadPrefix方法。
WriteComponent方法往流中写入部件。
procedure TWriter.WriteComponent(Component: TComponent);
function FindAncestor(const Name: string): TComponent;
begin
…
end;
begin
Include(Component.FComponentState, csWriting);
if Assigned(FAncestorList) then
Ancestor := FindAncestor(Component.Name);
Component.WriteState(Self);
Exclude(Component.FComponentState, csWriting);
end;
方法中用Component的WritState方法写入部件的属性。在写入之前将Component.FComponentState置为csWriting写入完后再将csWriting复位。
WriteDescendant是根据祖先AAncestor的情况写入部件Root。
procedure TWriter.WriteDescendent(Root: TComponent;AAncestor: TComponent);
begin
FRootAncestor := AAncestor;
FAncestor := AAncestor;
FRoot := Root;
WriteSignature;
WriteComponent(Root);
end;
方法先调用WriteSignature方法写入Filer对象标签。然后调用WriteComponent将部件Root写入流。
WriteRootComponent方法则是调用WriteDescendant方法写入部件,只是将后者的Ancestor参数以nil值传入。
procedure TWriter.WriteRootComponent(Root: TComponent);
begin
WriteDescendent(Root, nil);
end;