Teknik Makale

Delphi'de XLSB İkili Workbook Okuma ve Yazma

HotXLS, Excel Binary Workbook (XLSB) dosyalarını, XLSX için kullanılan aynı TXLSXWorkbook cephesi üzerinden Delphi'den okur ve yazar: Open, ikili workbooku paket içeriğinden saptar; SaveAs ya da xlsxExcelBinaryWorkbook ile SaveAsChecked ise bir tane yazar. Backend doğrulanmış bir alt kümeyi kapsar ve hedefe dokunmadan önce temsil edemediği her şeyi reddeder

Son cümle, yavaş okunmaya değen kısımdır. XLSB, XLSX'in basit bir türevi gibi görünür ve paketleme açısından öyledir. Sorun, iki biçimin farklı kodladığı ayrıntılarda ve bir kütüphanenin birini öbürüne köprülerken sessizce varsaydıklarında yaşar. Bu backenddeki en öğretici hata hiç bozuk bir kayıt değildi. Kaybolan bir varsayılan font ve değeri, biçim kodu ile sütun genişliği tamamen doğru olduğu hâlde Excel'de ####e dönen bir tarih sütunu oldu

XLSB dosyası nedir ve XLSX'ten nasıl ayrılır?

Bir XLSB dosyası, bir XLSX workbookunun parça yapısına tam olarak sahip bir OPC paketidir; ama workbook, worksheet, style ve shared-string parçaları XML yerine ikili kayıt saklar. Biçim [MS-XLSB] içinde tanımlanır ve kayıt kodlamasına çoğu zaman BIFF12 denir. Yine de [Content_Types].xml, relationship parçaları ve bir ZIP kabı alırsınız; değişen şey, xl/workbook.binin xl/workbook.xmlin yerine geçmesi, xl/worksheets/sheet1.binin worksheet XML'inin yerine geçmesidir ve devamı

Her BIFF12 kaydı, kompakt değişken uzunluklu bir başlıkla başlar. Kayıt türü bir ya da iki bayt alır; bayt başına yedi bit, üst bit devam bayrağı olacak şekilde; böylece bir kayıt türü 14 bite sığar. Yük boyutu, aynı yedi bit şemasıyla bir ile dört bayt arasında izler. Metinler 32 bitlik bir karakter sayısı ve ardından UTF-16 code unit'lerdir. BIFF8'e kıyasla bu çok daha temiz bir düzen: 8224 baytlık CONTINUE bölmesi yok, kayıt ortasında compressed-versus-wide string bayrakları yok. XLSX'e kıyasla XML tokenizasyonunu tamamen atlar; büyük XLSB dosyalarının diskte daha küçük olma ve Excel'de daha hızlı açılma eğiliminin nedeni budur

Bir paketleme ayrıntısı saptama kodunu sendeletir. Excel, XLSB workbook parçasını application/vnd.ms-excel.sheet.binary.macroEnabled.main content type'ı ile kaydeder; makro içermeyen workbooklar için bile. HotXLS o kaydı önce [Content_Types].xml içinde denetler ve content type yokken yalnızca .bin parça uzantısına döner; her iki yol da yine katı kayıt doğrulayıcısından geçmek zorundadır. Parça adları, relationship'ler ve content type'lar hakkındaki genel kurallar, Excel'in geçerli bir XLSX'i neden onardığı ve ardındaki OPC paket kuralları yazısında ele alınan kuralların aynısıdır

XLSX yerine XLSB'yi ne zaman seçmelisiniz?

Akıştaki bir sistem XLSB istiyorsa ya da açılış süresini önemseyen Excel kullanıcılarıyla büyük, değer yoğun workbooklar alışverişi yapıyorsanız XLSB'yi seçin; aksi hâlde XLSX daha güvenli varsayılan olarak kalır. XLSX, geniş araç desteği olan bir ISO standardıdır (ISO/IEC 29500), bir ZIP aracıyla ve bir metin editörüyle incelenebilir ve her spreadsheet ürünü onu okur. XLSB, yayımlanmış bir şartnamesi olan ama daha dar bir ekosisteme sahip bir Microsoft biçimidir

Pratikte talepler iki yönden gelir. Bazı raporlama hatları ve arşivler yıllar önce .xlsbde standardize oldu ve başka her şeyi reddediyor. Diğer ekipler yüz binlerce sayısal hücreli workbooklar üretiyor ve kullanıcıların ikili sürümü belirgin biçimde daha hızlı açtığını gördü. İkisi de meşru neden. "Daha verimli geliyor" değil; çünkü HotXLS ikili backendi bütün paketi belleğe alır, dolayısıyla sınırlı bellekli sunucu dışa aktarımları için araç değildir. Onlar için doğru yol, akış yapan XLSX yazıcısıdır

TXLSXWorkbook ile XLSB açma ve kaydetme

TXLSXWorkbook.Open biçim ipucu istemez: aynı çağrı XLSX, XLSM ya da XLSB yükler ve başarılı bir ikili yüklemeden sonra TXLSXWorkbook.SourceFormat, xlsxExcelBinaryWorkbook bildirir. Yazma açıktır. Biçimsiz bir SaveAs(FileName) her zaman bir Open XML workbooku üretir; dosya adı .xlsb ile bitsin bile. Bu yüzden ikili çıktı biçimi daima adıyla söyler

uses
  SysUtils, lxHandleX;

procedure ConvertToXlsb(const Source, Target: string);
var
  Book: TXLSXWorkbook;
begin
  Book := TXLSXWorkbook.Create;
  try
    // Open, XLSX, XLSM ya da XLSB'yi paketin kendisinden saptar
    if Book.Open(Source) <> 1 then
      raise Exception.Create('Input is unsupported or invalid');

    // Yalnızca uzantı asla ikili çıktıyı seçmez
    if Book.SaveAsChecked(Target, xlsxExcelBinaryWorkbook) <> 1 then
      raise Exception.Create('XLSB output is unsupported or failed');
  finally
    Book.Free;
  end;
end;

Akış çıktısı SaveAs(Stream, xlsxExcelBinaryWorkbook) üzerinden ya da SaveAsCheckedin akış overload'undan geçer. İçeride ikisi de özel SaveAsXlsbStream ve SaveAsXlsbFile metotlarına yönlenir; onları asla doğrudan çağırmazsınız. Dosya çıktısı, kardeş bir geçici dosyaya hazırlanır ve tek bir son değişimle taahhüt edilir. Bir akış hedefi okunabilir ve aranabilir olmalıdır; çünkü yazıcı baytlarının, boyutunun ve konumunun anlık görüntüsünü alır ve son kopya başarısız olursa onları geri yükler. TXLSXWorkbook.LastSaveUsedStreaming, ikili bir kayıttan sonra False bildirir; bu isabetlidir: tek bir bayt hedefinize ulaşmadan önce bütün paket bellekte durur

Tam sadakat yerine neden doğrulanmış bir alt küme?

HotXLS, XLSB için ikinci bir nesne modeli tutmaz. Açarken XlsbToOpenXml, ikili paketi bellek içi bir Open XML paketine çevirir ve sıradan XML ayrıştırıcısı onu yükler; kaydederken XML yazıcısı bir paket kurar ve OpenXmlToXlsb onu ikili kayıtlar olarak kodlar. Böylece her özellik iki çeviriden sağ çıkmak zorundadır ve köprünün tam olarak çeviremediği bir özellik, yaklaştırılmak yerine reddedilir

HotXLS 2.384.113 itibarıyla desteklenen küme, çoğu veri workbooku için yeterince geniştir: tipli değerler, shared ve inline string'ler, yaygın fontlar, dolgular, kenarlıklar ve sayı biçimleri, satır yükseklikleri ve sütun genişlikleri, önbelleklenmiş sonuçlu sıradan hücre formülleri, defined name'ler, birleştirilmiş hücreler, hyperlink'ler, donmuş ve bölünmüş pencereler, basit özel ölçütlü AutoFilter aralıkları ve sıradan worksheet tabloları. Dışında ise shared ve array formülleri, future ve kullanıcı tanımlı fonksiyonlar, dış workbook referansları, grafikler ve çizimler, PivotTable'lar ve slicer'lar, VBA, koşullu biçimlendirme, veri doğrulama ve elle hesaplama ya da yineleme gibi varsayım dışı hesaplama ayarları durur

Reddetme zorunludur, öneri değil. TXLSXWorkbook.GetConversionReport(xlsConversionExcelBinaryWorkbook), kaydetmeden önce desteklenmeyen özellikleri listeler ve o girişler, AAllowLossy = True ile ya da denetimsiz bir SaveAs ile bile kaydı düşürür. Bu bilinçli bir tercihtir. Bir ikili kopyadan PivotTable'ı sessizce düşürmek, workbook müşterinin karşısına gelene kadar kimsenin fark etmediği türden bir kayıptır

uses
  Classes, lxHandleX, lxDiagnostics;

function CanSaveAsXlsb(Book: TXLSXWorkbook; Log: TStrings): Boolean;
var
  Report: TXLSConversionReport;
  I: Integer;
begin
  Report := Book.GetConversionReport(xlsConversionExcelBinaryWorkbook);
  try
    Result := True;
    for I := 0 to Report.Count - 1 do
      if Report[I].Fidelity = xlsConversionUnsupported then
      begin
        Result := False;
        Log.Add(Report[I].SheetName + ' ' + Report[I].Location + ': ' +
          Report[I].Feature + ' - ' + Report[I].Message);
      end;
  finally
    Report.Free;   // raporun sahibi çağırandır
  end;
end;

Rapor canlı modeli kapsar. Son paket doğrulaması hâlâ, modelin hiç görmediği şeyler için son kapıdır; kaynak dosyadaki bozuk kayıtlar ya da tanıdık olmayan öznitelikler gibi. Bu yüzden temiz bir raporu başarısız bir kaydın izlemesi mümkündür ve üzerinde hareket edeceğiniz şey kayıt sonucudur

Varsayılan font ve 0.00 biçimi neden kayboldu?

Varsayılan font ve yerleşik sayı biçimleri, XML style okuyucusunun HotXLS'nin kendi yazdığı dosyalar için geçerli ama Excel'in yazdığı workbooklar için geçerli olmayan iki varsayım yapması yüzünden kayboldu. Köprü her iki kaynağı da kusursuz korudu; kayıp bir kat yukarıda, genel Open yolunda oldu. O boşluk bu backendin ana dersidir: doğru bir biçim köprüsü, sadık bir genel gidiş dönüşü garantilemez

İlk varsayım fonts[0]ile ilgiliydi. HotXLS XLSX kaydettiğinde sıfır numaralı yuvaya örtük bir varsayılan font yazar ve XML okuyucusu o yuvayı atlar; böylece kullanıcıya bakan Fonts koleksiyonu sıfır indeksinden başlar. Excel'in yazdığı bir XLSB o yuvada kendi açık varsayılanını taşır ve onu atlamak o seçimi çöpe attı. İkinci varsayım sayı biçimleriyle ilgiliydi. Okuyucu yalnızca özel numFmtId değerlerini (164 ve üzeri) NumberFormatsa eşliyordu; yerleşik id'ler sıfır olarak kalıyordu. Yerleşik biçim 2 taşıyan, yani 0.00 olan biçimli bir boş hücre General olarak geri dönüyordu

Düzeltmenin tamamı ikili içe aktarma yolunda durur. Bütün paket preflight'ı geçtikten sonra XLSB açılışı, özel bir style modunu yalnızca eşzamanlı XML ayrıştırması için ayarlar. O modda okuyucu, sıfır numaralı yuva dâhil her fontu yükler ve her fontIdyi bir kaydırır; desteklenen her yerleşik id'yi modelde açık bir biçim koduna çevirir ve tarih saptaması için yine özgün numFmtIdyi kullanır; böylece yerleşik bir tarih biçimi tarih semantiğini korur. Bir finally bloğu modu her zaman temizler ve sonraki sıradan bir XLSX ya da ODS Openi saklanan varsayılan-font durumunu da temizler

fonts[0] Excel sütun genişliklerini nasıl etkiler?

Excel, sütun genişliklerini workbookunun varsayılan fontundan, fonts[0]tan hesaplar; o sütundaki hücrelere uygulanan fontlardan değil. Sütun widthi, varsayılan fontun azami rakam genişliğinin karakter birimlerinde saklanır; bu yüzden fonts[0]ı değiştirmek, hücrelerinin hepsi başka bir font kullanan sütunlar dâhil her sütunun piksel genişliğini değiştirir

Bu, bütün araştırmanın en kafa karıştırıcı belirtisini üretti. Yeniden kaydedilen workbookta hücre değerleri, biçim kodları ve width öznitelikleri birebir aynıydı; hücre fontu ile Normal style korunmuş fonta işaret ediyordu ve Excel yine de bir tarih sütununu #### olarak render ediyordu. Yazılan fonts[0] Calibri 11'e dönmüştü, azami rakam genişliği daralmıştı ve tarihler artık sığmıyordu. HotXLS artık kaynak varsayılan fontunu korur ve onu fonts[0] olarak geri yazar; Calibri 11'e dönüş yalnızca hiç ikili kaynak görmemiş workbooklar içindir. Genişlik aritmetiğinin tamamı Delphi'de Excel sütun genişliği ve max digit width (MDW) yazısında

Yerele bağlı yerleşik biçimler tahmin edilmez, reddedilir

Açık bir biçim kodu taşımayan yerleşik bir sayı biçimi id'si yalnızca anlamı sabitken kabul edilir; yerele bağlı id'ler paket preflight'ında reddedilir. HotXLS, 0 ile 22, 37 ile 40 ve 45 ile 49 arası id'leri 0.00, 0%, mm:ss ve @ gibi bilinen kodlara eşler. 164 altındaki kalan id'ler, render'ı Excel kurulumunun bölgesel ayarlarına bağlı biçimlere aittir ve içe aktarma onları "Yerele bağlı bir sayı biçimi açık bir biçim kodu ister" mesajıyla reddeder

O id'ler için bir kod uydurmak, başarısız olmaktan daha kötü olurdu. Tahmin edilmiş bir Doğu Asya tarih deseni makul görünür, hatasız gidiş dönüş yapar ve kullanıcılarınızın yarısı için yanlış şeyi gösterir. Zamanlama da önemlidir: denetim, bütün paket preflight'ında, mevcut workbook temizlenmeden önce çalışır; böylece reddedilen bir dosya, önceden yüklenmiş modeli, dosya adını ve kaynak biçimini dokunulmamış bırakır. Excel date serial ve numFmt yazısı, yerleşik tarih id'lerinin 1900 ve 1904 tarih sistemleriyle nasıl etkileştiğini anlatır

procedure LoadBinaryWorkbook(Book: TXLSXWorkbook; const FileName: string);
var
  I: Integer;
begin
  // İkili girdi tam yüklemeyi baştan ister; seçici modlar en başta reddeder
  Book.MetadataOnly := False;
  Book.SelectedSheets.Clear;

  if Book.Open(FileName) <> 1 then
  begin
    for I := 0 to Book.Diagnostics.Count - 1 do
      Writeln(Book.Diagnostics[I].Message);
    Exit;   // önceden yüklenmiş workbook hâlâ sağlam
  end;

  // Kayıt yeniden hesaplamıyorsa formül çıktısı bir önbellek ister
  Book.RecalcBeforeSave := xrmAlways;
end;

Üretim kodunda XLSB için pratik kurallar

İkili backend, reddetmelerini bastırılacak hatalar değil veri gibi ele aldığınızda güvenle yaslanılabilir bir şeydir. Yukarıdaki davranıştan birkaç kural çıkar

  • Her ikili kayıtta biçimi adıyla söyleyin; yalnızca .xlsb uzantısı hâlâ XLSX üretir
  • Kullanıcı düzenlemeli workbookları kaydetmeden önce GetConversionReport(xlsConversionExcelBinaryWorkbook) çalıştırın ve desteklenmeyen özellikleri kullanıcıya görünür kılın
  • İkili girdi için MetadataOnlyyi kapalı ve SelectedSheetsi boş bırakın; çünkü tembel yükleme, çevrilmiş bir arşivi saklamayı gerektirirdi
  • Formüllere kaydetmeden önce bir önbelleklenmiş sonuç verin ya da yeniden hesaplayın; çünkü eksik önbellek, xrmManual altında ve temiz bir xrmIfDirty altında reddedilir
  • Belleği, çevrilmiş bütün paket artı ikili çıktı için bütçelendirin; belleğin sınırlı kalması gerekiyorsa akış yapan XLSX'i kullanın

Test ederken Excel'le köprü seviyesinde değil, genel API üzerinden karşılaştırın. Varsayılan-font ve 0.00 gerilemeleri ikisi de köprü testlerini geçti ve ikisi de Open ile SaveAsChecked üzerinden açılıp yeniden kaydedilen Excel yazımı bir workbookta hemen ortaya çıktı

FAQ

HotXLS, XLSB dosyalarını otomatik saptar mı?

Evet. TXLSXWorkbook.Open, workbook parçası için kaydedilmiş content type'a bakar ve ikili kayıtları doğrular; böylece aynı çağrı hem XLSX hem XLSB yükler ve SourceFormat hangisinin okunduğunu bildirir

.xlsb dosya adıyla SaveAs neden hâlâ XLSX yazar?

HotXLS çıktı biçimini asla uzantıdan çıkaramaz. İkili bir workbook yazmak için SaveAs ya da SaveAsCheckede xlsxExcelBinaryWorkbook geçirin

Bir workbook, XLSB alt kümesinin dışındaki özellikleri kullandığında ne olur?

Kayıt, kayıplı kayda izin verilmiş olsa bile hedef değişmeden önce başarısız olur. GetConversionReport, engelleyici özellikleri önceden listeler ve Diagnostics, başarısızlığı sonradan açıklar

XLSB desteği, conversion report ve workbook API'sinin geri kalanı yerel Delphi ve C++Builder spreadsheet componenti olan HotXLS içinde gelir