首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

Delphi 操作 Oracle 存取 RTF 文檔:VARCHAR 分段存儲實現全文檢索

     編輯:Delphi

問題背景

Oracle LONG RAW/BLOB 不支持 LIKE/= 查詢,無法對大文本進行全文檢索。本文方案:將 RTF 文檔按固定長度 (如 2000 字符) 分段存入 VARCHAR2(2000) 多行,保留 RTF 格式碼,利用數據庫原生索引實現模糊查詢。

數據表結構

CREATE TABLE TEST (
  DOCID    NUMBER NOT NULL,        -- 文檔主鍵
  DOCNAME  VARCHAR2(40) NOT NULL,  -- 文檔標題
  SUBID    NUMBER NOT NULL,        -- 片段序號 (1,2,3...)
  TEXT     VARCHAR2(2000) NOT NULL, -- RTF 片段內容
  PRIMARY KEY (DOCID, SUBID)
);

核心存取邏輯

1. 存儲:RTF → 臨時文件 → 分段寫入 DB

function RTFToDB(ARichEdit: TRichEdit; DocName: string; ATable: TADOTable): Boolean;
const TmpFile = 'c:\x.rtf'; BufSize = 2000;
var DocID, SubID, L: Integer; S: string; F: file of Char; Buf: array[1..BufSize] of Char;
begin
  ARichEdit.Lines.SaveToFile(TmpFile); // RichEdit 導出完整 RTF (含格式碼)
  AssignFile(F, TmpFile); Reset(F);
  try
    DocID := SelectSQL('SELECT NVL(MAX(DOCID)+1, 101) FROM TEST'); // 獲取新 ID
    if not ATable.Active then ATable.Active := True;
    SubID := 0;
    while not Eof(F) do
    begin
      Inc(SubID);
      BlockRead(F, Buf, BufSize, L); // 讀取 2000 字符
      SetLength(S, L); Move(Buf[1], PChar(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(TmpFile);
end;

2. 讀取:DB → 臨時文件 → RichEdit.LoadFromFile

function RTFFromDB(ARichEdit: TRichEdit; DocName: string; AQuery: TADOQuery): Boolean;
const TmpFile = 'c:\temp\x.rtf';
var S: string; F: file of Char; Buf: array[1..2000] of Char; I, L: Integer;
begin
  ARichEdit.Clear; AssignFile(F, TmpFile);
  try
    Rewrite(F);
    with AQuery do
    begin
      Active := False; SQL.Clear;
      SQL.Add('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(TmpFile); // RichEdit 解析 RTF 還原格式
    Result := True;
  except Result := False; end;
  DeleteFile(TmpFile);
end;

全文檢索關鍵:RTF 關鍵字轉碼

RTF 中漢字編碼為 \'XX\'YY (如「國」→ \'B9\'FA),單字節字符 (ASCII) 保持原樣。查詢前必須將用戶輸入關鍵字按相同規則轉碼。

轉碼函數

function ChnCharToRTFCode(Ch: string): string; // Ch 為雙字節漢字
var C1, C2: Char; O1, O2: Byte;
begin
  C1 := Ch[1]; C2 := Ch[2]; O1 := Ord(C1); O2 := Ord(C2);
  Result := LowerCase(Format('\''%2.2X\'%2.2X', [O1, O2])); // 輸出 \'b9\'fa
end;

function MakeLikeRTFString(StrToFind: string): string;
var I: Integer; Ch: string; S: string;
begin
  S := ''; I := 1;
  while I <= Length(StrToFind) do
  begin
    if Ord(StrToFind[I]) >= $80 then // 判斷雙字節字符引導位
    begin
      Ch := Copy(StrToFind, I, 2); Inc(I, 2);
      S := S + ChnCharToRTFCode(Ch);
    end
    else
    begin
      if StrToFind[I] = '''' then S := S + ''''''; // 單引號轉義 ''
      S := S + StrToFind[I]; Inc(I);
    end;
  end;
  Result := S;
end;

function MakeLikeString(StrToFind: string): string;
begin
  Result := 'SELECT DISTINCT DOCNAME FROM TEST WHERE TEXT LIKE ''%' + MakeLikeRTFString(StrToFind) + '%''';
end;

重疊段優化 (解決跨段關鍵字漏查)

若關鍵字恰好被切分在兩段邊界,單段 LIKE 會漏查。方案:每段有效長度 1900,末尾 100 字符存儲下一段前 100 字符 (重疊)。查詢關鍵字長度限制 ≤ 100 字符 (50 漢字),即可保證命中。

方案優缺點總結

優點缺點/限制
利用標準 VARCHAR 索引,查詢極快存儲空間增加 ~5% (重疊段)
保留完整 RTF 格式 (字體、顏色、段落)關鍵字長度受限 (≤ 重疊長度)
無需全文檢索引擎 (Oracle Text 等)RTF 格式碼干擾語義查詢 (如搜「紅色」會匹配格式碼)
兼容任何支持 VARCHAR 的數據庫大文檔分段數多,讀取需順序拼接

適用場景

  • 中小型文檔管理系統 (合同、公文、報告)。
  • 需保留富文本格式且要求關鍵字檢索。
  • 不想引入 Lucene/Elasticsearch/Oracle Text 等重型組件。

總結

通過 RTF 文件流分段存 VARCHAR + 關鍵字 RTF 轉碼 + 重疊段策略,在不改變數據庫架構前提下,實現了富文本的高效存儲與全文檢索。代碼邏輯清晰,易於移植到其他數據庫 (SQL Server, PostgreSQL, MySQL) 及數據訪問組件 (FireDAC, UniDAC)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved