Teknik Makale

HotXLS ile Yalnızca Üst Veri ve Seçmeli XLSX Yükleme

"Bu çalışma kitabı hâlâ TaxRate adında bir ad tanımlıyor mu?" sorusunu bir XLSX paketinin küçük bir parçası yanıtlar. Sayfa adları, gizli ya da çok gizli durumları ve çalışma kitabı düzeyindeki her tanımlı ad xl/workbook.xml'de yaşar (ECMA-376 Bölüm 1, §18.2); genellikle birkaç kilobayttır. Hücreler başka yerdedir — sayfa başına bir xl/worksheets/sheetN.xml parçası artı bir paylaşılan dize tablosu — ve baytların neredeyse tamamı oradadır. Bir şey sorabilmenizden önce tam çalışma kitabını kurmakta ısrar eden bir yükleyici, ilk birkaç kilobaytın hallettiği bir soruyu yanıtlamak için klasördeki her dosya için hepsini açar ve ayrıştırır

Önemli ayrım, bir çalışma kitabının şeklini okumak ile ağırlığını okumak arasındadır. Şekilden sonra durabilen, seçilmiş bir sayfa alt kümesi için ağırlığı yükleyen ve bir sayfayı sonra çekebilen bir yükleyici, "bir çalışma kitabını aç"ı her şeyi ya da hiçbir şeyi yapan bir adımdan, kapsamını çağrı başına belirlediğiniz bir şeye çevirir. Beraberinde iki tuzak da getirir: biri indeks tabanları, öteki kaynak dosyanın ne kadar süre yerinde kalması gerektiği üzerine

Yalnızca üst verili bir Open neyi okur ve nerede durur

TXLSXWorkbook.MetadataOnly, Open'dan önce True ayarlandığında HotXLS çalışma kitabı parçasını, stilleri, temayı, tanımlı adları, sayfa adlarını ve görünürlük durumlarını, bağlantıları, dış bağlantıları ve docProps/core.xml ile docProps/app.xml'den gelen belge özelliklerini ayrıştırır. Sonra her çalışma sayfasının hücre XML'ini ve sayfa başına uydu parçalarını atlar: yorumlar, çizimler ve tablolar. Paylaşılan dize tablosu da atlanır; kulağa geldiğinden daha önemli bir şeydir, çünkü metin ağırlıklı çalışma kitaplarında o tek parça boyut olarak sayfalarla yarışabilir

Geri dönen çalışma kitabı yapısal olarak eksiksizdir. Sheets.Count doğrudur, her sayfa gerçek adına ve Visibility'sine sahiptir, DefinedNames doldurulmuştur, ExternalLinks her bağlı hedefi listeler ve Title, Author ile LastModifiedBy dosyanın bildirdiğini taşır. Her sayfanın tek farkı sıfır hücreli olmasıdır

HotXLS MetadataOnly, bir Delphi XLSX arşivinde çalışma kitabı özelliklerini, sayfa adlarını, stilleri ve bağlantıları okur, sonra herhangi bir çalışma sayfası hücre XML'inden önce durur
MetadataOnly arşivin her yapısal parçasını okur ve çalışma sayfası hücre XML'inden önce durur. Delphi çalışma kitabı, sayfa adları, görünürlük, tanımlı adlar ve stillerle sıfır hücreyle geri döner

Tam olarak bir denetimin çalıştığı veri budur. Hangi çalışma kitapları bir sayfayı gizliyor? Hangileri hâlâ TaxRate'i tanımlıyor ve neye? Hangileri geçen yıl hizmetten çekilmiş bir dosya paylaşımına bağlanıyor? Aşağıdaki tarayıcı üçünü de tek bir dosya için yanıtlar. Yan yana duran iki indeksleme kuralına dikkat edin: Sheets[], XLS motoruyla aynı olan 1 tabanlıdır; DefinedNames ile ExternalLinks ise sıradan 0 tabanlı listelerdir

procedure ScanWorkbook(const FileName: WideString; Report: TStrings);
var
  Workbook: TXLSXWorkbook;
  Name: TXLSXDefinedName;
  I: Integer;
begin
  Workbook := TXLSXWorkbook.Create;
  try
    Workbook.MetadataOnly := True;           // Open tarafından okunur, o yüzden önce ayarlayın
    if Workbook.Open(FileName) <> 1 then
    begin
      Report.Add(FileName + ': open failed');
      Exit;
    end;
    for I := 1 to Workbook.Sheets.Count do   // Sheets[] 1 tabanlıdır
      case Workbook.Sheets[I].Visibility of
        xlsxSheetHidden:
          Report.Add(FileName + ': hidden sheet ' + Workbook.Sheets[I].Name);
        xlsxSheetVeryHidden:
          Report.Add(FileName + ': very hidden sheet ' + Workbook.Sheets[I].Name);
      end;
    Name := Workbook.DefinedNames.FindByName('TaxRate');
    if Name <> nil then
      Report.Add(FileName + ': TaxRate = ' + Name.Formula);
    for I := 0 to Workbook.ExternalLinks.Count - 1 do   // 0 tabanlı liste
      Report.Add(FileName + ': links to ' + Workbook.ExternalLinks[I].Target);
  finally
    Workbook.Free;
  end;
end;

Tarayıcı, Boolean Visible yerine bilerek Visibility'yi okur. Visible, iki gizli durum için de False'tur ve çok gizli bir sayfa, bir denetimin en çok işaretlemek istediği sayfadır; Excel'in Göster iletişim kutusu onu listelemez ve onu geri getiren tek şey VBA ya da dosyaya doğrudan bir müdahaledir

Kırk sayfanın ikisini yükleme

SelectedSheets, çalışma kitabının sahibi olduğu bir TStringList'tir. En az bir ad tuttuğunda Open, hücre XML'ini yalnızca adını verdiği sayfalar için ayrıştırır; diğer her sayfa doğru adı ve görünürlüğüyle, hücresiz oluşturulur. Öntanımlı olan boş bir liste her sayfayı yükler. Adlar, listenin kendi IndexOf'u ile aranır; öntanımlı ayarlarla eşleşme büyük/küçük harfi yok sayar ki bu, bir çalışma kitabındaki iki sayfa adının yalnızca harf büyüklüğüyle ayrışamayacağı yönündeki Excel kuralıyla örtüşür

Workbook := TXLSXWorkbook.Create;
Workbook.SelectedSheets.Add('Summary');
Workbook.SelectedSheets.Add('Q3 Detail');
if Workbook.Open('consolidated-2026.xlsx') = 1 then
  ShowSheet(Workbook.Sheets[1]);   // ShowSheet sizin kendi UI kodunuz

İki anahtar yalnızca tek yönde etkileşir. MetadataOnly = True seçimi geçersiz kılar ve tüm sayfaları atlar; ikisi birden ayarlı bir çalışma kitabı hiçbir yerde hücresiz döner ve seçim, aslında yalnızca üstüne rağmen bozuk gibi görünür. Her Open için birini kullanın: keşif için yalnızca üst veri, hedefli iş için bir seçim

Seçmeli yükleme, ParallelParse açıkken paralel ayrıştırma aşamasından da geçer; seçilmemiş sayfalar işçi işlerinin içinde atlanır, böylece iş parçacıkları zamanını istediğiniz sayfalara harcar. Aynı iki özellik, kaynak ikili bir .xlsb çalışma kitabı olduğunda da geçerlidir; tek bir kapsam kuralıyla: VBA projesi taşıyan bir .xlsb, bu iki seçenek olmadan açılmak zorundadır; herhangi biri ayarlıyken Open, seçmeli okumayı desteklenmiyor olarak bildirir

HotXLS SelectedSheets, Delphi'de kırk sayfa içinden iki adlandırılmış sayfanın hücrelerini ayrıştırırken LoadSheet, değişmemiş kaynak dosyadan atlanmış bir sayfayı somutlaştırır
SelectedSheets hücre XML'ini yalnızca adı geçen sayfalar için ayrıştırır; kalanını ad ile görünürlüğü koruyan boş taslaklar olarak bırakır. LoadSheet bir taslağı sonra somutlaştırır — kaynak dosya diskte duruyorsa ve değişmemişse

LoadSheet(1) neden yanlış sayfayı yükler?

Çünkü LoadSheet 0 tabanlı bir konum alırken Sheets[] 1 tabanlıdır. LoadSheet(1), çalışma kitabındaki ikinci sayfayı somutlaştırır ve hücreleri hâlihazırda olan bir sayfayı yüklemek True döndüren bir no-op olduğundan, karışmış bir indeks, kullanıcının tıkladığı sayfa boş kalırken başarı gibi görünebilir. Dönüşüm, kodunuzda tam olarak tek bir yerde bulunur

İkinci tuzak dosya ömrüdür. LoadSheet, çağrılar arasında arşivi açık tutmaz. Özgün dosyayı adıyla yeniden açar, çalışma kitabı parçasını, stilleri ve paylaşılan dizeleri ayrıştırır, sonra istenen çalışma sayfasını ayrıştırır. O dosyaya güvenmeden önce HotXLS, boyutunu ve son yazma zamanını Open anında yakalanan değerlerle karşılaştırır; bu arada başka bir süreç çalışma kitabının üzerine kaydetmişse LoadSheet, zaten elinizde tuttuğunuz modele başka bir sürümün hücrelerini karıştırmak yerine False döndürür

procedure TReportForm.ShowSheetNo(SheetNo: Integer);  // SheetNo, Sheets[] gibi 1 tabanlıdır
var
  Sheet: TXLSXWorksheet;
begin
  Sheet := FWorkbook.Sheets[SheetNo];
  if Sheet.Cells.Count = 0 then
    if not FWorkbook.LoadSheet(SheetNo - 1) then      // LoadSheet 0 tabanlı konumu ister
    begin
      StatusBar.SimpleText := 'The workbook changed on disk; reopen it';
      Exit;
    end;
  Grid.ShowSheet(Sheet);                              // sizin kendi grid kodunuz
end;

O tasarımdan birkaç sonuç çıkar. Bir TStream'den açılan çalışma kitabının geri dönülecek bir kaynak dosyası yoktur; dolayısıyla LoadSheet, akıştan açılan bir .xlsx için False döndürür. Aynısı, ayrıştırılmadan önce belleğe çözülen şifreli bir çalışma kitabı için de geçerlidir. Akıştan seçmeli açılan ikili bir .xlsb istisnadır; çünkü HotXLS, ertelenen sayfalar için akışın geçici bir kopyasını hazırlar. Ve gerçekten boş bir sayfa her çağrıda yeniden okunur; sıfır hücre, atlanmış bir sayfanın göründüğü şeydir çünkü

Nasıl kullanılacağına maliyet modeli karar verir. Her LoadSheet çağrısı paylaşılan dize tablosunu yeniden ayrıştırır; on sayfayı tek tek somutlaştırmak o parçanın bedelini on kez öder. Kullanıcının arada bir bir sekme daha açtığı bir görüntüleyici için makul bir bedeldir. Sayfa setini zaten biliyorsanız onun yerine SelectedSheets ile yeniden açın: bir arşiv geçişi, bir paylaşılan dize ayrıştırması. İçerik farkı da vardır: LoadSheet bir sayfanın hücrelerini, değerlerini, formüllerini ve biçimlerini getirir; yorumlar, çizimler ve tablolar bir Open ile gelir. Dolayısıyla kodunuzun yorumlarını ya da tablolarını okuduğu sayfalar baştan seçimin içine girer

Bir gidiş-dönüş neden hiç dokunmadığınız parçaları değiştirir?

Her şeyi yeniden üreten bir kaydetme, hiç değişmemiş baytları yeniden yazar. Temayı aç, bir nesne modeli kur, serileştir, yeniden sıkıştır; sonuç, tema hakkında kimse bir şey düzenlememişken orijinalden farklıdır. VBA projeleri ve özet önbellekleri için bedel, gürültülü bir difften fazladır: bir VBA projesi kendi iç akışlarını taşıyan ikili bir bileşik dosyadır ve bir özet önbelleği, alanlarına özet tablosu tanımlarının konumla eriştiği kaynak verinin anlık görüntüsünü tutar; dolayısıyla hiçbiri değişmemişken yapılacak en güvenli iş, baytlarını olduğu gibi bırakmaktır

Öntanımlı değeri True olan PreserveRawParts, SaveAs'i değişmemiş parçaların sıkıştırılmış baytlarını kaynak arşivden doğrudan kopyalamaya iter. Bu, temayı, VBA projesini, bağlantıları, grafikleri, gömülü medyayı, yazıcı ayarlarını, özet tabloları ve özet önbelleği tanımları ile kayıtları kapsar; her birini yalnızca model değiştirilmediğini söylediği sürece. O parçalar yolda asla açılmaz ya da yeniden sıkıştırılmaz. Özelliği False yapmak her parçanın eksiksiz yeniden serileştirilmesini zorunlu kılar; çıktının yalnızca nesne modelinin içerdiğini yansıtmasını istediğinizde uzanacağınız anahtar budur

Ham kopyalama yolu, LoadSheet ile aynı dosya denetimini kullanır: kaynak, Open anında görülen boyut ve zaman damgasıyla hâlâ var olmalıdır. Yoksa SaveAs o parçaları normal biçimde serileştirir ve dosya yine de eksiksizdir. İki durum daima serileştirme yolunu izler: kaynağı okuma sırasında XML'e çevrilen ikili bir .xlsb olan çalışma kitabı ve StrictOOXML ayarlı bir kaydetme — parça ad alanlarının değiştiği durum

Kısmen yüklenmiş bir çalışma kitabını kaydetmek bir karar ister. Bir .xlsb kaynağı için SaveAs, ertelenmiş bir sayfa kaldıkça reddeder ve Diagnostics içine "Kaydetmeden önce her ertelenmiş ikili çalışma sayfasını yükleyin" yazar. Bir .xlsx için seçmeli yüklenmiş bir çalışma kitabını salt okunur bir görünüm sayın: taslak bir sayfanın yazıcının yayacağı hücresi yoktur; düzenlemeden ve kaydetmeden önce ya her taslak için LoadSheet çağırın ya da seçimsiz yeniden açın

Kasıtlı olarak yeniden açan bir keşif akışı

Ayakta kalan desen iki geçişlidir. Birinci geçiş her dosyayı yalnızca üst veriyle açar ve karar verir. İkinci geçiş, eşleşen dosyaları bir sayfa seçimiyle yeniden açar ve işi yapar. Yeniden açma, birinci geçişin neyi önlediğini sayana dek israf gibi görünür: hücreleri ayrıştıran tek açılış ikinci Open'dır ve o da yalnızca birinci geçişin seçtiği sayfaları ayrıştırır

Delphi'de iki geçişli HotXLS keşif hattı: yalnızca-meta veri geçişi hangi çalışma kitaplarının ve sayfaların önemli olduğuna karar verir, sonra hedefli bir geçiş yalnızca seçilen sayfaları ayrıştırır
Birinci geçiş dizini yalnızca meta veriyle tarar ve hangi dosya ile sayfaların önemli olduğuna karar verir; ikinci geçiş eşleşmeleri bir sayfa seçimiyle yeniden açar. Hücre XML'ini yalnızca ikinci açış ayrıştırır
procedure ProcessInvoiceFile(const FileName: WideString);
var
  Probe, Workbook: TXLSXWorkbook;
  Wanted: TStringList;
  I: Integer;
begin
  Wanted := TStringList.Create;
  try
    // Geçiş 1: keşif, hücre yok
    Probe := TXLSXWorkbook.Create;
    try
      Probe.MetadataOnly := True;
      if Probe.Open(FileName) <> 1 then
        Exit;
      for I := 1 to Probe.Sheets.Count do
        if Pos('Invoice', Probe.Sheets[I].Name) > 0 then
          Wanted.Add(Probe.Sheets[I].Name);
    finally
      Probe.Free;
    end;
    if Wanted.Count = 0 then
      Exit;                               // bu dosyada ayrıştırmaya değer hiçbir şey yok

    // Geçiş 2: yalnızca seçilen sayfalar için hücreler
    Workbook := TXLSXWorkbook.Create;
    try
      Workbook.SelectedSheets.Assign(Wanted);
      if Workbook.Open(FileName) = 1 then
        PostInvoices(Workbook);           // sizin kendi işleme kodunuz
    finally
      Workbook.Free;
    end;
  finally
    Wanted.Free;
  end;
end;

Open'dan sonra MetadataOnly'yi temizlemek sorguyu eksiksiz bir çalışma kitabına çevirmez. Bayrak açılma sırasında okunur; sonradan değiştirmek yalnızca o nesne üzerindeki bir sonraki Open'ı etkiler. Sorgu yine de LoadSheet üzerinden bir seferde bir sayfa büyüyebilir; bir iki sayfa için bu makuldur, ama bir hattı temiz yeniden açış daha iyi hizmet eder — o, her sayfanın yorumlarını ve tablolarını da beraberinde getirir

Bunun diğer büyük dosya kollarıyla uyumu

Seçmeli yükleme ayrıştırdığınızı azaltır. Bir sayfanın yüklendikten sonraki maliyetine hiç dokunmaz ve iş her sayfanın her hücresini gerektiriyordu yardımcı olamaz. O durum depolama hakkındadır — satır bloğu hücre depolama ve aralık tabanlı stiller yazısında kapsanır — ve ayrıştırma verimi hakkındadır, paralel XLSX ayrıştırma ve bellek ayırıcı yazısında. Görev hiç düzenleme olmadan saf bir okumaysa akan doğrudan okuyucu, çalışma kitabı nesne modelini tamamen atlar ve bir model kuran her yükleme stratejisinden daha iyi araçtır

Bunların hepsi kendi Delphi ya da C++Builder yürütülebilirinizin içinde çalışır; taramayı yapan sunucuda Excel kurulumu yoktur. Desteklenen biçimler, sürümler ve lisanslama HotXLS Delphi elektronik tablo bileşeni sayfasında listelenir