Tehnički članak

BIFF8 AutoFilter DOPER kriteriji u Delphiju uz HotXLS

HotXLS svaki BIFF8 AutoFilter kriterij sprema kao AUTOFILTER zapis koji nosi dvije DOPER strukture od po 10 bajtova, a DOPER tip odlučuje kako Excel uspoređuje. Od v2.384.45 TXLSWorksheet.ApplyAutoFilter usporedbu poput '>=100' zapisuje kao IEEE number DOPER, pa Excel pogađa brojčane ćelije umjesto da uspoređuje tekst. Izvještaj o grešci koji je potaknuo izmjenu bio je kratak i izluđujuć: noćni izvoz primijenio je filter na stupac s iznosima, datoteka se otvorila bez prigovora, strelica na padajućem izborniku pokazivala je kriterij, a filter pogodio je točno nula redaka. Ništa nije bilo oštećeno. Bajtovi su bili valjani BIFF8, samo krive vrste valjani, i to je klasa grešaka kroz koju ovaj članak prolazi, zajedno s dvije starije greške na razini bajtova popravljene u v2.384.18

Što BIFF8 AutoFilter zapravo sprema?

BIFF8 AutoFilter je skup od tri vrste zapisa, ne jedan, i kriterije nosi samo zapis po polju. AUTOFILTERINFO ($009D, [MS-XLS] §2.4.8) bilježi koliko stupaca raspon filtra pokriva. FILTERMODE ($009B) je marker bez tijela koji HotXLS ispisuje samo kad barem jedno polje ima aktivni kriterij. Zatim svako aktivno polje dobiva vlastiti AUTOFILTER zapis ($009E, §2.4.6): indeks polja od nule, grbit riječ čija su donja dva bita wJoin, dva DOPER-a od točno po 10 bajtova i neobavezni rep koji drži znakove svakog string DOPER-a. Indeks polja na disku je nula-baziran iako ApplyAutoFilter polja broji od 1, što postaje važno prvog puta kad zapis potražite u hex dumpu. Prvi bajt svakog DOPER-a, vt, kaže kakav operand slijedi:

  • $04 je IEEE 754 double spremljen u preostalih 8 bajtova, tako Excel sprema brojčanu usporedbu
  • $06 je string čija duljina stoji u jednom cch bajtu, a znakovi su gurnuti u rep zapisa
  • $08 je Bes vrijednost, logička vrijednost ili kod greške zapakirani u dva bajta
  • $0C i $0E ne nose operand i znače pogađanje svih praznih odnosno svih nepraznih

Drugi bajt, grbitSgn, nosi usporedbu: 1 do 6 mapira se na <, =, <=, >, <> i >=. HotXLS oba bajta drži vidljivima i poslije kroz AutoFilterColumns, čije stavke izlažu Criteria1 i Criteria2 kao TXLSAutofilterDOPER objekte s DataType, grbitSgn i Value, pa možete testirati ono što će biti zapisano umjesto da nagađate

Anatomija HotXLS AUTOFILTER zapisa s indeksom polja od nule, grbit riječju čija donja dva bita nose wJoin i dvjema DOPER strukturama od po 10 bajtova čiji vt bajt bira IEEE number, string, Bes logički, prazni ili neprazni operand dok grbitSgn kodira operator usporedbe koji Excel primjenjuje
Svaki AUTOFILTER zapis nosi dva 10-bajtna DOPER-a, a vt bajt odlučuje uspoređuje li Excel kriterij kao broj, tekst, logičku vrijednost ili test praznoće — oba pročitajte natrag kroz AutoFilterColumns prije spremanja

Zašto je '>=100' filter u Excelu pogađao nula redaka?

Filter nije pogodio ništa jer je operand bio spremljen kao tekst, a Excel string DOPER uspoređuje s ćelijom kao tekst. Prije v2.384.45 CreateFilterDoper u lxFilter.pas ispravno je skidao prefiks >= i postavljao znak na 6, pa je zatim uvijek gradio vtString DOPER sa znakovima 100. Brojčana ćelija s vrijednošću 250 nikad ne prolazi tekstovnu usporedbu s "100", pa su ispali svi redci. Bez iznimke, bez dijagnostike, bez Excelova poziva na popravak. Pravilo od v2.384.45 usko je namjerno: ako kriterij počinje operatorom usporedbe i ostatak se po invariant-culture pravilima parsira kao broj, HotXLS zapisuje vtIEEENumber DOPER s istim znakom. Gola vrijednost bez operatora ostaje u string obliku, jer tako Excel sam sprema stavku odabranu s padajućeg popisa

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;

  // Polje 2 = drugi stupac od A1:B100 (na strani API-ja od 1)
  Sh.ApplyAutoFilter('A1:B100', 2, '>=100');

  Doper := Sh.AutoFilterColumns.Find(2).Criteria1;
  // v2.384.45+: DataType = 4 (IEEE broj), grbitSgn = 6 (>=)
  // Prije popravka: DataType = 6 (string), što ne pogađa ništa
  Assert(Doper.DataType = 4);

  Wb.SaveAs('orders.xls');
end;
HotXLS isti >=100 AutoFilter kriterij zapisuje kao vtString DOPER koji ne pogađa brojčane ćelije ili kao vtIEEENumber DOPER s grbitSgn 6 koji Excel brojčano vrednuje prema iznosu 250, što je tiha greška nula redaka koju je CreateFilterDoper popravio u v2.384.45
Bajtovi su oba puta bili valjani BIFF8 — promijenio se samo bajt tipa operanda, pa je Excel datoteku otvorio, kriterij prikazao u padajućem izborniku, a opet pogodio nula redaka

U parsiranju žive preostali oštri rubovi. Operand prolazi kroz TryStrToFloat s točkom kao decimalnim separatorom, pa '>=1.5' postaje broj, dok '>=1,5' ostaje string DOPER i opet tiho ne pogađa ništa, ma što Windows locale govorio. Datumi su ista zamka u drugom kostimu: '>=2026-01-01' nije broj, pa se zapisuje kao tekst, dok Excel datumske ćelije drži kao serijske brojeve. Za jednakost na broju i '=100' i brojčani Variant poput 100 daju IEEE DOPER sa znakom 2, dok goli string '100' daje tekstovno podudaranje. Brojčane operande gradite u kodu umjesto da ih formatirate za ljude:

var
  Fmt: TFormatSettings;
  Since: TDateTime;
begin
  Fmt := TFormatSettings.Create;
  Fmt.DecimalSeparator := '.';

  // Prag s decimalama: uvijek formatiraj s točkom
  Sh.ApplyAutoFilter('A1:D500', 3, '>' + FloatToStr(1499.5, Fmt));

  // Datumi: uspoređujte sa serijskim brojem koji Excel sprema u ćeliju.
  // Delphi TDateTime odgovara serijskom broju 1900-sustava za datume nakon ožujka 1900
  Since := EncodeDate(2026, 1, 1);
  Sh.AutoFilterColumns.SetFieldCriteria(4, '>=' + IntToStr(Trunc(Since)),
    xlAnd, Unassigned);
end;

Kako AND i OR spajaju dva uvjeta?

wJoin bitovi AUTOFILTER grbita su 0 za AND i 1 za OR, a HotXLS je te dvije konstante imao obrnute sve do v2.384.18. Between stil filtra poput najmanje 100 i ispod 500 spremljen je kao najmanje 100 ili ispod 500, što u praksi pogađa svaki broj i izgleda kao da se filter jednostavno nije primijenio. Javne operatorske konstante unose drugu opasnost pri prenošenju koda. U HotXLS-u xlAnd je 0 a xlOr je 1, dok ih Excel automation broji 1 i 2. XlAutoFilterOperator je običan Byte, pa kod preveden iz VBA makroe s literalnim brojevima prolazi kompilaciju bez prigovora, a literal 1 koji je u COM-u značio AND sada znači OR. Koristite imenovane konstante i problem ne može nastati:

// Iznos između 100 (uključivo) i 500 (isključivo)
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 = manje od
end;
Raspored wJoin bitova u HotXLS AutoFilter kriterijima gdje 0 spaja dva DOPER-a s AND a 1 s OR, brojevni prikaz kako su obrnute konstante prije v2.384.18 between filter proširile u OR koji pogađa sve, te sudar numeracije xlAnd i xlOr s Excel automation
Obrnute wJoin konstante pretvorile su between filter u onaj koji pogađa svaki broj, a prevedeni VBA se i dalje kompajlira jer je XlAutoFilterOperator običan Byte — literal 1 koji je pod COM automation značio AND ovdje znači OR

Logičke vrijednosti, prazne ćelije i strop od 255 znakova

Logički kriterij sprema se kao Bes vrijednost ([MS-XLS] §2.5.10), a Bes stavlja vrijednosni bajt bBoolErr prvi i flag fError drugi. HotXLS ih je do v2.384.18 pisao obrnutim redoslijedom, pa je filter za TRUE u error flag utisnuo 1, a Excel je kriterij čitao kao kod greške. Pisac i čitač bili su zamijenjeni zajedno, pa se HotXLS-ove vlastite datoteke bez prigovora vraćale kroz round trip dok se Excel razilazio, podsjetnik da round trip usklađen sam sa sobom ništa ne dokazuje o usklađenosti sa specifikacijom. Prazne ćelije uopće ne trebaju operand: '=' sam za sebe daje DOPER koji pogađa sve prazne ($0C), a '<>' sam za sebe DOPER koji pogađa sve neprazne ($0E)

String kriteriji u DOPER rasporedu udaraju o tvrdi limit. Polje duljine cch jedan je bajt, pa string operand ne smije prijeći 255 znakova, a CreateFilterDoper dulji tekst skraćuje nakon skidanja operatora umjesto da pusti bajt duljine da se prelije i desinkronizira rep zapisa. Skraćivanje je tiho, pa filter na stupcu s dugim opisima može pogađati drugačije nego puni tekst koji ste proslijedili. U BIFF8 rep svaki string sprema kao jednobajtni flag nakon kojeg slijede UTF-16 code units, a deklarirana veličina zapisa mora te bajtove brojati točno, ista knjigovodstvena disciplina obrađena u članku kako deklaracije duljine BIFF zapisa slijeću u Delphi XLS writeru

Zašto drugi poziv ApplyAutoFilter briše prvi?

Svaki poziv ApplyAutoFilter redefinira cijeli raspon filtra, pa preživi samo kriterij iz posljednjeg poziva. Iznutra zove SetAutoFilter, koji prije ponovne izgradnje raspona briše sva polja, što je ispravno za jedan stupac a iznenađujuće za dva. Da filtrirate više stupaca, ApplyAutoFilter zovite jednom da postavite raspon i prvi kriterij, a ostale dodajte kroz AutoFilterColumns.SetFieldCriteria, koji raspon i ostala polja ostavlja na miru. Oba puta broj polja izvan raspona ignoriraju bez podizanja iznimke, pa provjerite čitanjem natrag, po mogućnosti nakon ponovnog otvaranja spremljene datoteke:

Sh.ApplyAutoFilter('A1:D500', 1, 'North');                     // raspon + polje 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);

Imajte na umu da je AUTOFILTER zapis spremljena definicija: HotXLS kriterije zapisuje i ne vrednuje ih na klasičnom XLS radnom listu, pa pipeline kojem na serveru trebaju pogođeni redci mora ih izračunati sam tamo, dok XLSX fasada nudi vrednovanje na razini retka kako je prikazano u članku o HotXLS validaciji podataka, AutoFilteru i tablicama u Delphiju. Kad Excel jednom sakrije retke, svi zbrojevi ispod raspona ovise o tome kako SUBTOTAL i AGGREGATE tretiraju skrivene i filtrirane retke, i to je sljedeće mjesto gdje se brojčani filter koji tiho ne pogađa ništa pokazuje kao pogrešan broj

HotXLS čita i zapisuje BIFF8 XLS i XLSX radne knjige nativno iz Delphija i C++Buildera, uključujući AutoFilter kriterije s brojčanim, logičkim i AND/OR DOPER-ima koje Excel vrednuje kako treba. Pogledajte HotXLS Delphi spreadsheet komponentu za funkcije, izdanja i probnu verziju