Teknik Makale

HotXLS ile BIFF PivotCache Kayıtlarını Çerçeveleme

BIFF PivotCache alt akışı, bir PivotTable önbelleklenmiş veri kümesini onu görüntüleyen görünümden ayrı saklar ve HotXLS o alt akışı, kayıt numaralarına güvenmek yerine kayıt gövdelerini inceleyerek okur ve yazar. Ayrımın öyküsü tam olarak budur: aynı kayıt numarası, dosyayı hangi yazıcının ürettiğine göre birbiriyle uyumsuz iki gövde düzeni taşır; okuyucu çerçevelemeyi gördüğü ilk kayıt gövdesinden karar verir

Bu katmanla, bir PivotTable'ın gidiş-dönüşten sağ çıkması gerektiği anda tanışırsınız. Önbelleksiz bir pivot görünümü bir kabuktur ve Excel dosyayı açtığında önbelleği kaynak aralıktan yeniden kurar; bu, kaynak aralığın yok olduğu, verinin bir sorgudan yapıştırıldığı ya da çalışma kitabının, biri açtığında değişmemesi gereken arşivlenmiş bir dönem kapanışı olduğu noktaya kadar sorun değildir

İki yapı, dosyada iki yer

Önbelleklenmiş veri ile önbellek tanımı, çalışma kitabının farklı kısımlarında yaşar ve ikisini birbirine karıştırmamak düzeltilecek ilk şeydir. Önbelleklenmiş kayıtlar kendi alt akışlarını oluşturur; [MS-XLS] §2.1.7.12 bunu PIVOTCACHE = SXDB SXDBEx *SXFORMULA *FDB *DBB EOF olarak verir. Neyin yok olduğuna dikkat: o üretimin başında BOF yoktur

Tanım ise bunun yerine çalışma kitabı globals'ında, PIVOTCACHEDEFINITION = SXStreamID SXVS [SXSRC] [SXADDLCACHE] (§2.1.7.20.3) olarak durur; biçimlendirme kayıtlarının ardından, BoundSheet ile Country kayıtlarından önce konumlanır. Yani tek bir önbellek, yüzlerce kayıt arayla iki yerde tarif edilir ve aralarındaki bağ, üç konumda aynı anda uyuşmak zorunda olan bir akış tanımlayıcısıdır

BIFF8de HotXLS PivotCache çerçevelemesi: SXStreamID ile birlikte PIVOTCACHEDEFINITION, biçimlendirmeden sonra ve BoundSheetten önce çalışma kitabı globalsında durur; önbelleklenmiş kayıtlar ise dört haneli büyük harf onaltılık adla _SX_DB_CUR storageı altındaki bir akışta yaşar ve BOF olmaksızın SXDB, SXDBEx, SXFORMULA, FDB ve DBB kayıtlarını taşır; SXStreamID.idStm, SXDB idstm alanı ve akış adı uyuşmak zorundadır
Tek bir pivot önbelleği, yüzlerce kayıt arayla iki yerde tarif edilir ve bunları, globals'ta, SXDB başlığında ve alt akış adında aynı anda uyuşmak zorunda olan bir akış tanımlayıcısı birleştirir

Her önbellek, _SX_DB_CUR altında bir akışa aittir; adı, tanımlayıcısının dört haneli büyük harf onaltılık yazımıdır. SXStreamID.idStm, SXDB başlığında yinelenen idstm alanı ve o akış adının üçü de eşleşmek zorundadır. Yeni bir tanımlayıcı ayırırken önce dosyadan hâlihazırda okunmuş her numarayı rezerve edin; yoksa yeni bir önbellek, okuyucunun henüz yürümediği eski bir önbelleğe ait bir numaraya sahip çıkabilir

İnsanları yakalayan bir tanımlayıcı daha var. Bir pivot görünümündeki iCache değeri, ilgili SXStreamID'nin global dizideki sıfır tabanlı konumudur, seçebileceğiniz bir önbellek tanımlayıcısı değil. Yazarken önbellek nesnesinden gerçek çıktı konumuna eşlenmek zorundadır ve mevcut görünümler de onunla birlikte yeniden numaralandırılmalıdır; yoksa bir önbelleği yükseltmek, bir görünümü sessizce başka birine yöneltir

var
  Book: TXLSWorkbook;
  Cache: TXLSPivotCache;
  Field: TXLSPivotCacheField;
  V: TXLSPivotCacheValue;
begin
  Book := TXLSWorkbook.Create(nil);
  try
    Book.LoadFromFile('sales.xls');
    Cache := Book.PivotCaches.Add;
    Cache.SourceRangeSheet := 'Data';
    Cache.SourceFirstRow := 1;  Cache.SourceFirstCol := 1;
    Cache.SourceLastRow := 500; Cache.SourceLastCol := 6;
    Cache.SourceDataType := 1;        // SXVS SHEET, MS-XLS 2.4.317
    Cache.RefreshOnLoad := False;     // önbellekteki kayıtlara güven
    Cache.SaveData := True;

    Field := Cache.AddField('Region', xlpcftString);
    V.ValueType := xlpcftString;
    V.StrValue := 'North';
    Field.FindOrAddItem(V);

    Cache.SetRecordCount(0);          // temizle, sonra kayıt ızgarasını boyutlandır
    Cache.SetRecordCount(500);
    Book.StorePivotCaches;
  finally
    Book.Free;
  end;
end;

Çift SetRecordCount batıl inanç değildir. RecordCount tahsis yapmayan sıradan bir özellik yazımıdır ve iç büyüme yolu yalnızca yeni eklenen satırları ilklendirir; sayısı başlık yoluyla kurulmuş bir önbellek, sıfır uzunlukta bir indeks ızgarasıyla kalabilir. RecordIndices'e yapılan yazımlar sonra hatasızca çöpe gider. Sayıyı sıfıra çekip geri kurmak ızgarayı yeniden tesis eder ve bu, her alan eklendikten sonra gerçekleşmek zorundadır; çünkü satır genişliği alan sayısından gelir

Bir kayıt numarası size gövde düzenini neden söyleyemez?

Çünkü kayıt numaraları ile gövde düzenleri farklı zamanlarda değişti; aralarındaki eşleme bir işlev değildir. Eski setteki bir numara yalnızca daha eski yazıcıların dosyalarında görünür, bu da onu tek yönde güvenilir bir sinyal yapar. Başka bir numara ise gerçekten belirsizdir: hem doğru dosyalarda hem de yeni numarayı eski gövde düzeniyle kullanan bir aralık ara sürümde görünür

Çerçeveleme bu yüzden gövdeden karara bağlanmak zorundadır ve kayıt başına değil, her önbellek alt akışı için bir kez. HotXLS lehçeyi, her alt akıştaki ilk SXDBB kaydının uzunluğundan sabitler. Spesifikasyon çerçevelemesinde bir SXDBB tam olarak bir önbellek kaydı taşır; uzunluğu bir satır genişliğine eşittir. Eski paketli çerçevelemede ilk kayıt, sığdığı kadar satır taşır; birden fazla satırlı her önbellek için en az iki satır genişliğidir. İki öngörü ayrıştığında karşılaştırma belirleyicidir

HotXLS SXDBB çerçeveleme kilidi: bir kayıt numarası iki uyumsuz gövde düzeni taşır; okuyucu ilk SXDBB kayıt uzunluğunu satır genişliğiyle karşılaştırır, bir satır genişliği spesifikasyon lehçesini iki ya da daha çok satır genişliği eski paketli çerçevelemeyi sabitler, beraberlikler spesifikasyon okumasını alır ve lehçe kayıt başına değil alt akış başına bir kez sabitlenir
Kayıt numaraları gövde düzenine karar veremez; çünkü ikisi farklı zamanlarda değişti. HotXLS bu yüzden lehçeyi her alt akış için bir kez, ilk SXDBB uzunluğundan sabitler ve berabere kalınca spesifikasyon okumasını alır

Ayrışmadıklarında okuyucu spesifikasyon okumasını alır; ilke, Excel'in yazdığı dosyaların ara bir derlemenin yazdıklarından çok daha fazla olduğudur. O kör nokta kurgu gereği dardır ve gerçekleştiğinde bile dosyanın kendisi bayt bayt aynen oynatılır. Yalnızca çağıranlara açılan tipli indeksler etkilenir

İndeks genişliği başka bir kayıtta yaşar

SXDBB (§2.4.276), ayrık değer bayrağı kurulmuş her önbellek alanı için alan sırasınca bir indeks taşır ve her indeksin genişliğine başka yerde karar verilir: karşılık gelen SXFDB alan kaydı (§2.4.283) bir kısa öğe bayrağı bildirir ve o bayrak, indeksin iki bayt mı bir bayt mı kapladığını söyler. İki kayıt, tek zımni sözleşme ve onları birbirine bağlayan spesifikasyondaki tek bir cümle

O eşleşme, ev yapımı bir kodlamanın tam yanıldığı yerdir. Eski bir HotXLS yazıcısı her alanı minimum bit sayısına paketliyor, satırlar arasında bayt sınırına dolduruyordu; tek başına savunulabilir ama aynı yazıcının az önce SXFDB içinde bildirdiği genişlikle doğrudan çelişiyordu. Üç ayrık değerli bir alan, bir kayıtta bir bayt genişliğinde tarif edilirken öbüründe iki bit kaplıyordu. Düzeltme aritmetiği düzeltmek değildi; genişlik kararını her iki yayıcının da çağırdığı tek bir işleve çıkarmaktı, böylece iki kayıt artık ayrışamaz. BIFF kayıt uzunluğu bildirim sapması makalesinde tarif edilen kusur sınıfı da budur: bildirilen boyut ile gerçek gövde yollarını ayırır

Bu kayıtları hiç okumamanın sonucunu açıkça söylemeye değer; hafife alınması kolaydır. Okuyucu kayıt indekslerini atladığında, dosyadan yüklenen her önbellek her satırın her alanı için indeks sıfır bildiriyordu; yani her satır, her alanın ilk değerini işaret ediyordu. Bu yalnızca azalmış içe bakış değildir: pivot değerlendirme yolu da önbellekten hücreye doldurma yolu da o ızgarayı tüketir. Ve gidiş-dönüş bir test bunu yakalayamaz; çünkü hâlâ ham oynatmadaki bir önbellek orijinal baytlarından geri yazılır

// Kaynak (provenance) bayrakları elinizde neyin olduğunu ve neyin yeniden yazılabileceğini söyler
if Cache.FromRawBlobs then
begin
  Writeln('stream id        : ', IntToHex(Cache.StreamId, 4));
  Writeln('legacy framing   : ', Cache.RawFramingIsLegacy);
  Writeln('own storage      : ', Cache.RawHasStorageStream);
  Writeln('model complete   : ', Cache.RawModelIsComplete);
  // Yeniden yayım, yalnızca her kaydın burada modeli varken kayıpsızdır
  if Cache.CanUpgradeFraming then
    Writeln('safe to rewrite with the current emitters');
end;

Bir önbelleği yeniden yazmak ne zaman kayıpsızdır?

Yalnızca üç koşul birlikte tuttuğunda ve soruyu cevaplayan tek özellik CanUpgradeFraming'dir. Önbellek hâlâ ham oynatmada olmalı, alt akış bu kütüphanenin daha önce yanlış yazdığı çerçevelemelerden birinde olmalı ve okuyucu içindeki her kaydın eksiksiz bir tipli modelini kurmuş olmalı. Excel'in yazdığı bir önbellek asla uygun düşmez; çünkü alt akışı HotXLS'in modeli olmayan kayıtlar taşır ve modelden yeniden yazım onları düşürürdü

Eksiksizlik testi ilk bakışta göründüğünden daha katıdır. Okuyucunun yalnızca opak bayt olarak tuttuğu bir kayıt, modeli eksik işaretler. Yayıcının yeniden üretemediği bildirilmiş bir formül kaydı sayısı da öyle; çünkü yeniden yazım, birkaç formül kaydı bildirimini hiçbir formül kaydı bildirimi olarak yeniden yazar ve dosyada yeniden üretilemeyen bir değer, yeniden üretilemeyen bir kayda denktir

Bilinçli muhafazakarlık yazıcıdan da geçer. İndeksler, bant dışı bir sentinel olarak kodlanmak yerine yasal aralığa kırpılır; çünkü spesifikasyon, ayrık değer dizisine bir indeks tanımlar, başka hiçbir şeyi değil ve boş bir hücre de o dizide bir değerdir. BIFF kayıt tavanını aşan bir önbellek kaydı gövdesi hiç yazılmaz; bu binlerce önbellek alanı gerektirir ve BIFF8 sütun sınırı içinde zaten ulaşılamaz; yedek, Excel'in kaynak aralıktan tazelemesidir ki bu bozuk dosya değil tanımlı davranıştır

Tarihler kayıtlar arası son bağımlılığı taşır. Seriden tarihe dönüşüm, çalışma kitabının tarih sistemine bağlıdır ve kayıt yayıcısı kitabı göremez; bu yüzden taban tarih tercihi, varsayılanı 1900 sistemi olan ve kitap düzeyindeki kayıt yolu tarafından sağlanan bir parametre olarak geçilir. 1900 sistemi altında seri numarası değerin kendisidir; 1904 sistemi 1462 gün farklıdır. Tarih serilerinin daha geniş ele alınışı, tarih serileri, 1904 sistemi ve sayı formatları makalesindedir

Önbellek katmanında değil görünüm katmanında çalışıyorsanız görünür pivotu tarif eden kayıtlar BIFF8 PivotTable kayıt seti makalesinde, hesaplama tarafı davranış ise hesaplanan alanlar, hesaplanan öğeler ve tazeleme makalesinde ele alınır. Üç katman da HotXLS Delphi spreadsheet bileşeni içinde gelir; eski bir çalışma kitabını yükleyip önbelleğinin gerçekte ne içerdiğine bakmayı ve yeniden yazmanın güvenli olup olmadığını yapmadan önce karar vermeyi mümkün kılan da budur