SpreadsheetML'de para birimi biçimi taşıyan bir sütun tam olarak tek bir öğe ister: <col min="2" max="2" style="3"/>. ECMA-376 Bölüm 1 bir hücre stilini yalnızca üç yerde bağlar: bir sütun (§18.3.1.13), bir satır (§18.3.1.73) ve bir hücre (§18.3.1.4). Biçimi B2:D500000'e daraltın; SpreadsheetML'in o şekil için tek bir öğesi yoktur: bir sütun stili 500000. satırı aşardı, bir satır stili her sütunu kapsardı; sonuçta bir yazıcı, biçimi yalnızca stil içeren hücreler olarak — 1.499.997 tanesini — yayımlar
Bellek içi bir elektronik tablo modeli aynı duvara bir adım önce çarpar. Biçimlendirme, her hücre nesnesine özellik atamak demekse sütun başlığına tek bir tık, tek bir küçük tamsayıyı taşımak için bir milyon nesne ayırır ve yalnızca bir sayı biçimi isteyen bir Delphi süreci, diskte birkaç kilobaytlık bir çalışma kitabında adres alanını tüketir. Gerçek tasarım sorusu şudur: biçimlendirme düzenleme sırasında nerede yaşar ve ne zaman hücre başına veriye dönüşür
HotXLS bir aralığı hücre oluşturmadan nasıl biçimlendirir
HotXLS aralık biçimlendirmesini aralıklar (intervals) olarak tutar. TXLSXRange üzerindeki her stil ayarlayıcı — SetNumberFormat, SetNumberFormatIndex, SetFontIndex, SetFillIndex, SetBorderIndex, SetAlignmentIndex — çalışma sayfasının StyleOverlays listesine bir kayıt ekler: dört koordinat, çağrının sahiplendiği stil parçaları ve o parçaların havuz indeksleri. Dikdörtgenin içinde hâlihazırda var olan hücreler yeni parçaları anında alır. Boş koordinatlar boş kalır ve onlar için hiçbir şey ayrılmaz
var
Workbook: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Edge: TXLSXBorder;
BorderIdx, Row: Integer;
Stats: TXLSXCellStorageStatistics;
begin
Workbook := TXLSXWorkbook.Create;
try
Sheet := Workbook.Sheets.Add('Ledger');
Sheet.WriteStringRowValues(1, 2, ['Net', 'Tax', 'Gross']);
for Row := 2 to 1001 do
Sheet.WriteFloatRowValues(Row, 2, [Row * 1.5, Row * 0.3, Row * 1.8]);
// 1.048.576 satırın hepsinde B..D sütunları: çağrı başına bir aralık
Sheet.RCRange[1, 2, XlsxMaxRow, 4].SetNumberFormat('#,##0.00 "EUR"');
// Kenarlığı bir kez kurun, sonra indeksle uygulayın
Edge := TXLSXBorder.Create;
try
Edge.SetAll(xlsxBorderThin);
BorderIdx := Workbook.Borders.LookupOrAdd(Edge); // 1 tabanlı
finally
Edge.Free;
end;
Sheet.RCRange[1, 2, XlsxMaxRow, 4].SetBorderIndex(BorderIdx);
Stats := Sheet.Cells.GetStorageStatistics;
// 3.003 bekleyin: üç başlık hücresi artı 3.000 değer
WriteLn('Cells: ', Stats.CellCount);
if Workbook.SaveAs('ledger.xlsx') <> 1 then
raise Exception.Create('Save failed');
finally
Workbook.Free;
end;
end;
İki biçimlendirme çağrısı 3.145.728 koordinatı kapsadı ve o sayıma hiçbir şey eklemedi. Aralıklar bilerek 1. satırdan başlar: tam sütunlu bir aralık tek bir <col> öğesi olarak kaydedilir ve 1. satırdaki metin başlıklar zaten bir sayı biçiminden etkilenmez. RCRange, Range['A1:C5'], EntireColumn ve EntireRow'un döndürdüğü aralıklar çalışma sayfasının mülkiyetindedir; örnek onları hiç serbest bırakmaz
TXLSXFormatParts, birkaç aralığın çekişmesini önleyen şeydir. Üyeleri xfpFont, xfpFill, xfpBorder, xfpNumberFormat, xfpAlignment, xfpProtection ve xfpCellControl'dur ve her TXLSXRange ayarlayıcısı tam olarak birini sahiplenir. Bir hücre çözümlendiğinde onu kapsayan aralıklar eklenme sırasına göre uygulanır; her biri yalnızca kendi parçalarını yazar ve hücrenin üzerindeki açık biçimlendirme hepsini geçer. Sayı biçimini ayarlayan bir sütun aralığı ile dolgu ayarlayan bir satır aralığı, ikisi de B5 hücresine sağlam ulaşır
Tek bir kaydın birkaç parça taşıması gerektiğinde Sheet.StyleOverlays.Add(Row1, Col1, Row2, Col2, Parts, State), havuz indeksleri doldurulmuş bir TXLSXCellStyleState alır. Add koordinatları 1.048.576 × 16.384 örgüsüne kırpıp ters çevrilmiş köşeleri düzeltir ve Parts boşken hiçbir şey kaydetmeden nil döndürür. Yapısal düzenlemeler kayıtları adımında tutar: biçimlendirilmiş bir bloğun içindeki InsertRows aralığı eklenen sayı kadar genişletir ve üstüne ekleme bütün kaydı aşağı kaydırır
SetBorders neden hücre oluşturur da SetBorderIndex oluşturmaz?
TXLSXRange.SetBorders(Kind, Style, Color) her hücreye tek tek bakmak zorundadır, çünkü yanıt konuma bağlıdır. xlsxEdgeOutline ile üst satır üst kenarlığı alır, sol sütun sol kenarlığı alır, iç kısım hiçbir şey almaz; xlsxEdgeInside ile durum kabaca terstir. Çağrı ayrıca yeni kenarları, hücrenin hâlihazırda sahip olduğu kenarlıkla birleştirir; önce Outline sonra Inside, dolu bir örgü kurar. O hücre başına bileşim, var olmayan bir hücreyi oluşturan Cells[R, C] üzerinden geçer. 10 × 40'lık bir rapor bloğunda bu tam olarak doğrudur. xlsxEdgeAll ile EntireColumn(3)'te ise 1.048.576 yeni hücre nesnesidir
Böylece bölünme basittir. Bir ekran verisinden uzun her şey için kenarlığı ilk örneğin yaptığı gibi bir kez kurun ve indeksle uygulayın: TXLSXBorder.SetAll, sonra 1 tabanlı indeks için Workbook.Borders.LookupOrAdd, sonra SetBorderIndex. SetBorders'i, dış çizgisiyle iç çizgileri farklılaşan bloklar için saklayın; yukarıdaki 10 × 40 rapor bloğu gibi
İşi geri almadan biçimleri geri okumak
Aynı tuzak yalnızca okuyan kodu da yakalar. Cells[R, C], isabet olmayınca oluşturan bir erişicidir: B500000'in sayı biçimini sorun, doğru yanıtı artı B500000'de artık saklanacak ve kaydedilecek biçimli, boş yeni bir hücre alırsınız. Bunu biçimlendirmeyi denetlemek için bir sütun boyunca döndürün; aralığın önlemek için var olduğu milyon nesneyi, görüntüsü masum tek tek okumayla yeniden kurmuş olursunuz
TXLSXWorksheet.GetCellVisibleFormat, bir koordinatın etkin stilini hücre eklemeden çözer. Çalışma sayfası öntanımlısını, satır ve sütun aralıklarını, dikdörtgen aralıklarını, tablo stillerini ve hücre üzerindeki açık biçimlendirmeyi birleştirir. out ABuiltinNumberFormat parametreli aşırı yükleme, 14 (kısa tarih) gibi gömülü bir Excel biçimini, çalışma kitabının özel NumberFormats havuzuna işaret eden NumberFormatIndex'ten ayrı olarak bildirir. Cells.HasCell(R, C), fiziksel bir hücrenin var olup olmadığını yanıtlar
var
Sheet: TXLSXWorksheet;
Before, After: TXLSXCellStorageStatistics;
State: TXLSXCellStyleState;
Row, Builtin, Unformatted, Physical: Integer;
begin
Sheet := Workbook.Sheets[1]; // Sheets 1 tabanlıdır
Before := Sheet.Cells.GetStorageStatistics;
Unformatted := 0;
Physical := 0;
for Row := 1 to 200000 do
begin
// Etkin stil, sayfaya hücre eklemeden çözümlenir
State := Sheet.GetCellVisibleFormat(Row, 2, Builtin);
if (State.NumberFormatIndex = 0) and (Builtin = 0) then
Inc(Unformatted);
if Sheet.Cells.HasCell(Row, 2) then
Inc(Physical);
// Sheet.Cells[Row, 2].NumberFormatIndex aynı soruyu yanıtlar,
// ama ziyaret ettiği her satır için arkasında bir yeni hücre nesnesi bırakır
end;
After := Sheet.Cells.GetStorageStatistics;
// İki sayım da döngüden önce ve sonra aynı olmalı
WriteLn(Format('cells %d -> %d, styled blanks %d -> %d',
[Before.CellCount, After.CellCount,
Before.StyledBlankCellCount, After.StyledBlankCellCount]));
end;
Böyle bir denetimi gerçek iş yükünüze karşı bir kez çalıştırın. StyledBlankCellCount, stili olan ama değeri, formülü ya da zengin metni olmayan hücreleri sayar. Salt okunur olması gereken bir geçiş sırasında tırmanıyorsa bir yardımcı, salt stil taşımak için hücre oluşturuyor demektir; tipik olarak bir hücrenin biçimlendirilip biçimlendirilmediğine karar vermek için Cells[R, C] çağırıyor
256 satırlık blokların kodunuz için anlamı
Fiziksel hücreler 256 satırlık bloklarda yaşar. Bloğun içinde her dolu satır hücrelerini sütuna göre sıralı bir listede tutar; hücresiz bir satır yalnızca boş bir yuva, hücresiz bir 256'lık dilimin ise hiç bloğu yoktur. Verisi 3, 7 ve 512. satırlarda olan seyrek bir sayfa bu yüzden iki blok tutar. Kaydetme aynı yapıyı satır öncelikli sırada gezer; SpreadsheetML'in <row> öğeleri ile <c> çocukları için istediği sıra budur
Tuttuğunuz TXLSXCell referansı, çevresine başka hücreler eklenirken ya da çıkarılırken aynı nesne olarak kalır; bloklar kendi iç kayıtlarını yeniden düzenler, sizin tanıtıcılarınızı değil. WriteFloatRowValues ile WriteStringRowValues gibi toplu yazıcılar, taze bir sayfada daha hızlı yolu seçer ve satır öncelikli değerleri paketlenmiş bir depoya ekler. Cells[R, C] üzerinden yapılan ilk hücre başına erişim o depoyu bir kez hücre nesnelerine çevirir. GetStorageStatistics, hangi durumda olduğunuzu PackedStorageActive ve PackedCellCount ile, yanında RowBlockCount ve PopulatedRowCount ile gösterir
Stil havuzları kaydetme anında kırpılır. styles.xml yazılmadan önce HotXLS, yazı tipi, dolgu, kenarlık, sayı biçimi, hizalama ve koruma havuzlarını yalnızca hâlâ kullanımda olan girdileri tutacak biçimde yeniden kurar; bir kullanıcının bir saat boyunca yeniden stillendirdiği bir çalışma kitabı, terk edilmiş her dolguyu taşımaz. Mutlak bir FormatIndex'e sabitlenmiş hücreler yuvalarını olduğu gibi korur
Satır akışlı kayıtlar ve bunu gösteren sayılar
TXLSXWorkbook.StreamingWrite öntanımlı olarak True'dur. Açıkkken SaveAs, her çalışma sayfası <row>'unu üretir ve doğrudan ZIP girdisinin deflate akışına yazar; böylece bellekteki en büyük çalışma sayfası XML parçası her an tek bir satırdır. Kapalıyken her çalışma sayfasının XML'i önce tek bir eksiksiz dize olarak kurulur ve sonra sıkıştırılır; tepe bellek en büyük sayfayı izler. Dikdörtgen aralıklarının dönüştürdüğü yalnızca stil içeren hücreler — açılıştaki B2:D500000 durumu — o satır başına döngünün içinde üretilir; dosyaya eklenirler ama bellekte tek bir XML bloğu olarak asla durmazlar
Paylaşılan dizeler ile özet önbellek kayıtları aynı deseni izler. Her <si> ya da <r> girdisi kendi başına üretilir ve yazılır; böylece geniş bir analitik çalışma kitabında paylaşılan dize tablosu — çoğunlukla herhangi bir tekil çalışma sayfasından büyük — tek bir XML bloğu olarak var olmak zorunda kalmaz
Bunların hiçbirine körü körüne güvenmek gerekmez. Her kayıttan sonra çalışma kitabı dört salt okunur özellik açar: LastSaveUsedStreaming, LastSavePeakRowXmlChars (en uzun tek satır, karakter olarak), LastSaveBufferedWorksheetXmlChars (bellekte tutulan toplam çalışma sayfası XML'i, akışta sıfır) ve LastSavePeakAuxiliaryXmlChars (en uzun paylaşılan dize ya da özet girdisi). LastSaveUsedStreaming, .xlsx, .xltx ve .xltm çıktısında ve ikili .xlsb yazıcısında True'dur; OpenDocument çıktısı kendi yazıcısından geçer ve False bildirir
procedure ReportSave(Workbook: TXLSXWorkbook; const FileName: string;
Streaming: Boolean);
begin
Workbook.StreamingWrite := Streaming;
if Workbook.SaveAs(FileName) <> 1 then
raise Exception.CreateFmt('Save failed: %s', [FileName]);
WriteLn(Format('streaming=%s peakRow=%d buffered=%d peakAux=%d',
[BoolToStr(Workbook.LastSaveUsedStreaming, True),
Workbook.LastSavePeakRowXmlChars,
Workbook.LastSaveBufferedWorksheetXmlChars,
Workbook.LastSavePeakAuxiliaryXmlChars]));
end;
// Aynı çalışma kitabı, iki mod; süreç belleğini profiler'ınızda izleyin
ReportSave(Workbook, 'ledger-stream.xlsx', True);
ReportSave(Workbook, 'ledger-buffered.xlsx', False);
Kendi verinizde tamponlu satır, bütün sayfanın XML'i mertebesinde bir buffered değeri gösterirken akışlı satır sıfır ve en geniş satırınızdan büyük olmayan bir peakRow gösterir. StreamingWrite'i açık bırakın. Bayrak, yalnızca çalışma sayfası XML'inin sıkıştırmadan önce eksiksiz kurulup kurulmayacağına karar verir ve LastSaveBufferedWorksheetXmlChars o kurulumun boyutunu karakter olarak bildirir
Geri kalan bellek nereye gider?
Hücreler ve stiller sıkışık hâle geldikten sonra büyük bir çalışma kitabındaki sıradaki en büyük tüketiciler paylaşılan dize tablosu ile dosyanın taşıdığı uydu parçalardır: özet önbellekleri, çizimler ve nesne modelinin modellemediği parçalardan korunan XML. Bunların kendi kolları vardır ve tek bir ayar hepsini birden kapsamaz
Darboğazınız kaydetmek yerine açmaysa, seçmeli yükleme koldur: yalnızca üst veri ve seçmeli çalışma sayfası yükleme yazısı, bir çalışma kitabını dokunmayacağınız sayfalar için ödemeden okumayı ele alır. Çok büyük dosyalarda okuma yolu verimliliği için paralel XLSX ayrıştırma ve bellek ayırıcı notlarına bakın ve hiç nesne modeline ihtiyaç duymayan yalnızca çıktı iş yükleri için sunucu toplu işleri için akışlı yazma çoğunlukla buradaki herhangi bir miktar ayarlamadan daha iyi bir uyumdur
HotXLS, XLS ve XLSX'i Excel ya da OLE otomasyonu olmaksızın yerel Delphi ve C++Builder kodundan okur ve yazar; yukarıdaki her sayı kendi sürecinizden gelir ve orada denetlenebilir. Desteklenen biçimler ile RAD Studio sürümleri HotXLS Delphi elektronik tablo bileşeni sayfasında listelenir