Delphi中對Oracle存取RTF文檔

Delphi中對Oracle存取RTF文檔
 [轉載學習用。。。]

--------------------------------------------------------------------------------
 
周勇生 2001年08月23日 15:33 天極網 賽迪網  
Delphi中對Oracle存取RTF文檔

    關係數據庫都提供大文檔的存儲和提取。對於視頻資料、音頻資料、圖象資料等大文檔,一般需要 另外開闢字段用於存儲摘要信息,因此在查詢和檢索時並不訪問大字段,而只是在存儲和提取時才操作 大字段。例如,你不能對Oracle中的LONG RAW類型進行LIKE介詞的查詢,更不能使用等號“ =”查詢。 這對於存儲大段文本(容量超過2K)同時又需要全文檢索是相當不便的。本文將介紹如何利用數據庫的 字符串數據類型存取和查詢大段文本。這裏以Oracle數據庫和Delphi應用程序爲例,重點介紹如何在數據庫中存取 RTF文檔。

    對於純文本,可以簡單地將其分割成若干個串,分別存儲到VARCHAR(2000)字段中即可。在查詢時 可以使用LIKE比較,從而達到全文檢索的目的。爲了保留換行等段落信息,應當將回車換行(#13#10) 也作爲串的一部分進行保存。數據錄入時可以提供Memo控件(不是DBMemo)進行錄入,然後順序連接各行,當連接成的串臨近2000個字符(單字節字符)長度時,就存入一條記錄,然後對剩餘的行重複上述操作。這樣,最終將純文本存成若干長度不超過2000的VARCHAR(2000) 字段中。這裏需要另外開闢字段 用於存儲文本編號和子序號,以便區分不同的文本和讀取文本時順序連接所有的子串。查詢純文本時, 就可以象查詢普通的VARCHAR 字段一樣,可以使用LIKE,也可以使用等號“ =”(幾乎不需要使用)。 需要注意的是,可能用戶提供的關鍵字正好被存儲在不同的子串中,這時是查詢不到的。因此,在設計時應當考慮存儲重複的串。例如,每個子串中僅有前1900個字符是有效字符,最後100 個字符用於存儲 下一個子串的前100 個字符。這樣就避免了關鍵字被分開的情況。唯一的不足是,必須限制用戶輸入的 查詢關鍵字長度不得超過100 個字符(50個漢字),但這很正常,算不上不足。

    事實上,同樣可以利用這一技巧對 RTF文檔進行存取和查詢。這時,用於錄入和顯示 RTF文檔的是 RichEdit控件(不是DBRichEdit),而不再是Memo控件。對於 RTF文檔的存取,不能象存取純文本那樣 通過Memo的屬性Lines.Strings[Index]進行操作(儘管RichEdit控件具有相同的屬性),因爲這樣做就 無法保存文檔的格式了。需要利用的是RichEdit的兩個方法:SaveToFile和LoadFromFile。需要了解的 是, RTF文件中用純字符描述字體、字號、文本等各種格式信息和內容信息。因此,存儲和提取時可以 視爲純文本進行操作。但對於查詢,就不能直接用LIKE加關鍵字的方式進行。因爲 RTF文檔中的每一個 漢字都是用特殊的表示方法存儲,只有單字節字符是原樣存儲。所以在查詢時要對關鍵字進行處理才能 用在查詢語句中。

    在測試這個例子之前,必須有如下的數據結構,這裏以(http://www.oracle.com/global/cn/)Oracle創建表的 SQL語句形式給出:

(*
CREATE TABLE TEST(                { 表名爲 TEST }
  DOCID   NUMBER NOT NULL,        { 文檔編號    }
  DOCNAME VARCHAR(40) NOT NULL,   { 文檔標題    }
  SUBID   NUMBER NOT NULL,        { 文檔子編號  }
  TEXT    VARCHAR(2000) NOT NULL, { 子文檔內容  }
  PRIMARY KEY(DOCID, SUBID));     { 聯合主鍵    }
*)

    下面是程序實例中的主要部分:

{ ... ... }

const
  BufSize = 2000;                                
{ 串的最大容量 }

type
  TBuffer     = array [1..BufSize] of Char;      
  { 串緩存       }
  TFileOfChar = file of Char;                    
  { 字符類型文件 }

  TChnChar = string[2];                          
  { 漢字字符類型 }

  { SQL查詢,返回首記錄首字段的值  }
function  SelectSQL(S: string): Variant;
begin
  Result := NULL;
  with TADOQuery.Create(Application) do try
    Connection := FMain.ADOConnection1;
    SQL.Append(S);
    SQL.SaveToFile('c:a.txt');
    Open;
    Result := Fields[0].AsVariant;
  finally
    Free;
  end;
  end;

{ 下面的函數將RTF文檔存入數據庫 }
function  RTFToDB(ARichEdit: TRichEdit; { 文檔容器 }
                  DocName: string;      { 文檔標題 }
                  ATable: TADOTable     { 操作的表 }
                  ): Boolean;           { 返回類型 }
const
  TmpFileName = 'c:x.rtf';        { 臨時文檔 }
var
  DocID, SubID, L: Integer;        { 局部變量 }
  S: string;                       { 串       }
  F: TFileOfChar;                  { 字符文件 }
  Buf: TBuffer;                    { 文本緩存 }
begin
  ARichEdit.Lines.SaveToFile(TmpFileName);{ 先存入文件 }
  AssignFile(F, TmpFileName);         { 打開文件   }
  Reset(F);
  try
    DocID :=               { 產生新的文檔編號      }
      SelectSQL('SELECT NVL(MAX(DOCID) + 1, 101) FROM TEST');
    with ATable do if not Active then Active := True;{ 確認表打開 }
    SubID := 0;           { 初始化子編號          }
    while not EOF(F) do begin
      Inc(SubID);
      BlockRead(F, Buf, BufSize, L);{ 讀取兩千個字符        }
      S := Buf;
      SetLength(S, L);    { 取實際讀取到的字節數  }
      with ATable do begin { 增加一條子文檔        }
        Append;
        FieldByName('DOCID').AsInteger := DocID;
        FieldByName('DOCNAME').AsString := DocName;
        FieldByName('SubID').AsInteger := SubID;
        FieldByName('TEXT').AsString := S;
        Post;
      end;
      end;
    Result := True; { 存儲成功 }
  except
    Result := False;{ 存儲失敗 }
  end;
  CloseFile(F);           { 關閉文件 }
  DeleteFile(TmpFileName);{ 刪除文件 }
end;


{ 下面的函數從數據庫中讀取RTF文檔,並在指定的容器中顯示 }
function  RTFFromDB(ARichEdit: TRichEdit;{ RTF文檔容器  }
         DocName: string; { 文檔標題     }
         AQuery: TADOQuery    { 操作的數據集 }
                    ): Boolean;    { 返回類型     }
const
  TmpFileName = 'c:tempx.rtf';    { 臨時文件     }
var
  S: string;                        { 局部串變量   }
  F: TFileOfChar;                   { 字符文件     }
  Buf: TBuffer;                     { 串緩存       }
  I, L: Integer;                    { 局部變量     }
begin
  ARichEdit.Clear;              { 清除當前顯示的內容    }
  AssignFile(F, TmpFileName);   { 關聯文件              }
  try
    Rewrite(F);         { 打開文件,準備寫入從數據庫讀出的數據 }
    with AQuery do begin
      Active := False;  { 關閉數據集  }
      SQL.Clear;        { 重建SQL語句 }
      SQL.Append('SELECT SUBID, TEXT FROM TEST WHERE DOCNAME = ''' +
                 DocName + ''' ORDER BY SUBID');
      Open;             { 打開數據集  }
      if RecordCount <> 0 then begin  { 確認數據集非空           }
        First;         { 移到首記錄-子文檔        }
        repeat         { 讀出一條子文檔並寫入文件 }
          S := FieldByName('TEXT').AsString;
          L := Length(S);
          for I := 1 to L do Buf[I] := S[I];
          BlockWrite(F, Buf, L);
          Next;
        until EOF;
      end;
    end;
    CloseFile(F);{ 關閉文件 }    
    ARichEdit.Lines.LoadFromFile(TmpFileName);{ 從文件中裝入RTF文檔 }
    Result := True; { 讀取成功 }
  except            { 讀取失敗 }
    try CloseFile(F); except end;
    Result := False;
  end;
  DeleteFile(TmpFileName); { 刪除臨時文件 }
end;

六安 淮南市 蚌埠市 黃山市 滁州市 合肥市 阜陽市 巢湖 宿州市 安慶市 淮北市 蕪湖市 馬鞍山 池州 銅陵市 宣城 { 下面的函數將漢字單字轉換成RTF中表示的形式。                   }
{ 如表示漢字“國”的是ASCII(b9)和ASCII(fa),這裏是十六進制;    }
{ 那麼在 RTF文件中對“國”字的表示佔用了 8個字節:              }
{               'b9'fa                                        }
{ 因此,需要在查詢之前進行轉換。由於表示方法中含有Delphi用於    }
{ 字符串的分解符:單撇號“'”,因此在轉換時需要考慮這一點,     }
{ 否則就不能構造出正確的 SQL查詢語句                            }
function ChnCharToRTFCode(Ch: TChnChar): string;
var
  C1, C2: Char;
  O1, O2: Byte;
  S: string;
begin
  C1 := Ch[1];
  C2 := Ch[2];
  O1 := Ord(C1);
  O2 := Ord(C2);
  S := Format('''''%2X', [O1]) + Format('''''%2X', [O2]);
  Result := Lowercase(S);{ 轉換爲小寫 }
end;

{ 根據需要檢索的關鍵字轉換成LIKE中使用的串。     }
{ 這裏用於區別漢字的方法是根據編碼。             }
{ 按照Windows 中的雙字節編碼規則,對於雙字節字符 }
{ 如漢字字符,是由兩個字節構成,其中第一個字節是 }
{ 引導字符。漢字引導字符的ASCII 碼大於 127,因此 }
{ 可以根據此特點來區分漢字和單字節字符。         }
function  MakeLikeRTFString(StrToFind: string): string;
var
  I: Integer;
  ChnChar: TChnChar;
  S: string;
begin
  S := '';
  I := 0;
  while I < Length(StrToFind) do begin
    Inc(I);
    if Integer(StrToFind[I]) >= $80 then begin{ 漢字的首字節一定不小於128 }
      ChnChar := StrToFind[I] + StrToFind[I + 1];
      Inc(I);
      S := S + ChnCharToRTFCode(ChnChar);
    end else begin{ 單字節字符 }
      S := S + StrToFind[I];
      if StrToFind[I] = '''' then S := S + StrToFind[I];{ 單撇號的特殊處理 }
    end;
  end;
  Result := S;
end;

{ 構造對關鍵字進行全文檢索的查詢語句 }
function  MakeLikeString(StrToFind: string): string;
var
  S: string;
begin
  S := MakeLikeRTFString(StrToFind);
  S := 'SELECT DISTINCT DOCNAME FROM TEST WHERE TEXT LIKE ''%' + S + '%''';
  Result := S;
end;  


 http://i.mop.com/caibao35/blog/2008/01/01/6046774.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章