HotXLS ukladá každé kritérium BIFF8 AutoFilteru ako AUTOFILTER record s dvomi 10-bajtovými štruktúrami DOPER a typ DOPERu rozhoduje o tom, ako Excel porovnáva. Od v2.384.45 zapisuje TXLSWorksheet.ApplyAutoFilter porovnanie ako '>=100' ako IEEE number DOPER, takže Excel porovnáva číselné bunky, nie text. Bug report, ktorý zmenu spôsobil, bol krátky a ničivý: nočný export nastavil filter na stĺpci s čiastkou, súbor sa otvoril bez najmenšej námietky, dropdown šípka ukazovala kritérium a filtrovanie nezhodilo ani jeden riadok. Nič nebolo corrupt. Bajty boli platné BIFF8, len platné nesprávneho druhu — a o presne tejto triede zlyhaní je tento článok, spolu s dvomi staršími chybami na úrovni bajtov opravenými vo v2.384.18
Čo BIFF8 AutoFilter v skutočnosti ukladá?
BIFF8 AutoFilter nie je jeden record, ale súbor troch typov recordov a kritériá nesie len record za jednotlivé pole. AUTOFILTERINFO ($009D, [MS-XLS] §2.4.8) zaznamenáva, koľko stĺpcov filter range pokrýva. FILTERMODE ($009B) je marker bez tela, ktorý HotXLS emituje len vtedy, keď má aspoň jedno pole aktívne kritérium. Potom dostáva každé aktívne pole vlastný AUTOFILTER record ($009E, §2.4.6): zero-based index poľa, slovo grbit, ktorého dva najnižšie bity sú wJoin, dva DOPERy presne po 10 bajtoch a voliteľný tail nesúci znaky prípadného string DOPERu. Index poľa je na disku zero-based, hoci ApplyAutoFilter čísluje polia od 1, čo začne trápiť v momente, keď record hľadáte v hex dumpy. Prvý bajt každého DOPERu, vt, hovorí, aký operand nasleduje:
$04je IEEE 754 double uložený v zostávajúcich 8 bajtoch, takto Excel uchováva číselné porovnanie$06je reťazec, ktorého dĺžka sedí v jednom bajtecch, pričom samotné znaky sa tlačia do tailu recordu$08je Bes hodnota, Boolean alebo chybový kód zabalený do dvoch bajtov$0Ca$0Eoperand nenesú: znamenajú zhodu so všetkými prázdnymi, respektíve všetkými neprázdnymi bunkami
Druhý bajt, grbitSgn, nesie porovnanie: hodnoty 1 až 6 zodpovedajú <, =, <=, >, <> a >=. HotXLS má oba bajty po fakte dohľadateľné cez AutoFilterColumns, ktorej položky vystavujú Criteria1 a Criteria2 ako objekty TXLSAutofilterDOPER s DataType, grbitSgn a Value, takže si môžete overiť, čo sa zapíše, namiesto hádania
Prečo filter '>=100' nezhodil v Exceli ani jeden riadok?
Filter nezhodil nič preto, lebo operand bol uložený ako text a Excel porovnáva string DOPER s bunkou ako text. Pred v2.384.45 odstránil CreateFilterDoper v lxFilter.pas prefix >= správne a nastavil sign na 6, potom ale vždy postavil vtString DOPER so znakmi 100. Číselná bunka s hodnotou 250 nikdy neobstojí v textovom porovnaní s "100", takže vypadol každý riadok. Žiadna výnimka, žiadna diagnostika, žiadna výzva na opravu od Excelu. Pravidlo od v2.384.45 je zámerné úzke: keď kritérium začína porovnávacím operátorom a zvyšok sa pod invariant-culture pravidlami dá rozobrať ako číslo, HotXLS zapíše vtIEEENumber DOPER s tým istým signom. Holá hodnota bez operátora zostáva v string podobe, lebo presne takto si Excel ukladá položku vybranú z dropdown zoznamu
var
Wb: IXLSWorkbook;
Sh: TXLSWorksheet;
Doper: TXLSAutofilterDOPER;
begin
Wb := TXLSWorkbook.Create;
Sh := Wb.Sheets.Add;
Sh.Cells[1, 1].Value := 'Region';
Sh.Cells[1, 2].Value := 'Amount';
Sh.Cells[2, 1].Value := 'North';
Sh.Cells[2, 2].Value := 250;
// Pole 2 = druhý stĺpec A1:B100 (na strane API 1-based)
Sh.ApplyAutoFilter('A1:B100', 2, '>=100');
Doper := Sh.AutoFilterColumns.Find(2).Criteria1;
// v2.384.45+: DataType = 4 (IEEE number), grbitSgn = 6 (>=)
// Pred opravou: DataType = 6 (string), čo nezhodilo nič
Assert(Doper.DataType = 4);
Wb.SaveAs('orders.xls');
end;
Parsovanie je miesto, kde zostávajú ďalšie pichľavé hrany. Operand prechádza cez TryStrToFloat s bodkou ako desatinným oddeľovačom, takže '>=1.5' sa stane číslom a '>=1,5' ostáva string DOPER a znovu poticho nezhodí nič, bez ohľadu na to, čo hlási Windows locale. Dátumy sú tá istá pasca v inom kostýme: '>=2026-01-01' nie je číslo, takže sa zapíše ako text, hoci Excel drží dátumové bunky ako serial numbers. Pri rovnosti na čísle dajú aj '=100', aj číselný Variant ako 100 IEEE DOPER so sign 2, zatiaľ čo holý reťazec '100' dá textovú zhodu. Číselné operandy skladajte v kóde, neformátujte ich pre ľudí:
var
Fmt: TFormatSettings;
Since: TDateTime;
begin
Fmt := TFormatSettings.Create;
Fmt.DecimalSeparator := '.';
// Prah so zlomkovou časťou: vždy formátujte s bodkou
Sh.ApplyAutoFilter('A1:D500', 3, '>' + FloatToStr(1499.5, Fmt));
// Dátumy: porovnávajte so serial number, ktorý Excel v bunke uchováva.
// Delphi TDateTime sa pre dátumy po marci 1900 rovná serialu 1900 systému
Since := EncodeDate(2026, 1, 1);
Sh.AutoFilterColumns.SetFieldCriteria(4, '>=' + IntToStr(Trunc(Since)),
xlAnd, Unassigned);
end;
Ako spájajú AND a OR dve podmienky?
Bity wJoin grbitu AUTOFILTER sú 0 pre AND a 1 pre OR a HotXLS mal tieto dve konštanty prehodené až do v2.384.18. Between filter typu aspoň 100 a menej než 500 sa uložil ako aspoň 100 alebo menej než 500, čo v praxi zhodí každé číslo a vyzerá to, ako keby sa filter jednoducho neaplikoval. Verejné konštanty operátorov pridávajú druhé portovacie úskalie. V HotXLS je xlAnd 0 a xlOr 1, zatiaľ čo Excel automation ich čísluje 1 a 2. XlAutoFilterOperator je holý Byte, takže kód preložený z VBA makra s literálnymi číslami sa skompiluje bez protestu a literál 1, ktorý v COM znamenal AND, tu znamená OR. Používajte menované konštanty a problém nemôže nastať:
// Čiastka medzi 100 (vrátane) a 500 (výhradne)
Sh.ApplyAutoFilter('A1:D500', 3, '>=100', xlAnd, '<500');
with Sh.AutoFilterColumns.Find(3) do
begin
Assert(Operator = xlAnd); // wJoin = 0 na disku
Assert(Criteria2.grbitSgn = 1); // 1 = menšie než
end;
Booleany, prázdne bunky a strop 255 znakov
Boolean kritérium sa ukladá ako Bes hodnota ([MS-XLS] §2.5.10) a Bes dáva bajt hodnoty bBoolErr na prvé miesto a flag fError na druhé. HotXLS ich pred v2.384.18 zapisoval v opačnom poradí, takže filter na TRUE vložil 1 do error flagu a Excel čítal kritérium ako chybový kód. Writer aj reader boli prehodené spolu, a preto HotXLS round-tripoval vlastné súbory bez námietky, zatiaľ čo Excel nesúhlasil — pripomienka, že sebakonzistentný round trip nepovie nič o súlade so špecifikáciou. Prázdne bunky nepotrebujú operand vôbec: samotné '=' dáva match-all-blanks DOPER ($0C) a samotné '<>' match-all-non-blanks DOPER ($0E)
String kritériá narážajú v rozložení DOPERu na tvrdý limit. Dĺžkové pole cch je jeden bajt, takže string operand nemôže presiahnuť 255 znakov a CreateFilterDoper dlhší text po odstránení operátora skráti, namiesto aby nechal dĺžkový bajt pretekať a desynchronizovať tail recordu. Skrátenie je tiché a filter na dlhom popisnom stĺpci sa môže správať inak než s plným textom, ktorý ste odovzdali. V BIFF8 ukladá tail každý reťazec ako jedno-bajtový flag nasledovaný UTF-16 code units a deklarovaná veľkosť recordu musí tieto bajty počítať presne — tá istá účtovnícka disciplína, ako v článku ako sa rozhádžu deklarácie dĺžky BIFF recordov v Delphi XLS writeri
Prečo druhé volanie ApplyAutoFilter zmaže prvé?
Každé volanie ApplyAutoFilter predefinuje celý filter range, takže prežije len kritérium z posledného volania. Vnútorne volá SetAutoFilter, ktoré pred znovuvybudovaním range vymaže každé pole, čo je správne pre jeden stĺpec a prekvapujúce pre dva. Ak chcete filtrovať viac stĺpcov, zavolajte ApplyAutoFilter raz na vytvorenie range a prvého kritéria a ostatné pridajte cez AutoFilterColumns.SetFieldCriteria, ktoré range aj ostatné polia nechá na pokoji. Obidve cesty ignorujú číslo poľa mimo range bez výnimky, takže si to overte načítaním späť, ideálne po opätovnom otvorení uloženého súboru:
Sh.ApplyAutoFilter('A1:D500', 1, 'North'); // range + pole 1
Sh.AutoFilterColumns.SetFieldCriteria(3, '>=100', xlAnd, Unassigned);
Sh.AutoFilterColumns.SetFieldCriteria(4, True, xlAnd, Unassigned);
Wb.SaveAs('orders.xls');
Wb := TXLSWorkbook.Create;
Wb.Open('orders.xls');
Assert(Wb.Sheets[1].AutoFilterColumns.Find(1).Active);
Assert(Wb.Sheets[1].AutoFilterColumns.Find(3).Criteria1.DataType = 4);
Majte na pamäti, že AUTOFILTER record je uložená definícia: HotXLS kritériá zapíše a na klasickom XLS hárku ich nevyhodnocuje, takže pipeline, ktorá potrebuje zhodné riadky na serveri, si ich musí vypočítať sama, zatiaľ čo XLSX facade ponúka vyhodnocovanie na úrovni riadkov, ako popisuje dátová validácia, AutoFilter a tabuľky v HotXLS. Keď už Excel riadky skryje, všetky súčty pod range závisia od toho, ako SUBTOTAL a AGGREGATE zaobchádzajú so skrytými a filtrovanými riadkami, čo je ďalšie miesto, kde sa číselný filter, ktorý poticho nezhodí nič, prejaví ako zlé číslo
HotXLS číta a zapisuje BIFF8 XLS aj XLSX workbooky natívne z Delphi a C++Builderu, vrátane kritérií AutoFilter s číselnými, Boolean a AND/OR DOPERmi, ktoré Excel vyhodnotí tak, ako bolo zamýšľané. Pozrite si HotXLS Delphi spreadsheet component pre funkcie, edície a skúšobnú verziu na stiahnutie