Delphi控制元件的拿來主義(二) (轉)
這個例子是關於如何將一個文字資料匯入匯出作一些簡單的修改之後拿來使用的。:namespace prefix = o ns = "urn:schemas--com::office" />
◆功能
將圖書館的ISO中的部分資料轉到8中。
◆設計要求
1.顯示匯入進度條。
2.在匯入過程中,如果某條紀錄匯入失敗,不顯示異常,而將匯入失敗的紀錄記入日誌。
◆設計思路
程式的關鍵在文字資料欄位的分離。通常的做法,都是先將字串進行處理(RegulateString),然後把串中每個字元同分割符(可以是空格,逗號等)比較,將不是分割符的字元追加到一個串中(GetRecordItem),得到一個欄位的內容。透過一個迴圈(迴圈次數由GetItemNum來定),就可以將一個字串分成幾個欄位。最後的工作就是將分離出來的資料對號入座加入資料庫.
按照上面的思路,利用提供的已有和過程,實現起來應該不難,但問題是,我可不想每次編文字匯入程式的時候,都把什麼這啊那的函式過程重新定義一遍,哎,最煩的就是重複性的工作了.那麼有沒有現成的控制元件將上述過程都封裝起來呢?PS:又不用我編呢?
答案是肯定的!前幾天剛剛下了一個免費控制元件TPgCSV,據說可以實現文字的匯入和匯出.翻出來一看,正是我想要的.
在深入到下面的內容之前,有必要對該控制元件的類宣告部分作一定了解
(經作者Khashayar Sadjadi(khashi@pragena.8m.com)同意發表):
//中文部分為筆者所作的註釋
//注意:
//在該控制元件中,Export代表將文字資料匯入到資料庫,Import代表從資料庫匯出到文字。??? 怎麼和我理解的
//匯入匯出概念剛好是反的 :)
type
//在處理資料產生異常時,可選擇繼續還是中止
TPgCSVErrorResponse = (pgcsvAbort, pgcsvIgnore);
//程式事件宣告,可以將匯入/匯出的進度作為引數傳出
TPgCSVProgressEvent = procedure (Sender : T; AProgress: LongInt; var StopIt: Boolean) of object;
//發生異常時的事件處理宣告,異常資訊透過該介面傳給程式設計師。
TPgCSVExportErrorEvent = procedure (Sender : TObject; Mess: string; RecNo: LongInt; var Response:TPgCSVErrorResponse) of object;
TPgCSV = class(TComponent)
private
FDataset : TDataset;
FCSVMap,
FCSVFile,
FDateFormat,
FIgnoreStr : string;
FSeprator,
FDelimiter,
FFieldIndicator : Char;
FAuten,
FUseDelimiter,
FSilentExport,
FTrimData,
FStop,
FEmptyTable : Boolean;
FBeforeOpenTable,
FAfterOpenTable,
FBeforeCloseTable,
FAfterCloseTable,
FBeforeEmptyTable,
FAfterEmptyTable,
FBeforeExport,
FAfterExport,
FBeforeImport,
FAfterImport,
FOnAddRecord : TNotifyEvent;
FExportProgress,
FImportProgress : TPgCSVProgressEvent;
FExportError : TPgCSVExportErrorEvent;
FMtems,
FDefaultInt : Integer;
FBufferSize : LongInt;
FFieldCache : TList;
protected
FFile : TextFile;
//以下就是我所說的希望封裝的部分
function CountMapItems:Integer;//計算對映字串的欄位個數
function GetMapItem(ItemIndex:Integer;var AField:Boolean):string;//提取對映字串的欄位
function GetCSVRecordItem(ItemIndex:Integer;CSVRecord:string):string;//提取CSV檔案字串中的某一欄位
function BuildMap:string;//自動建立對映,如果CSVMap一欄為空的話,會由它來產生對映字串
function Extract(Item: Integer;S, WordDelim: string): string;//提取文字資料字串/對映字串中的某一欄位
function WordCount(const S ,WordDelim: string): Integer;//計算文字資料字串/對映字串中的欄位數目
function WordPosition(Item: Integer; const S, SubStr: string): Integer;//計運算元字串在字串中的位置
public
constructor Create(AOwner: TComponent); overr;
published
//properties
property Dataset : TDataset read FDataset write FDataset;
//設定要匯入或匯出的目標資料集.
property CSVMap : string read FCSVMap write FCSVMap;
//CSV 文字資料檔案到資料庫欄位值的對映字串.控制元件透過該對映決定文字中的哪些資料要匯入及要匯入哪個欄位.
property CSVFile : string read FCSVFile write FCSVFile;
//CSV 檔案格式,其實就是文字資料檔案。CSV代表什麼意思?呵呵,我也不知道
property Seprator : Char read FSeprator write FSeprator;
//分隔符,可以是空格,也可以是,、;、#等符號
property FieldIndicator : Char read FFieldIndicator write FFieldIndicator;
//欄位識別符號.
property AutoOpen : Boolean read FAutoOpen write FAutoOpen;
//將AutoOpen設為True可以在處理資料前自動開啟要匯入的資料表並在操作完畢後自動關掉它。
property IgnoreString : string read FIgnoreStr write FIgnoreStr;
//忽略紀錄的標識串.
//舉例來說
//IgnoreString:='(ignore)';
//CSVMap:='$Name,(ignore),$Age';
//在這種情況下,CSVToDataSet方法,即匯入資料方法將忽略文字檔案中的第二列的欄位。
property Delimiter : Char read FDelimiter write FDelimiter;
//在某些CSV文件中標識字串紀錄的識別符號,比如,"john","boy",12中的",在這種情況下,TPgCSV
//會忽略這些識別符號。
property EmptyTable : Boolean read FEmptyTable write FEmptyTable;
//只在從資料庫匯出(DataSetToCSV)方法中有效,作用是建立一個新的CSV檔案。
property UseDelimiter : Boolean read FUseDelimiter write FUseDelimiter;
//是否有Delimiter。
property SilentExport : Boolean read FSilentExport write FSilentExport;
//若該屬性為True,應用程式將不顯示資料操作時的異常,而將異常資訊透過一個介面傳給程式設計師處理.
property DateFormat : string read FDateFormat write FDateFormat;
//設定CSV檔案中日期資料的格式。
property TrimData : Boolean read FTrimData write FTrimData;
//是否去掉資料頭尾的空格.
property DefaultInt : Integer read FDefaultInt write FDefaultInt;
//整/實形資料轉換出錯後的預設值
property BufferSize : LongInt read FBufferSize write FBufferSize;
//CSV 檔案的緩衝值,以位元組為單位,可以加快匯入和匯出資料的速度。
//events
property BeforeOpenTable : TNotifyEvent read FBeforeOpenTable write FBeforeOpenTable;
property AfterOpenTable : TNotifyEvent read FAfterOpenTable write FAfterOpenTable;
property BeforeCloseTable : TNotifyEvent read FBeforeCloseTable write FBeforeCloseTable;
property AfterCloseTable : TNotifyEvent read FAfterCloseTable write FAfterCloseTable;
property BeforeEmptyTable : TNotifyEvent read FBeforeEmptyTable write FBeforeEmptyTable;
property AfterEmptyTable : TNotifyEvent read FAfterEmptyTable write FAfterEmptyTable;
property BeforeImport : TNotifyEvent read FBeforeImport write FBeforeImport;
property AfterImport : TNotifyEvent read FAfterImport write FAfterImport;
property BeforeExport : TNotifyEvent read FBeforeExport write FBeforeExport;
property AfterExport : TNotifyEvent read FAfterExport write FAfterExport;
property ExportProgress : TPgCSVProgressEvent read FExportProgress write FExportProgress;
//程式監控事件。每完成一條文字資料的匯入就觸發該事件。
property ImportProgress : TPgCSVProgressEvent read FImportProgress write FImportProgress;
property OnAddRecord : TNotifyEvent read FOnAddRecord write FOnAddRecord;
property ExportError : TPgCSVExportErrorEvent read FExportError write FExportError;
//發生異常時交由該事件處理,異常資訊透過該介面傳給程式設計師。
//methodes
//整個控制元件的核心內容
procedure CSVToDataset;//將文字匯入到資料集的方法
procedure DatasetToCSV;//將資料集的資料匯入到文字的方法
end;
procedure Register;
{略}
implementation
{略}
end.
從宣告部分中我們可以看到,TPgCSV將文字資料的匯入匯出全部封裝到了CSVToDataSet(文字資料匯入)和DataSetToCSV(文字資料匯出)兩個方法中.開發者可以在設計階段直接將文字檔案同要匯入/出的資料庫相連,然後在程式執行當中這兩個方法就可以了,根本不用理會那些函式什麼的,相當的方便.
那我是不是可以直接拿來使用呢?這裡就出現問題了.
----問題一及解決方案
在該控制元件自帶的Demo中,所使用的文字資料檔案,格式如下
test.csv
"11","12","13","14"
"21","22","23","24"
"31","32","33","34"
...
而ISO檔案中的資料格式(部分)為
sm01632.ISO
... a7507310175b特精裝d¥1893"‑1 a毛澤東珍品典藏(上、下冊)f中共中央文獻研究室編著‑c中獻d011200"...
...a7119029193b平裝 d¥20"‑1 a中國:加入WTO與經濟改革f王 夢 奎編著‑ c外文d011200"‑ ...
我們看到,同test.csv相比,sm01632.ISO檔案中的紀錄很不規則。
我們所需要的是這樣的紀錄
sm01632.ISO
... 7507310175,特精裝,1893,1,毛澤東珍品典藏(上、下冊),中共中央文獻研究室編著,中獻,011200...
...7119029193,平裝,20,1,中國:加入WTO與經濟改革,王 夢 奎編著,外文,011200‑ ...
TPgCSV能否對這種情況進行自動的處理呢?哇噻,這種萬能的控制元件好像不大可能有吧!(事實上也不需要)既然直接用TPgCSV處理無法實現正確匯入的,我們就需要在每一條從檔案中讀取出來的紀錄匯入資料庫之前,用程式對它們進行處理,轉換之後再交由TPgCSV進行操作。怎麼樣處理這裡就不贅述了。我們所關心的是TPgCSV有沒有開放出這樣一個處理的介面呢?
我們來看DataSetToCSV方法的實現程式碼(主要是註釋部分):
procedure TPgCSV.CSVToDataSet;
var
RecordString,
Temp : string;
i : Integer;
C : LongInt;
D : Boolean;
F : Real;
ErrorResponse : TPgCSVErrorResponse;
Buffer : Pointer;
begin
//create field cache
FFieldCache:=TList.Create;
//initiate map items
FMapItems:=0;
//allocate buffer size
GetMem(Buffer,FBufferSize);
//assign and open CSV file
AssignFile(FFile,FCSVFile);
SetTextBuf(FFile,Buffer^,FBufferSize);
Reset(FFile);
//open table if nessecary
if FAutoOpen then
begin
if Assigned(FBeforeOpenTable) then
FBeforeOpenTable(Self);
FDataset.Open;
if Assigned(FAfterOpenTable) then
FAfterOpenTable(Self);
end;
//export to table from CSV file
if Assigned(FBeforeExport) then
FBeforeExport(Self);
//set the counter to zero
C:=0;
Temp:=ShortDateFormat;
ShortDateFormat:=FDateFormat;
{*************************以下是文字資料匯入的核心程式碼部分,也是我要關心的部分**************************}
FDataset.DisableControls;
while (not Eof(FFile)) and (not FStop) do
begin
//read from CSV
Readln(FFile,RecordString);
//注意,這裡好像差了一點什麼東西
//add new record
try
FDataset.Append;
for i:=1 to CountMapItems do
if Uppercase(GetMapItem(i,D)) <> Uppercase(FIgnoreStr) then
case FDataset.FielyName(GetMapItem(i,D)).DataType of
ftInteger:
FDataset.FieldByName(GetMapItem(i,D)).AsInteger:=StrToIntDef(Trim(GetCSVRecordItem(i,RecordString)),FDefaultInt);
ftFloat:
begin
try
F:=StrToFloat(Trim(GetCSVRecordItem(i,RecordString)));
except
F:=FDefaultInt;
end;
FDataset.FieldByName(GetMapItem(i,D)).Aloat:=F;
end;
else
if FTrimData then
FDataset.FieldByName(GetMapItem(i,D)).AsString:=Trim(GetCSVRecordItem(i,RecordString))
else
FDataset.FieldByName(GetMapItem(i,D)).AsString:=GetCSVRecordItem(i,RecordString);
end;
//post record
FDataset.Post;
except
on E:Exception do
if not FSilentExport then
raise
else
if Assigned(FExportError) then
begin
FExportError(Self,E.Message,C,ErrorResponse);
if ErrorResponse = pgcsvAbort then
Break;
end;
end;
if Assigned(FOnAddRecord) then
FOnAddRecord(Self);
if Assigned(FExportProgress) then
FExportProgress(Self, C, FStop);
Inc(C);
end;
FDataset.EnableControls;
{*************************以上是文字資料匯入的核心程式碼部分**************************}
if Assigned(FAfterExport) then
FAfterExport(Self);
//close table if nessecary
if FAutoOpen then
begin
if Assigned(FBeforeCloseTable) then
FBeforeCloseTable(Self);
FDataset.Close;
if Assigned(FAfterCloseTable) then
FAfterCloseTable(Self);
end;
//close CSV file
CloseFile(FFile);
//disallocate buffer
FreeMem(Buffer);
ShortDateFormat:=Temp;
//free cache
for i:=FFieldCache.Count - 1 downto 0 do
Dispose(FFieldCache.Items[i]);
FFieldCache.Free;
end;
(這麼長!看得我眼都花了.好在找到了我所關心的核心程式碼,其他的?別管了吧,我這個懶惰的人.)差的是什麼呢?很明顯,我們希望把這個時候的RecordString開放出去,經過處理之後再回來進行匯入的操作.所以,這裡差的只是一個事件處理過程,在這個過程中,我們需要透過Delphi把RecordString的值傳出去讓處理.
既然沒有提供這個介面,那就自己動手新增了。由於需要傳引數,這個事件不能用標準的TNotifyEvent來定義,而需要重新宣告。
新的事件宣告和事件屬性如下:
type
...
TPgCSVRegulateStrEvent = procedure (Sender : TObject; var ARecordString: string) of object;
...
TPgCSV = class(TComponent)
Published
property RegulateString : TPgCSVRegulateStrEvent read FRegulateString write FRegulateString;
//寫完後別忘了按一下ctrl+shift+c
...
End;
好了,現在可以在我剛才註釋的地方寫事件呼叫方法的程式了.
...
while (not Eof(FFile)) and (not FStop) do
begin
//read from CSV
Readln(FFile,RecordString);
//xm4014's modification
if Assigned(FRegulateString) then
FRegulateString(self,RecordString);
//add new record
try
FDataset.Append;
...
重新編譯包檔案透過後,你就會在TPgCSV控制元件的事件頁面中發現RegulateString這個事件,雙擊它就可以新增你的處理程式碼了。這樣一來,無論文字資料中有什麼樣怪異的字元或格式,我們都透過這個事件處理預先過濾一編,讓控制元件放心的處理匯入的操作。簡單吧(也太簡單了點,居然還寫了這麼長!汗)
但是,這樣就行了嗎?
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-991437/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- delphi 控制元件的拿來主義(一) (轉)控制元件
- Delphi控制元件的拿來主義(三) (轉)控制元件
- 對Delphi控制元件的一點改良(二) (轉)控制元件
- 拿來主義實用主義以人為本----現代人生存
- 國產軟體的“拿來主義”:開源軟體、主導權
- 原來的控制元件delphi7裡何處尋? (轉)控制元件
- [Android]轉-Android 中的拿來主義(編譯,反編譯,AXMLPrinter2,smali,baksmali)!Android編譯XML
- 對Delphi控制元件的一點改良 (轉)控制元件
- 改良控制元件-Delphi自帶控制元件Bug的消除 (轉)控制元件
- 相當於delphi的日曆控制元件 (轉)控制元件
- 對Delphi控制元件的一點改良(三) (轉)控制元件
- 在Delphi中使用IP控制元件 (轉)控制元件
- 在delphi中使用flash控制元件 (轉)控制元件
- 自定義asp.net控制元件分析(二) (轉)ASP.NET控制元件
- 在Delphi中編寫控制元件的基本方法 (轉)控制元件
- Delphi的元件讀寫機制(二) (轉)元件
- 《delphi高手突破》節選二 (轉)
- 在Delphi中編寫控制元件的基本方法(1) (轉)控制元件
- DELPHI也可以實現控制元件陣列,用定義陣列變數實現控制元件陣列 (轉)控制元件陣列變數
- Delphi 常用文件資料之二 (轉)
- 告別理想主義,走向經驗主義 (轉)
- 實現控制元件的移動、改變大小(DELPHI實現) (轉)控制元件
- Delphi使用VB編寫的ActiveX控制元件全攻略 (轉)控制元件
- 直接拿來用!最火的前端開源專案(二)前端
- 直接拿來用!最火的iOS開源專案(二)iOS
- Delphi(BCB)中編譯器版本宏定義 (轉)編譯
- 直接拿來用!最火的Android開源專案(二)Android
- Delphi實用程式設計經驗二 (轉)程式設計
- 直接拿來用 九個超實用的PHP程式碼片段(二)PHP
- 自定義控制元件中的控制元件呼叫引用控制元件的頁面裡的函式 (轉)控制元件函式
- Delphi程式碼最佳化(二) 整數篇 (轉)
- Delphi實用程式設計經驗二則 (轉)程式設計
- 《Mastering Delphi 6學習筆記》之二 (轉)AST筆記
- Delphi皮膚控制元件去NAG提示控制元件
- delphi 控制元件 LssCalendar V2.0 (支援農曆的月曆控制元件)控制元件
- 一個delphi控制元件的破解 (12千字)控制元件
- Delphi重定義的訊息結構
- C#自定義控制元件:如果定義控制元件的事件C#控制元件事件