HotXLS menyimpan setiap kriteria AutoFilter BIFF8 sebagai record AUTOFILTER yang membawa dua struktur DOPER 10 byte, dan tipe DOPER menentukan cara Excel membandingkan. Sejak v2.384.45, TXLSWorksheet.ApplyAutoFilter menulis pembanding seperti '>=100' sebagai DOPER angka IEEE, sehingga Excel mencocokkan sel numerik alih-alih membandingkan teks. Laporan bug yang memicu perubahan ini pendek dan bikin frustrasi: export harian menerapkan filter di kolom amount, berkasnya terbuka tanpa keluhan, panah dropdown memperlihatkan kriterianya, dan filter mencocokkan nol baris. Tidak ada yang korup. Byte-nya valid BIFF8, hanya saja jenis valid yang salah, dan itulah kelas kegagalan yang dibahas artikel ini, bersama dua kesalahan level byte yang lebih lama yang diperbaiki di v2.384.18
Apa yang sebenarnya disimpan sebuah AutoFilter BIFF8?
AutoFilter BIFF8 adalah satu set tiga tipe record, bukan satu, dan hanya record per-field yang menyimpan kriteria. AUTOFILTERINFO ($009D, [MS-XLS] §2.4.8) mencatat berapa banyak kolom yang dicakup filter range. FILTERMODE ($009B) adalah marker tanpa body yang HotXLS keluarkan hanya kalau minimal satu field punya kriteria aktif. Lalu tiap field aktif mendapat record AUTOFILTER-nya sendiri ($009E, §2.4.6): indeks field berbasis nol, satu word grbit yang dua bit rendahnya adalah wJoin, dua DOPER masing-masing tepat 10 byte, dan ekor opsional yang menampung karakter DOPER string apa pun. Indeks field di disk berbasis nol padahal ApplyAutoFilter menomori field mulai dari 1, dan itu penting saat pertama kali Anda berburu record di hex dump. Byte pertama tiap DOPER, vt, menyatakan jenis operand yang mengikuti:
$04adalah IEEE 754 double yang tersimpan di 8 byte sisanya, itulah cara Excel menyimpan pembanding numerik$06adalah string yang panjangnya hidup di satu bytecch, dengan karakternya sendiri didorong ke ekor record$08adalah nilai Bes, Boolean atau kode error yang dipadatkan ke dua byte$0Cdan$0Etidak membawa operand dan berarti cocokkan semua blank serta cocokkan semua non-blank
Byte kedua, grbitSgn, menyimpan pembandingnya: 1 sampai 6 dipetakan ke <, =, <=, >, <> dan >=. HotXLS membuat kedua byte itu tetap terlihat setelahnya lewat AutoFilterColumns, yang itemnya mengekspos Criteria1 dan Criteria2 sebagai objek TXLSAutofilterDOPER dengan DataType, grbitSgn dan Value, jadi Anda bisa meng-assert pada apa yang akan ditulis alih-alih menebak-nebak
Mengapa filter '>=100' tidak mencocokkan satu baris pun di Excel?
Filter itu tidak mencocokkan apa pun karena operandnya tersimpan sebagai teks, dan Excel membandingkan DOPER string terhadap sel sebagai teks. Sebelum v2.384.45, CreateFilterDoper di lxFilter.pas benar melepas prefiks >= dan mengatur sign ke 6, lalu selalu membangun DOPER vtString yang menampung karakter 100. Sel angka berisi 250 tidak pernah memenuhi pembanding teks terhadap "100", jadi semua baris tersingkir. Tidak ada exception, tidak ada diagnostik, tidak ada prompt perbaikan dari Excel. Aturannya sejak v2.384.45 sengaja dibuat sempit: kalau kriteria diawali operator pembanding dan sisanya ter-parse sebagai number di bawah aturan invariant-culture, HotXLS menulis DOPER vtIEEENumber dengan sign yang sama. Nilai polos tanpa operator tetap berbentuk string, karena itulah cara Excel sendiri menyimpan item yang dipilih dari daftar dropdown
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;
// Field 2 = kolom kedua A1:B100 (berbasis 1 di sisi API)
Sh.ApplyAutoFilter('A1:B100', 2, '>=100');
Doper := Sh.AutoFilterColumns.Find(2).Criteria1;
// v2.384.45+: DataType = 4 (angka IEEE), grbitSgn = 6 (>=)
// Sebelum fix: DataType = 6 (string), yang tidak mencocokkan apa pun
Assert(Doper.DataType = 4);
Wb.SaveAs('orders.xls');
end;
Di parsing itulah sisa tajamannya berada. Operand melewati TryStrToFloat dengan titik sebagai pemisah desimal, jadi '>=1.5' menjadi number dan '>=1,5' tetap DOPER string yang lagi-lagi diam-diam tidak mencocokkan apa pun, apa pun kata locale Windows. Tanggal adalah jebakan yang sama dengan kostum berbeda: '>=2026-01-01' bukan number, jadi ditulis sebagai teks, sementara Excel menyimpan sel tanggal sebagai serial number. Untuk kesetaraan pada angka, '=100' maupun Variant numerik seperti 100 menghasilkan DOPER IEEE dengan sign 2, sementara string polos '100' menghasilkan pencocokan teks. Bangun operand numerik di kode, jangan memformatnya untuk manusia:
var
Fmt: TFormatSettings;
Since: TDateTime;
begin
Fmt := TFormatSettings.Create;
Fmt.DecimalSeparator := '.';
// Threshold dengan pecahan: selalu format dengan titik
Sh.ApplyAutoFilter('A1:D500', 3, '>' + FloatToStr(1499.5, Fmt));
// Tanggal: bandingkan dengan serial number yang Excel simpan di sel.
// TDateTime Delphi sama dengan serial sistem 1900 untuk tanggal setelah Maret 1900
Since := EncodeDate(2026, 1, 1);
Sh.AutoFilterColumns.SetFieldCriteria(4, '>=' + IntToStr(Trunc(Since)),
xlAnd, Unassigned);
end;
Bagaimana AND dan OR menggabungkan dua kondisi?
Bit wJoin milik grbit AUTOFILTER bernilai 0 untuk AND dan 1 untuk OR, dan HotXLS sempat menukar kedua konstanta itu sampai v2.384.18. Filter gaya between seperti minimal 100 dan di bawah 500 tersimpan sebagai minimal 100 atau di bawah 500, yang praktisnya mencocokkan setiap number dan terlihat seperti filter sama sekali tidak diterapkan. Konstanta operator publik menambah bahaya porting kedua. Di HotXLS, xlAnd bernilai 0 dan xlOr bernilai 1, sedangkan Excel automation menomorinya 1 dan 2. XlAutoFilterOperator adalah Byte biasa, jadi kode yang diterjemahkan dari macro VBA dengan angka literal compile bersih, dan literal 1 yang berarti AND di COM kini berarti OR. Pakai konstanta bernamanya dan masalah itu tidak mungkin muncul:
// Amount antara 100 (inklusif) dan 500 (eksklusif)
Sh.ApplyAutoFilter('A1:D500', 3, '>=100', xlAnd, '<500');
with Sh.AutoFilterColumns.Find(3) do
begin
Assert(Operator = xlAnd); // wJoin = 0 di berkas
Assert(Criteria2.grbitSgn = 1); // 1 = kurang dari
end;
Boolean, blank, dan plafon 255 karakter
Kriteria Boolean tersimpan sebagai nilai Bes ([MS-XLS] §2.5.10), dan Bes menaruh byte nilai bBoolErr lebih dulu lalu flag fError kemudian. HotXLS menulis keduanya dalam urutan terbalik sebelum v2.384.18, sehingga filter untuk TRUE menaruh 1 ke flag error dan Excel membaca kriterianya sebagai kode error. Writer dan reader tertukar bersamaan, itulah sebabnya HotXLS round-trip berkasnya sendiri tanpa keluhan sementara Excel tidak setuju, pengingat bahwa round trip yang konsisten dengan dirinya sendiri tidak membuktikan apa pun soal kesesuaian spec. Blank tidak butuh operand sama sekali: mengirim '=' sendirian menghasilkan DOPER match-all-blanks ($0C) dan '<>' sendirian DOPER match-all-non-blanks ($0E)
Kriteria string menabrak batas keras di layout DOPER. Field panjang cch hanya satu byte, jadi operand string tidak boleh melampaui 255 karakter, dan CreateFilterDoper memotong teks yang lebih panjang setelah melepas operatornya alih-alih membiarkan byte panjang wrap dan mendesinkronkan ekor record. Pemotongannya senyap, dan filter di kolom deskripsi panjang bisa mencocokkan berbeda dari teks lengkap yang Anda kirim. Di BIFF8 ekor menyimpan tiap string sebagai flag satu byte diikuti unit kode UTF-16, dan ukuran record yang dideklarasikan harus menghitung byte-byte itu persis, disiplin pembukuan yang sama yang dibahas di cara deklarasi panjang record BIFF bergeser di writer XLS Delphi
Mengapa panggilan ApplyAutoFilter kedua menghapus yang pertama?
Tiap panggilan ApplyAutoFilter mendefinisikan ulang seluruh filter range, jadi hanya kriteria dari panggilan terakhir yang selamat. Di dalamnya ia memanggil SetAutoFilter, yang mengosongkan semua field sebelum membangun ulang range, dan itu benar untuk satu kolom dan mengejutkan untuk dua. Untuk memfilter beberapa kolom, panggil ApplyAutoFilter sekali untuk menetapkan range dan kriteria pertama, lalu tambahkan yang lain lewat AutoFilterColumns.SetFieldCriteria, yang membiarkan range dan field lain sendiri. Kedua jalur mengabaikan nomor field di luar range tanpa raise, jadi verifikasi dengan membaca kembali, idealnya setelah membuka ulang berkas yang disimpan:
Sh.ApplyAutoFilter('A1:D500', 1, 'North'); // rentang + field 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);
Ingat bahwa record AUTOFILTER adalah definisi tersimpan: HotXLS menulis kriterianya dan tidak mengevaluasinya di worksheet XLS klasik, jadi pipeline yang butuh baris-baris cocok di server harus menghitungnya sendiri di sana, sementara facade XLSX menawarkan evaluasi level baris seperti ditunjukkan di data validation, AutoFilter, dan tables HotXLS di Delphi. Begitu Excel benar-benar menyembunyikan baris, total apa pun di bawah range bergantung pada cara SUBTOTAL dan AGGREGATE memperlakukan baris tersembunyi dan terfilter, tempat berikutnya filter numerik yang diam-diam tidak mencocokkan apa pun muncul sebagai angka yang salah
HotXLS membaca dan menulis workbook BIFF8 XLS dan XLSX secara native dari Delphi dan C++Builder, termasuk kriteria AutoFilter dengan DOPER numerik, Boolean, dan AND/OR yang Excel evaluasi sesuai harapan. Lihat komponen spreadsheet Delphi HotXLS untuk fitur, edisi, dan unduhan trial