Read方法使用了BDE API的DbiGetBlob函數從FDataSet中讀取數據,在本函數中,各參數的含義是這樣的:FDataSet.Handle代表DataSet的BDE句柄,F(xiàn)Reacord表示BLOB字段所在記錄,F(xiàn)FieldNo表示BLOB字段號,F(xiàn)Position表示要讀的的數據的起始位置,Count表示要讀的字節(jié)數,Buffer是讀出數據所占的內存,Result是實際讀出的字節(jié)數。該BDE函數返回函數調用的錯誤狀態(tài)信息。
Read方法還調用了NativeToAnsiBuf進行字符集的轉換。
function TBlobStream.Write(const Buffer; Count: Longint): Longint;
var
Temp: Pointer;
begin
Result := 0;
if FOpened then
begin
if FField.FTransliterate then
begin
GetMem(Temp, Count);
try
AnsiToNativeBuf(FDataSet.Locale, @Buffer, Temp, Count);
Check(DbiPutBlob(FDataSet.Handle, FRecord, FFieldNo, FPosition,
Count, Temp));
finally
FreeMem(Temp, Count);
end;
end else
Check(DbiPutBlob(FDataSet.Handle, FRecord, FFieldNo, FPosition,
Count, @Buffer));
Inc(FPosition, Count);
Result := Count;
FModified := True;
end;
end;
Write方法調用了BDE API的DbiPutBlob函數實現(xiàn)往數據庫BLOB字段存儲數據。
該函數的各參數含義如下:
表20.2 調用函數DbiPutBlob的各傳入參數的含義
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
參數名 含義
──────────────────────────────
FDataSetHandle 寫入的數據庫的BDE句柄
FRecord 寫入數據的BLOB字段所在的記錄
FFieldNo BLOB字段號
FPosition 寫入的起始位置
Count 寫入的數據的字節(jié)數
Buffer 所寫入的數據占有的內存地址
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
方法中還根據FField和FTransliterate的值判斷是否進行相應的字符集轉換,最后移動BLOB流的位置指針,并將修改標志FModified置為True。
3. Seek和GetBlobSize方法的實現(xiàn)
Seek方法的功能主要是移動BLOB流的位置指針。GetBlobSize方法是私有的,在Seek方法中被調用,其功能是得到BLOB數據的大小。它們的實現(xiàn)如下:
function TBlobStream.GetBlobSize: Longint;
begin
Result := 0;
if FOpened then
Check(DbiGetBlobSize(FDataSet.Handle, FRecord, FFieldNo, Result));
end;
function TBlobStream.Seek(Offset: Longint; Origin: Word): Longint;
begin
case Origin of
0: FPosition := Offset;
1: Inc(FPosition, Offset);
2: FPosition := GetBlobSize + Offset;
end;
Result := FPosition;
end;
GetBlobSize調用了BDE API的DbiGetBlobSize函數,該函數的參數的含義同前面的API函數相同。
4. Truncate方法
該方法是通過調用BDE API函數實現(xiàn)的。其實現(xiàn)如下:
procedure TBlobStream.Truncate;
begin
if FOpened then
begin
Check(DbiTruncateBlob(FDataSet.Handle, FRecord, FFieldNo, FPosition));
FModified := True;
end;
end;
該方法從BLOB流的當前位置起刪除所有數據,并設置修改標志FModified為True。在Delphi VCL中許多部件特別是數據庫應用方面的部件都用BDE API函數完成對數據庫的訪問,如Data Access和Data Control部件。各種數據庫部件都是BDE API函數外層的包裝簡化了對數據庫的訪問操作。BDE API中還提供了避開BDE配置工具在程序中直接處理Alias(建立、修改、刪除等)的函數支持,這也是部件所沒有提供的。在Delphi數據庫應用安裝程序中,這些Alias操作函數無疑是相當重要的。有關BDE API函數的詳細介紹,可閱讀Delphi2.0 Client/Server Suite所帶的BDE API 幫助文件。
20.2 讀寫對象的實現(xiàn)原理和應用
讀寫對象(Filer)包括TFiler對象、TReader對象和TWriter對象。TFiler對象是文件讀寫的基礎對象,在應用程序中使用的主要是TReader和TWriter。TReader和TWriter對象都直接從TFiler對象繼承。TFiler對象定義了Filer對象的基本屬性和方法。
Filer對象主要完成兩大功能:
● 存取窗體文件和窗體文件中的部件
● 提供數據緩沖,加快數據讀寫操作
20.2.1 TFiler對象
TFiler對象是TReader和TWriter的抽象類,定義了用于部件存儲的基本屬性和方法。它定義了Root屬性,Root指明了所讀或寫的部件的根對象,它的Create方法將Stream對象作為傳入參數以建立與Stream對象的聯(lián)系, Filer對象的具體讀寫操作都是由Stream對象完成。因此,只要是Stream對象所能訪問的媒介都能由Filer對象存取部件。TFiler 對象還提供了兩個定義屬性的方法:DefineProperty和DefineBinaryProperty,這兩個方法使對象能讀寫不在部件published部分定義的屬性。
因為Filer對象主要用于存取Delphi的窗體文件和窗體文件中的部件,所以要清楚地理解Filer對象就要清楚Delphi 窗體文件(DFM文件)的結構。
DFM文件是用于Delphi存儲窗體的。窗體是Delphi可視化程序設計的核心。窗體對應Delphi應用程序中的窗口,窗體中的可視部件對應窗口中的界面元素,非可視部件如TTable和TOpenDialog,對應Delphi應用程序的某項功能。Delphi應用程序的設計實際上是以窗體的設計為中心。因此,DFM文件在Delphi應用設計中也占很重要的位置。窗體中的所有元素包括窗體自身的屬性都包含在DFM文件中。
在Delphi應用程序窗口,界面元素是按擁有關系相互聯(lián)系的,因此樹狀結構是最自然的表達形式;相應地,窗體中的部件也是按樹狀結構組織;對應在DFM文件中,也要表達這種關系。DFM文件在物理上,是以二進制方式存儲的,在邏輯上則是以樹狀結構安排各部件的關系。Delphi編輯窗口支持以文本方式顯示DFM文件。從該文本中可以看清窗體的樹狀結構。下面是DFM文件的文本顯示:
Object Form1: TForm1
Left = 72
Top = 77
ActiveControl = DBIMage1
…
Object Panell: TPanel
Left = 6
…
Object DBLabel1: TDBText
…
end
Object DBImage1: TDBImage
…
end
end
Object Panel2: TPanel
Left = 6
…
Object Label1: TLable
…
end
end
Object Panel3: TPanel
Left = 6
…
Object DBLabel2: TDBText
…
end
end
end
關于DFM文件中存儲屬性值的規(guī)則,請參見自定義部件開發(fā)這一章。
對照TFiler對象的屬性。Root屬性就表示部件樹的根──窗體。Filer對象的許多方法都是讀從根起始的樹中所有的部件。Ancestor屬性表示根的祖先對象,IgnoreChildren屬性則是讀部件時忽略根的子結點。
下面介紹Filer對象的屬性和方法。
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方法創(chuàng)建一個新的Filer對象,建立它和流Stream的聯(lián)系;并且給它分配一個緩沖區(qū)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對象的緩沖區(qū)與相聯(lián)的Stream對象同步。對Reader對象來說,是通過重新分配緩沖區(qū);對于Writer對象是通過寫入當前緩沖區(qū)。
FlushBuffer是一個抽象方法,TReader和TWriter都覆蓋了它,提供了具體實現(xiàn)。
20.2.1.2 TFiler對象的實現(xiàn)原理
TFiler對象是Filer對象的基礎類,它定義的大多數方法都是抽象類型的,沒有具體實現(xiàn)它,這些方法要在TReader和TWrite中覆蓋。但它們提供了Filer對象的框架,了解它無疑是很重要的。
1. TFiler對象屬性的實現(xiàn)
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的實現(xiàn),我們還會看到這幾個屬性的原理介紹。
2. TFiler對象方法的實現(xiàn)
在TFiler對象定義的眾多方法中很多都是抽象類方法,沒有具體實現(xiàn)。在TFiler 的后繼對象TReader中覆蓋了這些方法。在后面章節(jié),會介紹這些方法的實現(xiàn)。
在TFiler對象中有具體實現(xiàn)的有兩個方法Create和Destroy。
、 Create方法的實現(xiàn)
Create方法是TFiler的構造方法,它有兩個參數Stream和BufSize。Stream是指定與TFiler對象相聯(lián)系的Stream對象,F(xiàn)iler對象都是用Stream對象完成具體的讀寫。BufSize是TFiler對象內部開設的緩沖區(qū)的大小。Filer對象內部開設緩沖區(qū)是為了加快數據的讀寫,它的實現(xiàn)如下:
constructor TFiler.Create(Stream: TStream; BufSize: Integer);
begin
FStream := Stream;
GetMem(FBuffer, BufSize);
FBufSize := BufSize;
end;
FStream、FBuffer和FBufSize都是TFiler在private部分定義的數據域。FStream表示與Filer對象相聯(lián)的Stream對象,F(xiàn)Buffer指向Filer對象內部開設的緩沖區(qū),F(xiàn)BufSize是內部緩沖區(qū)的大小。Create方法用Stream參數值給FStream賦值,然后用GetMem分配BufSize大小的動態(tài)內存作為內部緩沖區(qū)。
⑵ Destroy方法的實現(xiàn)
Destroy方法是TFiler對象的析構函數,它的作用就是釋放動態(tài)內存。
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 對象配合使用將使對象讀寫發(fā)揮巨大作用。
20.2.2.1 TWriter對象的屬性和方法
1. Position屬性
聲明:property Position: Longint;
TWriter對象的Position屬性表示相關聯(lián)的流中的當前要寫的位置,TReader 對象也有這個屬性,但與TReader對象不同的是TWriter對象的Position的值比流的Position值小,這一點一看屬性實現(xiàn)就清楚了。
2. RootAncesstor屬性
聲明:property RootAncestor: TComponent;
RootAncestor屬性表示的是Root屬性所指的部件的祖先。如果Root 是繼承的窗體,Writer對象將窗體擁有部件與祖先窗體中的相應部件依次比較,然后只寫入那些與祖先中的不同的部件。
3. Write方法
聲明:procedure Write(const Buf; Count: Longint);
Write方法從Buf中往與Writer相關聯(lián)的流中寫入Count個字節(jié)。
4. WriteListBegin方法
聲明:procedure WriteListBegin;
WriteListBegin方法往Write對象的流中寫入項目列表開始標志,該標志意味著后面存儲有一連串的項目。Reader對象,在讀這一連串項目時先調用ReadListBegin方法讀取該標志位,然后用EndOfList判斷是否列表結束,并用循環(huán)語句讀取項目。在調用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傳入的標識符寫入流中。
相關推薦:2010年9月計算機等級考試試題及答案解析專題北京 | 天津 | 上海 | 江蘇 | 山東 |
安徽 | 浙江 | 江西 | 福建 | 深圳 |
廣東 | 河北 | 湖南 | 廣西 | 河南 |
海南 | 湖北 | 四川 | 重慶 | 云南 |
貴州 | 西藏 | 新疆 | 陜西 | 山西 |
寧夏 | 甘肅 | 青海 | 遼寧 | 吉林 |
黑龍江 | 內蒙古 |