它们实现中调用的CheckValue是TReader的私有方法其实现如下
procedure TReaderCheckValue(Value: TValueType)
begin
if ReadValue <> Value then
begin
Dec(FBufPos)
SkipValue;
PropValueError;
end;
end;
CheckValue方法的功能是检测紧接着要读的值是否是Value指定的类型如果不是则跳过该项目并触发一个SInvalidPropertyValue错误
EndOfList函数只是简单地判断下一字节是否是VaNull将判断结果返回并将字节移回原来位置
简单数据类型读方法的实现
简单数据类型指的是布尔型字符型整型字符串型浮点型集合类型和标识符将它们放在一起介绍是因为它们的实现方法类似
因为它们的实现都用到了ReadValue方法因此先来介绍ReadValue方法的实现
function TReaderReadValue: TValueType;
begin
Read(Result SizeOf(Result))
end;
该方法调用私有方法Read从Reader对象流中读一个字节并移动位置指针
ReadValue方法专门从流中读取值的类型的所有的数据读写方法中在读取数据前都要调用ReadValue方法判断是否是所要读的数据如果是则调用Read方法读取数据否则触发一个异常事件下面看Integer类型的读方法
function TReaderReadInteger: Longint;
var
S: Shortint;
I: Smallint;
begin
case ReadValue of
vaInt:
begin
Read(S SizeOf(Shortint))
Result := S;
end;
vaInt:
begin
Read(I SizeOf(I))
Result := I;
end;
vaInt:
Read(Result SizeOf(Result))
else
PropValueError;
end;
end;
因为Delphi 中整型可分位位和位因此读取整型数据时分别作了判断
布尔类型的数据是直接放在值类型标志上如果类型为VaTrue则值为True;如果类型为VaFalse则值为False
function TReaderReadBoolean: Boolean;
begin
Result := ReadValue = vaTrue;
end;
ReadString方法也利用ReadValue方法判断是字符串还是长字符串
function TReaderReadString: string;
var
L: Integer;
begin
L := ;
case ReadValue of
vaString:
Read(L SizeOf(Byte))
vaLString:
Read(L SizeOf(Integer))
else
PropValueError;
end;
SetString(Result PChar(nil) L)
Read(Pointer(Result)^ L)
end;
如果VaString类型紧接着一个字节存有字符串的长度如果是VaLString类则紧接着两个字节存放字符串长度然后根据字符串长度用SetString过程给分配空间用Read方法读出数据
ReadFloat方法允许将整型值转换为浮点型
function TReaderReadFloat: Extended;
begin
if ReadValue = vaExtended then Read(Result SizeOf(Result)) else
begin
Dec(FBufPos)
Result := ReadInteger;
end;
end;
字符类型数据设有直接的标志它是根据VaString后面放一个序值为的字节来判断的
function TReaderReadChar: Char;
begin
CheckValue(vaString)
Read(Result )
if Ord(Result) <> then
begin
Dec(FBufPos)
ReadStr;
PropValueError;
end;
Read(Result )
end;
出于读取DFM文件需要Filer对象支持读取标识符
function TReaderReadIdent: string;
var
L: Byte;
begin
case ReadValue of
vaIdent:
begin
Read(L SizeOf(Byte))
SetString(Result PChar(nil) L)
Read(Result[] L)
end;
vaFalse:
Result := False;
vaTrue:
Result := True;
vaNil:
Result := nil;
else
PropValueError;
end;
end;
一般说来各种复杂的数据结构都是由这些简单数据组成定义了这些方法等于给读各种类型的数据提供了元操作使用很方便例如读取字符串类型的数据时如果采用传流方法还要判断字符串的长度使用ReadString方法就不同了但应该特别注意的是这些类型数据的存储格式是由Delphi设计的与简单数据类型有明显的不同因此存入数据时应当使用Writer对象相应的方法而且在读数据前要用NextValue方法进行判断否则会触发异常事件
读取部件的方法的实现
Reader对象中用于读取部件的方法有ReadSignatureReadPrefixReadComponentReadRootComponent和ReadComponents
ReadSignature方法主要用于读取Delphi Filer对象标签一般在读取部件前都要用调用ReadSignature方法以指导部件读写过程
procedure TReaderReadSignature;
var
Signature: Longint;
begin
Read(Signature SizeOf(Signature))
if Signature <> Longint(FilerSignature) then ReadError(SInvalidImage)
end;
FilerSignature就是Filer对象标签其值为TPF 如果读的不是TPF 则会触发SInValidImage异常事件
ReadPrefix方法是用于读取流中部件前的标志位该标志表示该部件是否处于从祖先窗体中继承的窗体中和它在窗体中的位置是否很重要
procedure TReaderReadPrefix(var Flags: TFilerFlags; var AChildPos: Integer)
var
Prefix: Byte;
begin
Flags := [];
if Byte(NextValue) and $F = $F then
begin
Prefix := Byte(ReadValue)
Byte(Flags) := Prefix and $F;
if ffChildPos in Flags then AChildPos := ReadInteger;
end;
end;
TFilerFlags的定义是这样的
TFilerFlag = (ffInherited ffChildPos)
TFilerFlags = Set of TFilerFlag;
充当标志的字节的高四位是$F低四位是集合的值也是标志位的真正含义如果ffChildPos置位则紧接着的整型数字中放着部件在窗体中的位置序值
ReadComponent方法用于从Reader对象的流中读取部件Component 参数指定了要从流中读取的对象函数返回所读的部件
function TReaderReadComponent(Component: TComponent) TComponent;
var
CompClass CompName: string;
Flags: TFilerFlags;
Position: Integer;
…
begin
ReadPrefix(Flags Position)
CompClass := ReadStr;
CompName := ReadStr;
Result := Component;
if Result = nil then
if ffInherited in Flags then
FindExistingComponent else
CreateComponent;
if Result <> nil then
try
Include(ResultFComponentState csLoading)
if not (ffInherited in Flags) then SetCompName;
if Result = nil then Exit;
Include(ResultFComponentState csReading)
ResultReadState(Self)
Exclude(ResultFComponentState csReading)
if ffChildPos in Flags then ParentSetChildOrder(Result Position)
FLoadedAdd(Result)
except
if ComponentCreated then ResultFree;
raise;
end;
end;
ReadCompontent方法首先调用ReadPrefix方法读出部件标志位和它的创建次序值(Create Order)然后用ReadStr方法分别读出部件类名和部件名如果Component参数为nil则执行两个任务
● 如果ffInberited 置位则从Root 找已有部件否则就从系统的Class表中找到该部件类型的定义并创建
● 如果结果不为空将用部件的ReadState方法读入各种属性值并设置部件的Parent 属性并恢复它在Parent部件的创建次序
[] [] [] []