技術文章

HotXLS 的僅中繼資料與選擇性 XLSX 載入

HotXLS 能開啟一份 XLSX 活頁簿,卻完全不剖析任何工作表儲存格。TXLSXWorkbook.MetadataOnly 會讀取活頁簿屬性、已定義名稱、工作表名稱與可見狀態、樣式、佈景主題、連線與外部連結,然後在儲存格 XML 之前停下來。對一個需要知道某個目錄裡活頁簿內容的掃描器而言,這就是幾分鐘與幾秒鐘的差別

另外三個相關的切換補完了這幅圖像:SelectedSheets 只為具名子集剖析儲存格、LoadSheet 稍後從原始封存具現化一張被略過的工作表,而 PreserveRawParts 則在儲存時把未變更零件的壓縮位元組原樣直通複製。三者合用,把「開啟活頁簿」從一次全有全無的操作,變成你可以劃定範圍的東西

在不剖析任何儲存格的情況下,你能學到什麼?

比大多數探索任務所需要的還多。在 Open 之前設好 MetadataOnly,活頁簿回來時會帶著它的工作表名稱與可見狀態、已定義名稱、文件屬性、樣式與佈景主題、外部連結以及連線。這些工作表會以零儲存格的物件存在

這正是目錄建立、稽核或路由決策所依賴的資訊。哪些活頁簿定義了一個叫做 TaxRate 的名稱?哪些帶有隱藏工作表?哪些連結到一部已除役的伺服器?用完整載入每個檔案來回答這些問題,正是一份夜間工作變成徹夜工作的原因

var
  Workbook: TXLSXWorkbook;
  I: Integer;
begin
  Workbook := TXLSXWorkbook.Create(nil);
  try
    Workbook.MetadataOnly := True;      // set before Open
    Workbook.Open(FileName);
    for I := 1 to Workbook.Sheets.Count do      // Sheets[] is 1-based
      if not Workbook.Sheets[I].Visible then
        Report.Add(FileName + ': hidden sheet ' +
          Workbook.Sheets[I].Name);
  finally
    Workbook.Free;
  end;
end;

從四十張工作表中只載入兩張

SelectedSheets 是一份工作表名稱的 TStringList。當它非空時,Open 只會為它點名的工作表剖析儲存格 XML;其他每一張工作表都保有正確的名稱與可見狀態,但不持有任何儲存格。空清單則代表先前的行為,也就是全部載入

它與 MetadataOnly 的互動值得把話講明白,因為弄反了會產生一份令人困惑的空活頁簿:MetadataOnly = True 會覆寫選擇並略過所有工作表。兩者擇一使用。探索用僅中繼資料;鎖定目標的工作用選擇

Workbook.SelectedSheets.Add('Summary');
Workbook.SelectedSheets.Add('Q3 Detail');
Workbook.Open('consolidated-2026.xlsx');
// Later, when the user opens a tab you skipped.
// LoadSheet takes the 0-based position and needs the source file,
// so it does not work on a workbook opened from a stream
if Workbook.LoadSheet(ZeroBasedPosition) then
  Grid.Refresh;

LoadSheet(Index) 會在需要時,從原始封存具現化一張被略過的工作表,而這正是讓這套模式能在互動式應用程式中使用、而不只是在批次中使用的原因。開啟時只載入使用者正在看的那張,其餘的等他們點擊時再載入。封存正是為此保持開啟,所以只要使用者可能還會要求另一張工作表,就讓活頁簿保持活著

為什麼一次往返會改動你從未碰過的零件?

因為質樸的儲存會重新產生一切。解壓縮佈景主題、建構物件模型、把它序列化回去、再重新壓縮——於是位元組不同了,即使佈景主題什麼也沒變。對 VBA 專案與樞紐快取而言,這比浪費還糟,因為這些零件帶有物件模型無法完整表達的結構

PreserveRawParts 用以下方式回應了這一點:在 SaveAs 期間,直接從來源封存把未變更零件的壓縮表示複製過去。佈景主題、VBA 專案、樞紐分析表與樞紐快取在未被修改時,會以位元組形式原樣搬移。沒有解壓縮、沒有重新壓縮、沒有在一次序列化往返中遺失某些東西的風險

代價是一條你本來就有的限制:來源封存在儲存時仍必須可用。如果你的工作流程把檔案讀進記憶體、關閉它、一小時後才儲存,原始複製路徑就沒有可複製的來源。在活頁簿的整個生命週期內,讓來源保持可觸及,這正是 LoadSheet 同樣施加的要求

刻意重新開啟的探索管線

在正式環境中行得通的模式,分為兩趟且不賣弄聰明。第一趟以僅中繼資料開啟並做決策。第二趟以工作表選擇重新開啟重要的檔案,並執行工作。重新開啟感覺浪費,直到你算出第一趟避免了什麼:第二次開啟是唯一剖析儲存格的一次,而且它只剖析第一趟所選的工作表

// Pass 1: discovery
Workbook.MetadataOnly := True;
Workbook.Open(FileName);
Wanted := SheetsMatching(Workbook, 'Invoice');
Workbook.Free;

// Pass 2: targeted load
Workbook := TXLSXWorkbook.Create(nil);
Workbook.SelectedSheets.AddStrings(Wanted);
Workbook.Open(FileName);

不要嘗試在 Open 之後清除旗標,就地把一份僅中繼資料的活頁簿升級。旗標是在開啟期間讀取的;事後清除它,對於一份儲存格 XML 從未被造訪過的活頁簿,什麼也不會改變

這與其他大型檔案的槓桿如何搭配

選擇性載入減少的是你剖析的東西。它不會減少一張工作表一旦載入後的成本,也幫不上你確實需要每張工作表每個儲存格的情況。對於那種情況,相關的工作落在儲存側,描述於 列塊儲存格儲存與區間樣式疊加的筆記,以及剖析側的 並行 XLSX 剖析與記憶體配置器。當任務是純粹的掃描、完全沒有編輯時,串流直接讀取器會跳過物件模型,效能會勝過任何會建構物件模型的載入策略

HotXLS 從 Delphi 與 C++Builder 的原生程式碼開啟 XLS 與 XLSX 檔案,不依賴 Excel,所以一項伺服器端的探索工作,除了你自己的可執行檔之外,什麼都不必安裝就能執行——支援的格式與授權請見 HotXLS 試算表元件頁