HotXLS, pustaka Excel native Delphi dan C++Builder, membaca nilai yang sudah Excel simpan di samping sebuah formula lewat TryGetCachedFormulaValue dan IXLSFormulaCacheReader. Tak satu pun titik masuk memanggil kalkulator, men-decompile token formula, memperbarui state kotor, atau menulis apa pun kembali ke model, sehingga workbook yang hanya Anda baca tetap persis seperti saat Anda membukanya
Skenario yang menggerakkan ini membosankan dan sangat umum. Pekerjaan malam membuka beberapa ratus workbook buatan orang lain, menarik satu kolom total dari masing-masing, dan mendorong angkanya ke gudang data. Totalnya sudah duduk di dalam file — Excel menghitungnya dan menyimpannya. Namun begitu pekerjaan itu meminta nilai dari sel formula, pustaka yang hanya punya satu jawaban untuk pertanyaan itu membangun graf dependensi dan mengevaluasi seluruh sheet, dan pekerjaan yang semestinya terikat I/O berubah menjadi benchmark perhitungan
Mengapa membaca sel formula biayanya perhitungan ulang penuh?
Karena getter nilai pada sel formula adalah permintaan untuk menghasilkan nilai, dan satu-satunya cara yang benar secara universal untuk menghasilkannya adalah mengevaluasi formula. Itu bawaan yang tepat untuk aplikasi yang menyunting workbook, dan bawaan yang salah untuk pipeline yang mengekstraknya. Lebih buruk lagi, evaluasi tidak bebas efek samping: ia menulis hasil kembali ke sel, membalik flag kotor, dan bisa ter-resolve berbeda dari aplikasi penghasilnya ketika sebuah fungsi tak didukung atau referensi eksternal putus. Pekerjaan yang Anda gambarkan ke tim operasional sebagai baca-saja diam-diam menghasilkan workbook yang tak lagi cocok dengan yang di disk, dan jika nanti ada yang menyimpannya, file di disk ikut berubah
Membaca nilai cache adalah separuh lain kontraknya. Ia menjawab pertanyaan yang lebih sempit — apa yang disimpan aplikasi penghasil di sini? — dan menolak menjawab apa pun yang lain. Saat Anda sungguhan menginginkan angka segar, HotXLS tetap memberi Anda perhitungan ulang inkremental yang digerakkan graf dependensi; intinya ekstraksi dan evaluasi seharusnya dua panggilan berbeda, bukan satu panggilan dengan dua suasana
Tiga fakta ortogonal tentang satu sel
Kesimpulannya dulu: sebuah nilai cache formula membawa tiga fakta independen, dan melunturkannya menjadi satu Variant kehilangan informasi yang Anda butuhkan. TXLSFormulaCacheInfo menjaganya terpisah sebagai State, Kind, dan Value. TXLSFormulaCacheState mencatat asal-usul melintasi lima kasus — xlfcsNotFormula, xlfcsMissing, xlfcsLoaded, xlfcsCalculated, dan xlfcsInvalidated — sementara TXLSFormulaCacheValueKind mengklasifikasi payload sebagai xlfcvBlank, xlfcvNumber, xlfcvDateTime, xlfcvString, xlfcvBoolean, atau xlfcvError. Pemisahan inilah yang membuat kehadiran bisa dilaporkan dengan jujur: cache kosong, cache string kosong, cache False, cache nol, dan cache error semuanya nilai nyata, sehingga kehadiran tak pernah boleh disimpulkan dari VarIsEmpty atau VarIsNull. TryGetCachedFormulaValue mengembalikan True hanya untuk xlfcsLoaded dan xlfcsCalculated, dan tetap mengisi keadaan yang bisa didiagnosis saat mengembalikan False
var
Book: TXLSXWorkbook;
Info: TXLSFormulaCacheInfo;
begin
Book := TXLSXWorkbook.Create;
try
Book.Open('quarterly-model.xlsx');
// SheetIndex, Row and Col are all 1-based here
if Book.TryGetCachedFormulaValue(1, 12, 5, Info) then
Writeln('cached value: ', VarToStr(Info.Value))
else
Writeln('no usable cache, state ordinal ', Ord(Info.State));
finally
Book.Free;
end;
end;
Mengapa nilai cache itu hilang?
Ada tepat empat alasan TryGetCachedFormulaValue mengembalikan False, dan keadaannya memberi tahu yang mana yang berlaku. xlfcsNotFormula berarti sel memuat literal atau tak ada apa pun, dan koordinat yang keluar rentang meluntur ke jawaban yang sama. xlfcsMissing berarti sel itu sungguhan formula tetapi penghasilnya tak menyimpan payload nilai untuknya — hasil umum ketika sebuah generator menulis formula dan membiarkan Excel mengisinya saat pertama dibuka. xlfcsInvalidated berarti teks formula diganti setelah load, sehingga nilai yang dulu ada di sana menggambarkan ekspresi yang tak lagi ada. xlfcsCalculated, sebaliknya, adalah kasus sukses: ia menandai nilai yang kode Anda sendiri atau evaluator HotXLS hasilkan pada sesi ini, berlawanan dengan xlfcsLoaded yang datang dari file
Kejujuran tentang cache yang hilang lebih penting daripada menambalnya. HotXLS menolak mengarang nilai, dan saat menyimpan sama ketatnya — hanya xlfcsLoaded dan xlfcsCalculated yang menulis nilai cache, sedangkan xlfcsMissing dan xlfcsInvalidated menulis formula saja alih-alih membekukan angka basi ke dalam file. Itu menyisakan tiga respons waras dalam sebuah pipeline: lewati barisnya dan catat celahnya, hitung ulang workbook itu satu secara sengaja dan terima biayanya, atau evaluasi dan rekonsiliasi. Jika angka hasil evaluasi tak sepaham dengan yang akan ditulis aplikasi penghasil, tracer evaluasi formula adalah alat untuk menemukan di mana dua perhitungan itu menyimpang, alih-alih menebak dari hasilnya
Satu pembaca melintasi mesin klasik, OOXML, dan ODF
Pipeline tidak seharusnya peduli apakah file yang baru dibukanya BIFF, OOXML, atau ODF. IXLSFormulaCacheReader adalah satu-satunya titik masuk baca-saja untuk ketiganya: baik TXLSWorkbook.CreateFormulaCacheReader maupun TXLSXWorkbook.CreateFormulaCacheReader mengembalikan adapter ringan di atas pencarian sel sparse yang sudah dipakai tiap mesin, dengan koordinat sheet, baris, dan kolom 1-based yang identik. Kelas workbook sengaja tidak mengimplementasikan interface itu sendiri — referensi interface ke workbook akan mengubah semantik kepemilikannya dan membiarkan pemanggil menyelinap lewat lease masa hidup. Sebagai gantinya, menghancurkan workbook mengosongkan pointer mentah di dalam lease itu, dan pembaca yang masih dipegang kode Anda melempar EXLSFormulaCacheReaderInvalidated pada kueri berikutnya alih-alih me-dereferensikan memori yang sudah bebas. Itu pemeriksaan masa hidup fail-fast, bukan jaminan konkurensi
var
Reader: IXLSFormulaCacheReader;
Info: TXLSFormulaCacheInfo;
Row, Missing, Errors: Integer;
Total: Double;
begin
Reader := Book.CreateFormulaCacheReader;
Total := 0;
Missing := 0;
Errors := 0;
for Row := 2 to LastRow do
if Reader.TryGetCachedFormulaValue(1, Row, 7, Info) then
begin
case Info.Kind of
xlfcvNumber: Total := Total + Double(Info.Value);
xlfcvError: Inc(Errors);
end;
end
else if Info.State = xlfcsMissing then
Inc(Missing);
// No calculator ran, no dirty flag moved, Book is unchanged
end;
Di mana byte cache sebenarnya tinggal
Untuk file .xls klasik, cache-nya adalah field FormulaValue dari record Formula, delapan byte yang dideskripsikan [MS-XLS] §2.5.133. Saat high word sama dengan $FFFF, payload-nya bukan double IEEE 754 melainkan variabel bertag, dan tata letaknya mudah salah secara halus: tipe variabel duduk di val[0] dan payload boolean atau BErr duduk di val[2], dengan val[1] tak terdefinisi. HotXLS sebelumnya membaca payload dari val[1], jenis off-by-one yang hanya muncul di file spesifik yang men-cache boolean atau error alih-alih angka. Pembaca dan penulis shared-formula kini sepaham pada offset yang sama, sehingga cache TRUE selamat dari muat dan simpan utuh alih-alih meluruh menjadi noise
Kesetiaan tipe di format paket adalah masalah terpisah dengan jebakannya sendiri. Di OOXML nilai cache tergantung pada elemen c sebagai <v>, dengan atribut t menyebutkan tipenya per ECMA-376 Part 1 §18.3.1.4. HotXLS membaca t="e" langsung menjadi Variant varError dan memetakannya kembali ke teks error standar saat menyimpan, sehingga error tak pernah menyamar sebagai integer biasa — tetapi RTL Delphi tak membantu Anda di sini, karena VarAsType(Integer, varError) melempar exception konversi. Konstruksi yang bekerja mengatur TVarData.VType dan TVarData.VError secara langsung. Tanggal mengikuti disiplin yang sama ke arah sebaliknya: t="d" dan tipe nilai tanggal ODF adalah deklarasi tipe eksplisit dan menjadi varDate, sementara cache numerik BIFF tak membawa flag tanggal sama sekali dan karena itu tetap Double. HotXLS tak pernah menebak tanggal dari format angka sel, karena format angka itu presentasi dan cache itu data. ODF menambah satu kasus lagi yang layak diketahui — office:value-type="void" mengekspresikan cache yang hadir tetapi tak membawa nilai, dan karena ODF tak punya tipe nilai error, teks yang tampak error dipertahankan sebagai teks alih-alih dipromosikan menjadi error
function DescribeCache(const Info: TXLSFormulaCacheInfo): string;
begin
case Info.State of
xlfcsNotFormula: Result := 'not a formula cell';
xlfcsMissing: Result := 'formula stored with no cached value';
xlfcsInvalidated: Result := 'formula replaced since load';
else
case Info.Kind of
xlfcvError: Result := 'error code ' + IntToStr(TVarData(Info.Value).VError);
xlfcvDateTime: Result := DateTimeToStr(VarToDateTime(Info.Value));
xlfcvBoolean: Result := BoolToStr(Info.Value, True);
xlfcvNumber: Result := FloatToStr(Double(Info.Value));
xlfcvString: Result := VarToStr(Info.Value);
else
Result := 'present but blank';
end;
end;
end;
Apakah shared formula berbagi nilai cache-nya?
Tidak, dan mengasumsikan sebaliknya adalah cara satu pemeriksaan berakhir melaporkan angka yang sama untuk seluruh kolom. Sebuah shared formula OOXML berbagi ekspresi formula dan optimasi penyimpanannya saja; setiap sel anggota tetap memiliki <v>-nya sendiri. HotXLS karena itu tak pernah menyebarkan cache anggota akar ke pengikut yang datang tanpa nilai, dan pengikut yang termuat sebagai xlfcsMissing tetap melaporkan xlfcsMissing setelah simpan dan buka ulang. Jika Anda sedang menggarap cara grup itu disimpan dan diuraikan semula, mekanika atribut si shared formula dan uraiannya dibahas terpisah; untuk pembacaan cache, aturannya mereduksi menjadi satu baris — tanyakan setiap sel, percayai tak satu pun yang tak Anda minta
Pembacaan nilai cache, pembaca lintas-mesin terpadu, dan mesin perhitungan ulang yang bisa Anda pilih tidak memanggilnya semuanya dikirim dalam HotXLS Delphi Spreadsheet Component standar untuk Delphi dan C++Builder, tanpa ketergantungan pada Excel atau server otomatisasi OLE mana pun; halaman produk membawa referensi API lengkap untuk titik masuk workbook dan pembaca yang ditampilkan di sini