HotXLS 不啟動 Excel 就能計算 XLSX 檔裡已儲存的 AutoFilter 準則。從 v2.384.136 起,TXLSXWorksheet.PreviewAutoFilterRows、ApplyAutoFilter 與 AutoFilterRowVisible 能執行值清單、日期群組、前/後 n 名與百分比篩選、相對日期與平均篩選、填色與字型色彩篩選,以及條件式格式設定的圖示篩選,全程在凍結的參考日期與明確的資源限制之下
常見的觸發場景是:某人在 Excel 裡篩好一份活頁簿,而伺服器得接著處理它。有人設了「營收前 10 名」、訂單日期「本月」,再給逾期列上紅色填色,存檔上傳。您的 Delphi 服務現在需要拿到 Excel 會顯示的那組列:統計它、匯出它,或把正確的列隱藏後寫回檔案。檔案裡只有定義。在這個版本之前,HotXLS 能計算普通的自訂準則,但進階類型只是躺在儲存的 XML 裡,沒有東西執行它們,所以誠實的答案是「用 Excel 開」
HotXLS 實際執行已儲存 AutoFilter 的哪些部分?
HotXLS 每欄執行一種篩選類型,取自 Excel 寫下的 filterColumn 元素,欄與欄之間以 AND 結合,與 Excel 的下拉選單完全一致。這些元素來自 ECMA-376 Part 1 §18.3.2 的 AutoFilter 群組,各自對應一條獨立的計算路徑:
filters帶filter與dateGroupItem子元素:勾選的值清單、可選的空白項,以及從年到秒的曆法日期群組,以 OR 結合customFilters:一或兩個比較準則以 AND 或 OR 串接,這條路徑本來就有top10:前或後 n 個項目,或 n 百分比,val限 1-500 個項目或 1-100 百分比dynamicFilter:今天、昨天、明天、本/上/下週、月、季、年、年初至今、M1-M12、Q1-Q4、高於平均與低於平均colorFilter與iconFilter:透過差異樣式解析出的有效填色或字型色彩,以及按圖示集與零基圖示 id 的條件式格式設定圖示
清單之外的一切都以 EXLSXAutoFilterUnsupported 失敗,而不是產出一組猜測的列。包括 ST_DynamicFilterType 的高低於標準差類型、非格里曆的 calendarType 值、未知的擴充元素、解析不出來的 dxfId 參照,以及不知怎麼同時帶兩種篩選類型的欄。一個悄悄回傳「差不多」列集合的篩選引擎,比一個拒絕執行的更糟,因為沒人會發現,直到一份報告寄出去、裡面的客戶是錯的
先預覽,後套用
HotXLS 把工作拆成唯讀預覽與交易式套用,兩者收同一個 TXLSXAutoFilterExecutionOptions 記錄。PreviewAutoFilterRows 回傳符合的資料列數,絕不碰列可見性。ApplyAutoFilter(AResetVisible, Options) 先備妥每一列的決策,然後才一步發布新的隱藏列集合,所以準備期間的例外會讓工作表原封不動
var
Book: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Options: TXLSXAutoFilterExecutionOptions;
Matching: Integer;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('orders-filtered-by-sales.xlsx');
Sheet := Book.Sheets[1]; // Sheets 索引器從 1 起算
// 一律從預設值出發,再改單一欄位
Options := XLSDefaultAutoFilterExecutionOptions;
Options.ReferenceDate := EncodeDate(2026, 10, 19);
Matching := Sheet.PreviewAutoFilterRows(nil, Options); // 只取數量
WriteLn('Rows Excel would show: ', Matching);
// True:符合的列會變可見,即使先前被手動隱藏
Sheet.ApplyAutoFilter(True, Options);
Book.SaveAs('orders-applied.xlsx');
finally
Book.Free;
end;
end;
每次都用 XLSDefaultAutoFilterExecutionOptions 初始化記錄。這個函式先把記錄歸零再設好每個限制,執行環境對任何限制非正數的記錄以 EXLSXAutoFilterLimit 拒絕。一個宣告在堆疊上、逐欄位填值的記錄,您忘掉的欄位裡通常裝著垃圾或零。無參數重載(ApplyAutoFilter(True)、PreviewAutoFilterRows(Callback)、AutoFilterRowVisible(Row))用同樣的預設值,以當天日期為參考
AResetVisible 旗標決定通過篩選但原本就被隱藏的列怎麼辦。True 讓每個符合的列變可見,這是 Excel 重新套用時的行為。False 保留符合列上較早的手動隱藏,只對不符合的列追加隱藏。AutoFilter 範圍之外的列無論如何保持原狀。如果活頁簿在準備與發布之間被改動,ApplyAutoFilter 會丟例外,而不發布基於過期資料算出的決策
為什麼值清單不理會星號與問號?
filters 元素裡的值清單條目是字面的顯示 token,所以 HotXLS 把 * 與 ? 當普通字元比對。下拉選單裡勾選的 A*,指的是儲存格恰好顯示 A*,不是「以 A 開頭」。萬用字元屬於 customFilters,那裡的「開頭是」與「包含」才是真正的模式,並遵循HotXLS 如何比對 Excel 萬用字元準則與波浪號跳脫覆蓋的那套獨立規則。把兩種語意搞混,是把零件編號欄篩成一團亂的最快方式
比對對象是顯示值,這就帶出「哪個顯示」的問題。HotXLS 以儲存格的基礎數字格式格式化它,包括已儲存的樣式覆蓋,並刻意忽略條件式格式設定疊上去的數字格式。HotXLS 測試夾具裡的原生 Excel 控制項說明了 token 為什麼要緊:數值 1 與文字 "1" 都比對得上一個勾選的 1;格式化為 0.0 的數值 1 則改為與文字 "1.0" 一起比對上 1.0;文字 "01" 與兩者都不同。長得像日期的文字永遠不會被解析成日期,所以裝著 "2026-10-19" 的文字儲存格不會落進任何日期群組
前 10 名、平均與動態日期篩選怎麼判定?
HotXLS 對整個來源欄一次性計算排名與平均門檻,而不是對其他篩選留下來的可見列計算。只有有限數值進入母體,這意味著「100」這類長得像數字的文字會被排除,而母體在任何列決策做出之前就收集完畢。原生控制活頁簿裡,高於平均篩選在第二欄藏掉數值 50 之後,仍然保持整欄的平均值 27,而不是切換到剩餘列的 21.25
並列值在門檻處全數納入,與 Excel 一致。對含有兩個並列 20 的值做前 3 名篩選,會選出四列。百分比模式取數值母體的截斷份額,下限一個項目,所以五個值的前 50% 選出兩個值,門檻 30。後 n 名的變體鏡像這個比較。這些數字來自 HotXLS 測試夾具裡記錄的 Excel 自身列集合,不是讀規範來的——規範對大部分情況隻字未提
動態日期需要凍結的參考日期
TXLSXAutoFilterExecutionOptions.ReferenceDate 為整個操作凍結「今天」的意義。本週或上季這類相對篩選,以及餵給色彩篩選的條件式格式設定日期規則,全都對照同一個日期計算,所以 23:59:59 的預覽與一秒後的套用結論一致。值為零時,操作開始那一刻選定當天日期;序號值遵循活頁簿的 1900 或 1904 日期系統,包括 M2 與 Q1 篩選在 1900 系統下 Excel 那個虛構序號 60
這些視窗是半開的曆法區間:本週從週日跑到下一個週日,本季從季首日跑到下一季首日,年初至今從 1 月 1 日到參考日結束。任何批次或測試裡執行的代碼,請明確釘住 ReferenceDate。跨午夜跑的夜間排程,或十月寫的、一月還得通過的單元測試,否則會產出看不出原因就彼此不同的列集合
公式儲存格以快取值參與篩選。篩選引擎不會悄悄重算活頁簿,所以由未留下快取結果的寫入器存出的檔案,需要先做一次顯式計算,篩選才看得到有意義的數字或日期
色彩篩選看得到哪些顏色?
HotXLS 的色彩篩選看得到直接儲存格格式加上任何符合的條件式格式設定覆蓋所構成的有效顏色,僅來自表格樣式的則看不到。Excel 行為相同:內建表格樣式塗成淺藍的帶狀列,對篩選下拉來說不是「淺藍」。原生控制項裡,被條件式格式設定塗紅的儲存格會被紅色填色篩選選中,而直接格式化為紅、卻被作用中黃色規則覆蓋的儲存格則被排除
三個精修隨後續版本到齊。v2.384.138 把線性與路徑漸層填色視為有別於純色與無填色的獨立狀態,漸層因此永遠比不上一個純色準則,哪怕它某個停靠點恰好是那個顏色。v2.384.140 為複合文字加了字型色彩篩選,儲存格裡任何非空執行段帶著要求的 RGB、自動或繼承色彩即為符合。v2.384.150 補完條件式複合文字的判定,而它偏偏是最怪的那個
原生 Excel 會在「文字相同、基礎樣式相同、符合的條件式規則路徑相同」的複合文字儲存格與普通儲存格之間重用色彩決策,所以可見列集合可能取決於這些儲存格的順序。HotXLS 重現了這一點:以列升冪準備完整資料範圍,即使您只對某一列問 AutoFilterRowVisible。檢視第 40 列因此要付出一次範圍走訪的成本,但列檢視、預覽與套用對同一列永遠給出同一個答案
每種資源都有上限,以及該接住什麼
HotXLS 用 TXLSXAutoFilterExecutionOptions 裡各自的欄位,為 AutoFilter 操作的每類工作設界,因為上傳表單來的篩選活頁簿是不可信輸入。預設值:MaxXmlCharacters 1048576、MaxXmlNodes 16384、MaxFilterItems 4096、MaxExaminedCells 4000000、MaxEffectiveFormatCells 與 MaxPopulationValues 各 1000000、MaxContextBytes 67108864 位元組、MaxConditionalSteps 16000000。MaxContextBytes 量的是保留的邏輯狀態與預留的暫存信封,不是行程記憶體
type
TFilterJob = class
public
Deadline: TDateTime;
VisibleRows: TList<Integer>;
procedure HandleRow(Sender: TObject; SheetIndex, Row: Integer;
Visible: Boolean; var Cancel: Boolean);
procedure HandleWork(Sender: TObject; ACompleted: Int64;
var ACancel: Boolean);
end;
procedure TFilterJob.HandleRow(Sender: TObject; SheetIndex, Row: Integer;
Visible: Boolean; var Cancel: Boolean);
begin
// 每個資料列都會呼叫;Visible 是篩選決策
if Visible then
VisibleRows.Add(Row);
end;
procedure TFilterJob.HandleWork(Sender: TObject; ACompleted: Int64;
var ACancel: Boolean);
begin
// ACompleted 統計受檢儲存格,格式化期間可能停滯不動
ACancel := Now > Deadline;
end;
PreviewAutoFilterRows 的列回呼收到的是每一列連同其決策,不只是符合的列,函式仍然回傳符合數。設定它的 Cancel 參數可以在不丟例外的情況下提前結束走訪。OnWork 是取消掛鉤:把 ACancel 設為 True 會丟出 EXLSXAutoFilterCancelled。操作準備決策期間握有活頁簿的讀取租約,所以兩個回呼都不許修改那本活頁簿
Options := XLSDefaultAutoFilterExecutionOptions;
Options.ReferenceDate := Date;
Options.MaxExaminedCells := 500000; // 上傳檔案用更緊的上限
Options.OnWork := Job.HandleWork;
try
Sheet.PreviewAutoFilterRows(Job.HandleRow, Options);
Sheet.ApplyAutoFilter(True, Options);
except
on E: EXLSXAutoFilterUnsupported do
Log('Filter needs Excel: ' + E.Message);
on E: EXLSXAutoFilterLimit do
Log('Filter exceeded a budget: ' + E.Message);
on E: EXLSXAutoFilterCancelled do
Log('Filter cancelled at deadline');
on E: EXLSXAutoFilterExecution do
Log('Filter state invalid: ' + E.Message);
end;
三個特定例外都繼承自 EXLSXAutoFilterExecution,後者本身就涵蓋無效狀態,例如參考日期超出支援的曆法、或活頁簿在發布前被改動,所以基底類別請放最後。每種情況下工作表的列可見性都不變,這讓「記日誌、跳過、把檔案標記待人工審查」成為安全的預設策略
邊界在哪裡
覆蓋面很廣,但在向客戶承諾對等之前,有幾條邊界值得知道:
- 標準差動態篩選在驗證過的政策存在之前一律拒收,即使列舉裡有對應值
- 週視窗從週日開始;依賴其他週起始日的活頁簿不在建模範圍
- 公式結果只來自儲存的快取,篩選呼叫內部不做重算
- 表格樣式的顏色永遠比不上色彩篩選,這是設計決定,也與 Excel 一致
- 未知的複合文字結構或未知的色彩中繼資料會明確失敗,而字型家族這類已知的非色彩屬性不會擋下色彩比對
- XLS 門面與其 BIFF8 儲存是另一條路徑,見HotXLS 如何寫出 BIFF8 AutoFilter DOPER 準則
如果您還在琢磨自己產生的活頁簿裡,AutoFilter 定義、資料驗證與表格之間的關係,從HotXLS 在 Delphi 中的資料驗證、AutoFilter 與表格開始,它涵蓋 SetAutoFilter、欄 id,以及篩選何時真正生效
FAQ
PreviewAutoFilterRows 會改變哪些列被隱藏嗎?
不會。PreviewAutoFilterRows 只計算已儲存的準則,透過回傳值與可選回呼回報決策。只有 ApplyAutoFilter 發布新的隱藏列集合,而且是在所有決策備妥之後,所以失敗時原始可見性完好無缺
相對日期篩選為什麼不同天給出不同的列?
今天、本週、上個月這類篩選按定義相對於當天日期,與 Excel 完全一樣。需要可重現結果時,把 TXLSXAutoFilterExecutionOptions 的 ReferenceDate 設成固定日期;零值代表操作開始那一刻的日期
HotXLS 能以來自表格樣式的顏色篩選嗎?
不能,Excel 也不能。HotXLS 的色彩篩選只比對直接儲存格格式與支援的條件式格式設定覆蓋。僅由內建或自訂表格樣式塗上的顏色不參與,所以色彩篩選的結果與 Excel 下拉會選中的內容一致
進階 AutoFilter 執行自 v2.384.136 起隨HotXLS,免 Excel 讀寫 Excel 檔的 Delphi 試算表元件的 XLSX 工作表 API 提供,漸層、複合文字與條件式複合文字的顏色處理陸續補到 v2.384.150