Artikel Teknis

Baca Nilai Cached Formula Excel di Delphi tanpa Recalc

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

Record HotXLS TXLSFormulaCacheInfo menjaga tiga fakta ortogonal tentang satu sel formula tetap terpisah: State asal-usul melintasi lima kasus, Kind payload melintasi enam, dan Variant Value, sehingga cache kosong atau False tak pernah keliru dianggap cache yang absen
Asal-usul, tipe payload, dan nilai payload tetap terpisah, satu-satunya cara cache kosong, nol, string kosong, atau error bisa dilaporkan sebagai nilai aslinya
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

Field delapan byte FormulaValue dari record Formula XLS klasik sebagaimana HotXLS membacanya: double IEEE 754 kecuali high word sama dengan FFFF, saat itu tipe variabel duduk di val nol dan payload Boolean atau error di val dua
Saat high word adalah FFFF, field itu variabel bertag, dan payload duduk di val[2] dengan val[1] tak terdefinisi, persis byte yang dulu diambil pembaca

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

Pandangan HotXLS atas grup shared formula OOXML yang atribut si-nya hanya berbagi ekspresi dan tata letak penyimpanan, sementara setiap sel anggota memiliki nilai cache-nya sendiri, sehingga pengikut yang termuat tanpa nilai terus melaporkan xlfcsMissing
Grup berbagi ekspresi, bukan angkanya, jadi cache akar tak pernah disebarkan dan anggota yang datang tanpa nilai terus melaporkan celah itu

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