|
20.2.2.2 TWriter对象的实现 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;
|