Delphi中對Oracle存取RTF文檔(作者:蘇湧)

來源:互聯網
上載者:User

關聯式資料庫都提供大文檔的儲存和提取。對於視頻資料、音頻資料、圖象資料等大文檔,一般需要 另外開闢欄位用於儲存摘要資訊,因此在查詢和檢索時並不訪問大欄位,而只是在儲存和提取時才操作 大欄位。例如,你不能對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文檔中的每一個 漢字都是用特殊的表示方法儲存,只有單位元組字元是原樣儲存。所以在查詢時要對關鍵字進行處理才能 用在查詢語句中。

在測試這個例子之前,必須有如下的資料結構,這裡以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:/temp/x.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;{ ... ... }

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.