技術記事

Delphi で Excel なしで AutoFilter を実行する HotXLS

HotXLS は、Excel を起動せずに、XLSX ファイルにすでに保存されている AutoFilter 条件を評価します。v2.384.136 から、TXLSXWorksheet.PreviewAutoFilterRows、ApplyAutoFilter、AutoFilterRowVisible が、値リスト、日付グループ、上位/下位 n 件とパーセントフィルタ、相対日付と平均のフィルタ、塗りつぶしとフォント色のフィルタ、条件付き書式アイコンのフィルタを、凍結された基準日と明示的なリソース上限の下で実行します

いつもの引き金は、誰かが Excel でフィルタしたブックと、それに基づいて動作しなければならないサーバーです。誰かが「売上の Top 10」「受注日の今月」「期限切れ行の赤い塗りつぶし」を設定して保存し、ファイルをアップロードします。Delphi のサービスは今、Excel が表示するはずの行集合を必要としています。それを数える、エクスポートする、正しい行を非表示にしたままファイルを書き戻す。ファイルが持っているのは定義だけです。このリリースより前の HotXLS は普通の custom 条件を評価できましたが、高度な種類は実行するものなしに保存 XML の中に座っており、正直な答えは「Excel で開いてください」でした

HotXLS は保存済み AutoFilter から実際に何を実行するのか

HotXLS は、Excel が書いた filterColumn 要素から、列につき 1 つのフィルタ種別を実行し、列を AND で結合します。Excel のドロップダウンとまったく同じです。要素は ECMA-376 Part 1 §18.3.2 の AutoFilter グループに由来し、それぞれが固有の評価経路に写像されます

Delphi における HotXLS AutoFilter 実行は、保存された各 filterColumn 要素を 5 つの評価経路のいずれかに振り分ける。filters、customFilters、top10、dynamicFilter、colorFilter、iconFilter であり、それ以外は EXLSXAutoFilterUnsupported を上げる
HotXLS は ECMA-376 Part 1 の AutoFilter グループから列ごとに 1 つのフィルタ種別を評価し、列を AND で結合するため、可視行集合は Excel のドロップダウンが表示するはずのものと一致します
  • filter と dateGroupItem の子を持つ filters: チェックされた値リスト、任意の空欄エントリ、年から秒までの暦日付グループを OR で結合したもの
  • customFilters: AND か OR で結ばれた 1 つか 2 つの比較条件。すでに存在していた経路です
  • top10: 上位か下位 n 件、または n パーセント。val は 1〜500 件または 1〜100 パーセントに制限されます
  • dynamicFilter: 今日、昨日、明日、今週/先週/来週、今月/先月/来月、四半期、年、年初来、M1〜M12、Q1〜Q4、平均より上、平均より下
  • colorFilter と iconFilter: differential style を通じて解決された実効塗りつぶし色またはフォント色、アイコンセットと 0 起点の icon id による条件付き書式アイコン

そのリストの外のものは、推測された行集合を生む代わりに EXLSXAutoFilterUnsupported で失敗します。ST_DynamicFilterType の標準偏差より上/下の型、非グレゴリオ暦の calendarType 値、未知の拡張要素、未解決の dxfId 参照、そしてどういうわけか 2 つのフィルタ種別を同時に運ぶ列が含まれます。「だいたい合ってる」行を静かに返すフィルタエンジンは、拒否するものより悪質です。間違った顧客が入ったレポートが出ていくまで誰も気付かないのですから

まずプレビュー、次に適用

HotXLS は仕事を読み取り専用のプレビューとトランザクション的な適用に分け、どちらも同じ TXLSXAutoFilterExecutionOptions レコードを取ります。PreviewAutoFilterRows は一致するデータ行数を返し、行の可視性には一切触れません。ApplyAutoFilter(AResetVisible, Options) はまずすべての行判定を準備し、それから新しい非表示行集合を 1 ステップで公開するため、準備中の例外はシートを元のまま残します

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 で拒否します。スタックで宣言してフィールドを 1 つずつ埋めたレコードは、忘れたフィールドにゴミかゼロを載せているのが普通です。引数なしのオーバーロード(ApplyAutoFilter(True)、PreviewAutoFilterRows(Callback)、AutoFilterRowVisible(Row))は同じ既定値を、基準日を現在日として使います

AResetVisible フラグは、フィルタを通るけれどすでに非表示だった行の扱いを決めます。True は一致する全行を可視にします。再適用時の Excel の振る舞いです。False は一致行での以前の手動非表示を保ち、失敗した行にだけ非表示を足します。AutoFilter 範囲の外の行はどちらでも状態を保ちます。準備と公開の間にブックが変わっていた場合、ApplyAutoFilter は、古いデータから計算された判定を公開する代わりに例外を上げます

値リストがアスタリスクとクエスチョンマークを無視する理由

filters 要素の値リストエントリはリテラルの表示トークンなので、HotXLS は * と ? を普通の文字として比較します。ドロップダウンでチェックされた A* というエントリは、セルが正確に A* を表示していることを意味し、「A で始まる」ではありません。ワイルドカードは customFilters の領分で、あちらでは「で始まる」「を含む」が本当にパターンであり、HotXLS が Excel のワイルドカード条件とチルダエスケープを照合する方法で扱った別の規則を保ちます。この 2 つのセマンティクスを混ぜるのが、部品番号列をナンセンスにフィルタする最速の道です

比較は表示値に対して行われるので、どの表示かという疑問が生じます。HotXLS はセルを基本の数値書式で、保存されたスタイルオーバーレイを含めてフォーマットし、条件付き書式がその上に適用する数値書式は意図的に無視します。HotXLS のフィクスチャ内のネイティブ Excel コントロールがトークンの重要性を示します。数値の 1 とテキストの "1" はどちらも 1 の選択に一致し、0.0 書式の数値 1 は代わりに 1.0 とテキストの "1.0" に一致し、テキストの "01" は両方と区別されたままです。日付風テキストは日付にパースされないため、"2026-10-19" を保持するテキストセルが日付グループに落ちることはありません

Top 10、平均、動的日付のフィルタはどう判定するのか

HotXLS はランキングと平均のしきい値を、別のフィルタが可視に残した行に対してではなく、ソース列全体に対して 1 度だけ計算します。母集団に入るのは有限の数だけで、つまり "100" のような数値風テキストは除外され、母集団は行判定が始まる前に収集されます。ネイティブコントロールのブックでは、平均より上のフィルタは、2 つ目の列が値 50 を隠した後でも列全体の平均 27 を保ち、残りの行の平均 21.25 に切り替わりません

同値はしきい値で含まれます。Excel と同じです。20 で 2 件が同着の値への Top 3 フィルタは 4 行を選びます。パーセントモードは数値母集団の切り捨てた分率を取り、1 件の下限付きです。5 値の Top 50 パーセントは、しきい値 30 で 2 値を選びます。Bottom の変種は比較を鏡像します。これらの数字は、仕様書を読んだのではなく、HotXLS テストフィクスチャに記録された Excel 自身の行集合から来ています。仕様はほとんどの点で沈黙しています

HotXLS は Delphi で Excel AutoFilter の Top 10、パーセント、平均より上のしきい値をソース列全体に対して 1 度だけ計算し、非表示の値を母集団に残し、同値はしきい値で結果に含める
平均より上は残り行の平均 21.25 ではなく列全体の平均 27 を保ち、20 で 2 件が同着の値への Top 3 フィルタは 4 行を選びます

動的日付には凍結された基準日が要る

TXLSXAutoFilterExecutionOptions.ReferenceDate は、1 つの操作の間、「今日」の意味を凍結します。今週や先四半期のような相対フィルタも、色フィルタに供給される条件付き書式の日付ルールも、その 1 つの日付に対して評価されるため、23:59:59 のプレビューと 1 秒後の適用は一致します。ゼロ値は操作開始時に現在日を 1 度選びます。シリアル値はブックの 1900 または 1904 日付システムを尊重し、1900 システムでの Excel の架空シリアル 60 も含めて、M2 と Q1 のフィルタに効きます

ウィンドウは半開の暦区間です。今週は日曜から翌日曜まで、今四半期は四半期初日から次の四半期初日まで、年初来は 1 月 1 日から基準日の終わりまでです。バッチやテストで走るものには、ReferenceDate を明示的に固定してください。真夜中を跨ぐ夜間ジョブや、10 月に書かれて 1 月にも通らなければならないユニットテストは、さもなければ見える理由のない異なる行集合を生みます

HotXLS は Delphi で Excel dynamicFilter の相対日付ルールを、1 つの凍結された TXLSXAutoFilterExecutionOptions.ReferenceDate に対して評価し、半開の今週、四半期、年初来のウィンドウを使う
基準日を凍結すれば 23:59:59 のプレビューと 1 秒後の適用が一致し、M2 と Q1 のフィルタに影響する 1900 システムのシリアル 60 の癖も固定されます

数式セルはキャッシュされた値でフィルタされます。フィルタエンジンはブックを黙って再計算しないため、キャッシュ結果を残さなかったライターが保存したファイルは、フィルタが意味のある数や日付を見る前に明示的な計算パスが要ります

色フィルタはどの色を見るのか

HotXLS の色フィルタは、セルの直接書式と一致する条件付き書式のオーバーライドから来る実効色を見ますが、テーブルスタイルだけに由来するものは見ません。Excel も同じ振る舞いです。組み込みテーブルスタイルが薄い青に塗った縞行は、フィルタドロップダウンにとって「薄い青」ではありません。ネイティブコントロールでは、条件付き書式で赤く塗られたセルは赤い塗りフィルタに選ばれ、直接赤に書式化されていてもアクティブな黄色のルールに上書きされたセルは除外されます

3 つの改良が後続リリースで届きました。バージョン 2.384.138 はリニアとパスのグラデーション塗りを、単色とも塗りなしとも異なるものとして扱い、グラデーションはそのストップの 1 つが正確にその色を持っていても、単色の基準に一致しません。バージョン 2.384.140 はリッチテキストのフォント色フィルタリングを足しました。空でない run のいずれかが要求された RGB、自動、継承色を運ぶならセルは一致します。バージョン 2.384.150 が条件付きリッチテキストの判定を完成させました。これが奇妙なやつでした

ネイティブ Excel は、リッチテキストセルと、同じテキスト、同じ基本スタイル、同じ一致する条件付きルール経路を持つ素のセルとの間で、色判定を再利用でき、そのため可視行集合はそれらのセルの順序に依存し得ます。HotXLS はこれを、完全なデータ範囲を行の昇順で準備することで再現します。1 行について AutoFilterRowVisible を尋ねるだけのときでもです。したがって 40 行の検査は範囲全体のパスを要しますが、行検査、プレビュー、適用は同じ行に対して常に同じ答えを返します

すべてのリソースに上限があり、捕捉すべきもの

HotXLS は AutoFilter 操作の各種の仕事を、TXLSXAutoFilterExecutionOptions の専用フィールドで制限します。アップロードフォームから来るフィルタ済みブックは信頼できない入力だからです。既定は 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;

3 つの具体的な例外はすべて EXLSXAutoFilterExecution から派生し、それ自体が、対応カレンダーの外の基準日や公開前に変わったブックのような不正状態をカバーするため、基本クラスは最後に書いてください。いずれの場合もシートの行の可視性は変わらず、「ログを取り、スキップし、ファイルに手動レビューの印を付ける」が安全な既定ポリシーになります

境界はどこにあるか

カバレッジは広いですが、顧客にパリティを約束する前に知っておくべき縁がいくつかあります

  • 標準偏差の動的フィルタは、検証済みポリシーが存在するまで拒否されます。列挙型には値があるのですが
  • 週ウィンドウは日曜に始まります。別の週の開始に頼るブックはモデル化されません
  • 数式の結果は保存されたキャッシュだけから来て、フィルタ呼び出し内の再計算はありません
  • テーブルスタイルの色は、設計上、そして Excel に合わせて、決して色フィルタに一致しません
  • 未知のリッチテキスト構造や未知の色メタデータは明示的に失敗しますが、フォントファミリのような既知の非色プロパティは色の一致を妨げません
  • XLS ファサードとその BIFF8 ストレージは別の経路で、HotXLS が BIFF8 AutoFilter の DOPER 条件を書く方法で扱われます

自分で生成するブックで、AutoFilter 定義、データ検証、テーブルがどう関係するかをまだ決めているなら、HotXLS のデータ検証、AutoFilter とテーブルから始めてください。SetAutoFilter、列 id、フィルタが実際に効くのはいつかを扱っています

FAQ

PreviewAutoFilterRows はどの行が非表示かを変えるのか

いいえ。PreviewAutoFilterRows は保存された条件を評価し、戻り値と任意のコールバックで判定を報告するだけです。新しい非表示行集合を公開するのは ApplyAutoFilter だけで、すべての判定が準備された後にそうするため、失敗しても元の可視性は無傷です

相対日付フィルタが日によって異なる行を返すのはなぜか

今日、今週、先月のようなフィルタは、Excel とまったく同じく現在日との相対で定義されます。繰り返し可能な結果が要るときは、TXLSXAutoFilterExecutionOptions の ReferenceDate を固定日へ設定してください。ゼロ値は操作開始時の日付を意味します

HotXLS はテーブルスタイル由来の色でフィルタできるか

できませんし、Excel もできません。HotXLS の色フィルタは、セルの直接書式と対応済みの条件付き書式オーバーライドだけに一致します。組み込みかカスタムのテーブルスタイルだけが塗った色は参加しないため、色フィルタの結果は Excel のドロップダウンが選ぶはずのものと一致します

高度な AutoFilter 実行は、v2.384.136 以降、Excel なしで Excel ファイルを読み書きする Delphi スプレッドシートコンポーネント HotXLS の XLSX ワークシート API とともに出荷されます。グラデーション、リッチテキスト、条件付きリッチテキストの色処理は v2.384.150 までに追加されました