後一頁 前一頁 回目錄 回首頁 |
20.2.1.1 TFiler 物件的屬性和方法1. Root屬性 聲明: property Root: TComponent;Root 屬性給Filer物件指出被讀寫的物件中哪一個物件是根或主要擁有者。RootComponent和WriteRootComponent方法在讀和寫部件及其擁有的部件前先設定Root的值。2. Ancestor屬性 聲明: property Ancestor: TPersistent;Ancestor 屬性用於往繼承下來的窗體中寫部件,因為當寫部件時,Write物件只需要寫入與所繼承的部件不同的屬性,所以在寫之前要跟蹤每個繼承的部件,並且比較它們的屬性。如果 Ancestor為nil,就表示沒有相應的繼承的部件,Writer物件應當將部件完全寫入流。Ancestor一般為nil,只有當呼叫WriteDescendant和WriteDescendantRes時,才給賦值。當編寫和覆蓋DefineProperties時,必須設定Ancestor的值。3. IgnoreChildren屬性 聲明: property Ignorechildren: Boolean;IgnoreChildren 屬性使一個Writer物件存儲部件時可以不存儲該部件擁有的部件。如果IgnoreChildren屬性為True,則Writer物件存儲部件不存它擁有的子部件。否則,Writer物件將所有其擁有的物件寫入流。4. Create方法 聲明: constructor Create(Stream: TStream; BufSize: Cardinal); Create方法建立一個新的Filer物件,建立它和流Stream的聯繫;並且給它分配一個緩衝區Buffer。Buffer的大小由BufSize指定。5. Defineproperty方法 聲明: procedure Defineproperty(const Name: String; ReadData: TReaderProc;WriteData: TWriterProc; HasData: Boolean); virtual; abstract; Defineproperty 方法定義Filer物件將作為屬性存儲的數據。Name參數描述接受的屬性名,該屬性不在published部分定義。ReadData和WriteData參數指定在存取物件時讀和寫所需數據的方法。HasData參數在執行時決定了屬性是否有數據要存儲。只有當物件有數據要存儲時,才在該物件的 DefineProperties中呼叫DefineProperty。DefineProperties有一個Filer物件作為它的參數,呼叫的就是該Filer物件的DefineProperty和DefineBinaryProperty方法。當定義屬性時,Writer物件應當引用Ancestor屬性,如果該屬性非空,Writer物件應當只寫入與從Ancestor繼承的不同的屬性的值。一個最簡單的例子是 TComponent的DefineProperties方法。儘管TComponent 沒有在published中定義Left、Top屬性,但該方法存儲了部件的位置資訊。procedure TComponent.DefineProperties(Filer: TFiler); begin Filer.DefineProperty('Left', ReadLeft, WriteLeft, LongRec(FDesignInfo).Lo <> 0); Filer.DefineProperty('Top', ReadTop, WriteTop, LongRec(FDesignInfo).Hi <> 0); end; 6. DefineBinaryproperty 方法聲明: procedure DefineBinaryproperty(const Name: String;ReadData, WriteData: TStreamProc; HisData: Boolean); virtual; abstract; DefineBinaryProperty 方法定義Filer物件作為屬性存儲的二進制數據。Name參數描述屬性名。ReadData和WriteData參數描述所存儲的物件中讀寫所需數據的方法。HasData參數在執行時決定屬性是否有數據要存。DefineBinaryProperty和DefineProperty方法的不同之處在於,二進制型的屬性直接用Stream物件讀寫,而不是通過Filer物件。通過ReadData和WriteData傳入的方法,直接將物件數據寫入流或從流讀出。 DefineBinaryProperty屬性用得較少。只有標準的VCL物件定義了象圖形、圖像之類的二進制屬性的部件中才用它。 7. FlushBuffer方法 聲明: procedure FlushBuffer; virtual: abstract;FlushBuffer 方法用於使Filer物件的緩衝區與相聯的Stream物件同步。對Reader物件來說,是通過重新分配緩衝區;對於Writer物件是通過寫入目前緩衝區。FlushBuffer是一個抽象方法,TReader和TWriter都覆蓋了它,提供了具體實現。
20.2.1.2 TFiler 物件的實現原理
TFiler物件是Filer物件的基礎類,它定義的大多數方法都是抽象型式的,沒有具體實現它,這些方法要在TReader和TWrite中覆蓋。但它們提供了Filer物件的框架,了解它無疑是很重要的。 1. TFiler物件屬性的實現 TFiler物件定義了三個屬性:Root、Ancestor和IgnoreChildren。正如定義物件屬性通常所採用的方法那樣,要在private部分定義存儲屬性值的數據欄位,然後在public或Published部分定義該屬性,並按需要增加讀寫控制。它們的定義如下: TFiler = class(TObject) private …FRoot: TComponent; FAncestor: TPersistent; FIgnoreChildren: Boolean; public … property Root: TComponent read FRoot write FRoot;property Ancestor: TPersistent read FAncestor write FAncestor; property IgnoreChildren: Boolean read FIgnoreChildren write FIgnoreChildren; end; 它們在讀寫控制上都是直接讀寫私有的數據欄位。 在介紹 TReader和TWriter的實現,我們還會看到這幾個屬性的原理介紹。2. TFiler物件方法的實現 在 TFiler物件定義的眾多方法中很多都是抽象類方法,沒有具體實現。在TFiler 的後繼物件TReader中覆蓋了這些方法。在後面章節,會介紹這些方法的實現。在 TFiler物件中有具體實現的有兩個方法Create和Destroy。ぇ Create方法的實現Create方法是TFiler的構造方法,它有兩個參數Stream和BufSize。Stream是指定與TFiler物件相聯繫的Stream物件,Filer物件都是用Stream物件完成具體的讀寫。BufSize是TFiler物件內部開設的緩衝區的大小。Filer物件內部開設緩衝區是為了加快數據的讀寫,它的實現如下: constructor TFiler.Create(Stream: TStream; BufSize: Integer); begin FStream := Stream; GetMem(FBuffer, BufSize); FBufSize := BufSize; end; FStream、FBuffer和FBufSize都是TFiler在private部分定義的數據欄位。FStream表示與Filer物件相聯的Stream物件,FBuffer指向Filer物件內部開設的緩衝區,FBufSize是內部緩衝區的大小。Create方法用Stream參數值給FStream賦值,然後用GetMem分配BufSize大小的動態記憶體作為內部緩衝區。 え Destroy方法的實現Destroy方法是TFiler物件的析構函數,它的作用就是釋放動態記憶體。 destructor TFiler.Destroy; begin if FBuffer <> nil then FreeMem(FBuffer, FBufSize); end; 20.2.2 TWriter 物件
TWriter 物件是可實例化的,往流中寫數據的Filer物件。TWriter物件直接從TFiler繼承而來,除了覆蓋從TFiler繼承的方法外,還增加了大量的關於寫各種數據型式(如Integer、String和Component等)的方法。TWriter物件和TReader 物件配合使用將使物件讀寫發揮巨大作用。
20.2.2.1 TWriter 物件的屬性和方法
1. Position屬性 聲明: property Position: Longint;TWriter 物件的Position屬性表示相關聯的流中的目前要寫的位置,TReader 物件也有這個屬性,但與TReader物件不同的是TWriter物件的Position的值比流的Position值小,這一點一看屬性實現就清楚了。2. RootAncesstor屬性 聲明: property RootAncestor: TComponent;RootAncestor 屬性表示的是Root屬性所指的部件的祖先。如果Root 是繼承的窗體,Writer物件將窗體擁有部件與祖先窗體中的相應部件依次比較,然後只寫入那些與祖先中的不同的部件。3. Write方法 聲明: procedure Write(const Buf; Count: Longint);Write 方法從Buf中往與Writer相關聯的流中寫入Count個位元群組。4. WriteListBegin方法 聲明: procedure WriteListBegin;WriteListBegin 方法往Write物件的流中寫入項目清單開始標誌,該標誌意味著後面存儲有一連串的項目。Reader物件,在讀這一連串項目時先呼叫ReadListBegin方法讀取該標誌位,然後用EndOfList判斷是否清單結束,並用回圈語句讀取項目。在呼叫WriteListBegin方法的後面必須呼叫WriteListEnd方法寫清單結束標誌,相應的在Reader物件中有ReadListEnd方法讀取該結束標誌。5. WriteListEnd方法 聲明: procedure WriteListEnd;WriteListEnd 方法在流中,寫入項目清單結束標誌,它是與WriteListBegin相匹配的方法。6. WriteBoolean方法 聲明: procedure WriteBoolean(Value: Boolean);WriteBoolean 方法將Value傳入的布爾值寫入流中。7. WriteChar方法 聲明: procedure WriteChar(Value: char);WriteChar 方法將Value中的字元寫入流中。8. WriteFloat方法 聲明: procedure WriteFloat(Value: Extended);WriteFloat 方法將Value傳入的浮點數寫入流中。9. WriteInteger方法 聲明: procedure WriteInteger(Value: Longint);WriteInteger 方法將Value中的整數寫入流中。10. WriteString方法 聲明: procedure WriteString(const Value: string);WriteString 方法將Value中的字元串寫入流中。11. WriteIdent方法 聲明: procedure WriteIdent(const Ident: string);WriteIdent 方法將Ident傳入的標識符寫入流中。12. WriteSignature方法 聲明: procedure WriteSignature;WriteSignature 方法將Delphi Filer物件標籤寫入流中。WriteRootComponent方法在將部件寫入流之前先呼叫WriteSignature方法寫入Filer標籤。Reader物件在讀部件之前呼叫ReadSignature方法讀取該標籤以指導讀操作。13. WritComponent方法 聲明: procedure WriteComponent(Component: TComponent);WriteComponent 方法呼叫參數Component的WriteState方法將部件寫入流中。在呼叫WriteState之前,WriteComponent還將Component的ComponetnState屬性置為csWriting。當WriteState返回時再清除csWriting.14. WriteRootComponent 方法聲明: procedure WriteRootComponent(Root: TComponent);WriteRootComponent 方法將Writer物件Root屬性設為參數Root帶的值,然後呼叫WriteSignature方法往流中寫入Filer物件標籤,最後呼叫WriteComponent方法在流中存儲Root部件。
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 write SetPosition;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 elsebegin 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; |
後一頁 前一頁 回目錄 回首頁 |