Artikel Teknis

Kriteria DOPER AutoFilter BIFF8 di Delphi dengan HotXLS

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:

  • $04 adalah IEEE 754 double yang tersimpan di 8 byte sisanya, itulah cara Excel menyimpan pembanding numerik
  • $06 adalah string yang panjangnya hidup di satu byte cch, dengan karakternya sendiri didorong ke ekor record
  • $08 adalah nilai Bes, Boolean atau kode error yang dipadatkan ke dua byte
  • $0C dan $0E tidak 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

Anatomi record AUTOFILTER HotXLS yang memperlihatkan indeks field berbasis nol, word grbit yang dua bit rendahnya menampung wJoin, dan dua struktur DOPER 10 byte yang byte vt memilih operand IEEE number, string, Bes Boolean, blank atau non-blank sementara grbitSgn mengenkode operator pembanding yang diterapkan Excel
Tiap record AUTOFILTER membawa dua DOPER 10 byte, dan byte vt menentukan apakah Excel membandingkan kriteria sebagai number, teks, Boolean atau uji blank — baca keduanya kembali lewat AutoFilterColumns sebelum menyimpan

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;
HotXLS menulis kriteria AutoFilter >=100 yang sama sebagai DOPER vtString yang tidak mencocokkan sel numerik atau sebagai DOPER vtIEEENumber dengan grbitSgn 6 yang Excel evaluasi secara numerik terhadap amount 250, kegagalan nol baris yang senyap itu yang diperbaiki CreateFilterDoper di v2.384.45
Kedua kali byte-nya valid BIFF8 — hanya byte tipe operand yang berubah, itulah sebabnya Excel membuka berkasnya, memperlihatkan kriteria di dropdown, dan tetap mencocokkan nol baris

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;
Tata letak bit wJoin HotXLS untuk kriteria AutoFilter di mana 0 menggabungkan dua DOPER dengan AND dan 1 dengan OR, garis bilangan yang memperlihatkan bagaimana konstanta tertukar pra-v2.384.18 melebarkan filter between menjadi OR yang mencocokkan segalanya, dan bentrokan penomoran xlAnd xlOr dengan Excel automation
Konstanta wJoin yang tertukar mengubah filter between menjadi yang mencocokkan setiap number, dan VBA yang diterjemahkan tetap compile karena XlAutoFilterOperator adalah Byte biasa — literal 1 yang berarti AND di COM automation berarti OR di sini

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