45 satır veri taşıyan on dört sütunluk bir elektronik tablo, ODS olarak kaydedildiğinde genelde beklediğinizden bir eleman fazla ile biter: <table:table-row table:style-name="ro1" table:number-rows-repeated="1048530"><table:table-cell table:number-columns-repeated="14"/></table:table-row>. O tek satır, 46'dan aşağısı ızgaranın dibine dek her satırı tarif eder. Gerçekten genişletin — o satırın 1.048.530 kez yazılmış hâlini — küçücük bir content.xml bir milyon satır kaydına, bir milyon yükseklik girdisine ve sütunlar varsayılan hücre stili taşıyorsa neredeyse on beş milyon stilli boş hücreye dönüşür
Tek bir yinelenen satırın içe aktarımı neden pahalıya gelir?
Maliyet genişlemeden gelir ve genişleme, içe aktarıcı yazmanın en bariz yoludur. ro1 stili style:row-height="0.452cm" koyar; bu 12.81 puandır. Yinelenen satır başına bir SetRowHeight(RowIndex + i, RowHeight) çağrısıyla uygulayan bir satır bitiricisi, nesne modelinin tuttuğu satır başına yükseklik tablosuna 1.048.530 girdi yazar. O tablo, her yazım önce mevcut anahtarı arayan anahtarlı bir string listesiyse içe aktarma tekrar sayısına göre kuadratiktir; bir milyonluk tekrar sayısı, kuadratiğin kuramsal olmaktan çıktığı girdinin ta kendisidir
Boş hücreler işi daha da bozar. Böyle bir dosyada her <table:table-column> çoğunlukla bir table:default-cell-style-name adlandırır ve yinelenen satırdaki her boş hücre onu miras alır. Stilli bir boş hücre, çoğu nesne modeline göre hâlâ bir hücredir. Yinelenen satır başına sütun başına bir tane somutlaştırırsanız bellek faturası yükseklik tablosunu gölgede bırakır
Kaydetme tarafında aynanın öbür yüzü vardır. Model, 1.048.575. satırın yüksekliği olduğunu söylediği anda, 1. satırdan üzerinde bir şey olan son satıra dek yürüyen bir yazıcı bir milyon <table:table-row> elemanı üretir. Ucuz içe aktarımın ardından pahalı kayıt, maliyeti yalnızca başka bir düğmeye taşır
HotXLS yinelenen satır yüksekliğini nasıl saklar?
HotXLS Delphi Excel Component, yinelenen bir ODS satır yüksekliğini run olarak saklar: FirstRow, LastRow ve tek bir Height taşıyan bir TXLSXRowHeightRun kaydı. TXLSXWorkbook.OpenODS, tekrar sayısı 1 olan bir satırı bitirdiğinde herkes gibi SetRowHeight çağırır. Daha büyük her sayım için bütün kuşağı tek run olarak kaydeder; böylece örnekteki kuyruk, 12.81 pt'te 46'dan 1.048.575'e dek olan satırlar olur. Izgaranın sonunu aşan bir tekrar sayısı reddedilmez, 1.048.576. satırda kesilir
İçe aktarıcıda, sonucu kaynak dosyayla karşılaştıranları şaşırtan bir kural vardır. ODS yazan tablo uygulamaları, kimsenin boyutlandırmadığı satırlar dahil her satır stiline bir style:row-height vermeye ve bunları style:use-optimal-row-height="true" ile işaretlemeye eğilirlidir. Hiç hücre saklamayan bir satır için otomatik yükseklik zaten varsayılan yüksekliktir; HotXLS bu yüzden ona bir yükseklik kaydetmez. Bir şey saklayan satırlar, optimal işaretli olsalar bile yüksekliklerini korur; çünkü içeriğine sığmak için büyümüş bir metin kaydırmalı satır, çalışma kitabı XLSX olarak yeniden açıldığında küçülüp kalırdı. Ödünleşim doğrudur: gerçek bir yüksekliği düşürmek görünür bir düzen değişimi üretir, gereksiz birini tutmak tek bir run'a mal olur
Yükseklikleri geri okumak sıradan RowHeight özelliğinden geçer. Önce satır başına listeye bakar, satırın açık bir girdisi yoksa run'lara düşer; HasRowHeight aynı sırayı izler. Run'lar hiç örtüşmediği için geri düşüş, satırı içeren ilk run'da durur
var
Workbook: TXLSXWorkbook;
Sheet: TXLSXWorksheet;
Spans: TXLSXAxisSpans;
I: Integer;
begin
Workbook := TXLSXWorkbook.Create;
try
// Son satır elemanı 1.048.530 kez yinelenen bir sayfa
Workbook.OpenODS('tail-repeat.ods');
Sheet := Workbook.Sheets[1]; // Sheets 1 tabanlıdır
// Saklı satır üstverisi: önce satır başına girdiler, sonra run'lar
Spans := Sheet.GetStoredAxisSpans(True);
for I := 0 to High(Spans) do
Writeln(Spans[I].FirstIndex, '..', Spans[I].LastIndex);
// İki okuma da aynı run'dan gelir
Writeln(Sheet.RowHeight[46]:0:2, ' pt');
Writeln(Sheet.RowHeight[1048575]:0:2, ' pt');
// Satır başına girdi, run'dan önce arandığı için o kazanır
Sheet.RowHeight[500000] := 36;
// Run içinde bir satırı silmek ona tek satırlık bir delik açar
Sheet.ClearRowHeight(500001);
Writeln(Sheet.HasRowHeight(500001)); // FALSE
Writeln(Sheet.RowHeight[500002]:0:2, ' pt'); // hâlâ run yüksekliği
finally
Workbook.Free;
end;
end;
Örnekteki iki düzenleme bilerek farklı davranır. Sheet.RowHeight[500000] := 36 run'a dokunmaz ve satır başına listeye bir girdi ekler; o girdi önce bakıldığı için run'ı gölgeler. ClearRowHeight böyle çalışamaz; silinmiş bir satır, üzerine run uzansa bile yükseklik yok bildirmek zorundadır. O yüzden önce satır başına girdileri siler, sonra run listesini yeniden kurar: silinen satırın dışındaki her run olduğu gibi kalır, onu içeren run öncesi ve sonrası diye ikiye bölünür ve 500.001. satır için hiçbir şey saklanmaz. GetStoredAxisSpans(True) artık 46..500000 ile 500002..1048575'i iki ayrı run olarak, 500.000'in tek satırlık girdisinin yanında listeler. ClearRowHeights iki yapıyı tek çağrıda boşaltır
Devralınan boş hücre stilleri nereye gidiyor?
Miras kalan boş stiller stil overlay'lerine dönüşür: arkalarında hücre nesnesi olmayan, stil taşıyan dikdörtgenler. Bir satırı okurken içe aktarıcı, yinelenen hücreleri aynı stile çözülen bitişik sütun aralıkları hâlinde gezer. Stili tam olarak Default görünen bir aralık hiçbir şey saklamaz. Gerçek biçimlendirme taşıyan bir aralık — söz gelimi ortalanmış ya da koyu bir sütun varsayılanı — tüm sütunlarını kapsayan tek satır değeri olur
O satır değeri işlendiğinde ve satır yineleniyorsa, HotXLS aralığın sol üstünde tek bir hücre somutlaştırır, miras kalan stili ona uygular ve altı stil indeksini (font, dolgu, kenarlık, sayı biçimi, hizalama, koruma) bütün dikdörtgen için Sheet.StyleOverlays.Add ile kaydeder: aralıktaki her sütun, ilk yinelenen satırdan sonuncusuna dek. Kalan 1.048.529 satır için başka hiçbir şey kurulmaz. Sheet.Cells.Count, gerçekten veri tutan hücre sayısına yakın kalır
Dikdörtgenin içindeki bir hücre, kodunuz ona dokunduğu anda overlay'i üzerine alır. Kuyruğu koyu bir sütun varsayılanıyla biçimlendirilmiş bir sayfayı açtıktan sonra Sheet.Cells[700000, 1].FontIndex, bir satır önce o koordinatta hiç hücre olmamış olsa da koyu fonta çözülür. Biçimlendirilmiş ama boş sütunlu XLSX dosyaları aynı düzeneği kullanır; satır bloğu hücre saklama ve aralık stil overlay'leri yazısı, birden çok overlay'in nasıl katmanlaşıp çözüldüğünü anlatır. ODS içe aktarıcısı yalnızca overlay'lerin kaynağında ayrışır: onları tekrar sayılarından kendisi türetir
SaveAsODS tekrar sayısını nasıl geri yazar?
TXLSXWorkbook.SaveAsODS, birbirinin tıpatıp aynı satır kuşaklarını tek bir table:number-rows-repeated taşıyan <table:table-row> olarak yazar ve kuşağı yalnızca satırlarla ilgili bir şey değiştiği yerde bitirir. Tablo, üzerinde bir şey taşıyan en düşük satıra kadar iner: bir değer, yalnızca stil taşıyan boş bir hücre, tek satırlık bir yükseklik, bir yükseklik run'ının sonu, bir overlay'in alt kenarı, gizli ya da anahat girdisi olan bir satır ya da bir doğrulama ve koşullu biçim alanı
Yazıcı 1. satırdan yola çıkıp sonraki sınırı önceden bakarak arar. Adaylar: somutlaşmış bir sonraki hücre, bir yükseklik run'ının başlangıcı ya da sonu, herhangi bir satır başına girdi (yükseklik, gizli bayrağı, anahat düzeyi, daraltılmış durumu, elle sayfa sonu), overlay kenarları, birleştirilmiş aralıklar, köprüler, yorumlar, görseller ve her doğrulama ile koşullu biçim dikdörtgeninin kenarları. O sisteme dek her şey tek elemandır. Her elemanın içinde aynı mantık sütunlar boyunca işler; böylece boş bir kuşak, overlay'in çözümlediği stil adıyla table:number-columns-repeated taşıyan tek bir <table:table-cell> olur
Satır stilleri paylaşılır. Satır yüksekliği metin olarak biçimlendirilir, örnek için 12.81pt; o metin, gizli bayrağı ve sayfa sonu bayrağıyla birlikte otomatik satır stilinin anahtarını oluşturur. Aynı üç değeri taşıyan her satır, tek bir style:row-height özelliği tutan aynı ro<N> stilini işaret eder. Yüksekliği olmayan, görünür ve sayfa sonu olmayan bir satır hiç satır stili almaz
var
Workbook, Reopened: TXLSXWorkbook;
Saved: TMemoryStream;
begin
Workbook := TXLSXWorkbook.Create;
Reopened := TXLSXWorkbook.Create;
Saved := TMemoryStream.Create;
try
Workbook.OpenODS('tail-repeat.ods');
Workbook.Sheets[1].RowHeight[500000] := 36;
Workbook.Sheets[1].ClearRowHeight(500001);
// Kuyruk, birkaç yinelenen satır elemanı olarak yazılır
Workbook.SaveAsODS(Saved);
Writeln('ODS stream: ', Saved.Size, ' bytes');
Saved.Position := 0;
Reopened.OpenODS(Saved);
// Beklenen: geçersiz kılma, delik ve iki run parçası gidiş-dönüşü atlatır
Writeln(Reopened.Sheets[1].RowHeight[500000]:0:2); // 36.00
Writeln(Reopened.Sheets[1].HasRowHeight(500001)); // FALSE
Writeln(Reopened.Sheets[1].RowHeight[500002]:0:2); // run yüksekliği
finally
Saved.Free;
Reopened.Free;
Workbook.Free;
end;
end;
Yukarıdaki sayfa için kuyruk dört parçaya bölünür: 46-499.999 arası run yüksekliğiyle, 500.000 36 pt ile, 500.001 satır stilsiz, 500.002-1.048.575 arası yeniden run yüksekliğiyle. Varsayılan stili overlay bırakmış her sütun, overlay'in bittiği yere bir bölme daha ekleyebilir. Kuyruğun üzerindeki 45 veri satırı, her biri hücre taşıdığı için satır başına bir eleman yazılır
XLSX başka hikâyedir. SpreadsheetML'de tekrar attribute'ü yoktur ve bir <row> elemanı tam olarak tek satır tarif eder; dolayısıyla run destekli bir sayfayı .xlsx olarak kaydetmek, run'ın kapsadığı satırları sayar ve her birine ht ile customHeight="1" yazar. Model bellekte kompakt kalır; dosyanın ne kadar büyüyeceğine hedef biçim karar verir. ODS olarak gelip yalnızca ODS olarak çıkacak bir çalışma kitabı o satır başına maliyeti asla ödemez
Satırları yeniden numaralayan düzenlemeler
Satır yüksekliklerinin ikinci bir temsili, satır taşıyan her işlem onu da taşıdığında doğrudur. TXLSXWorksheet.InsertRows ile DeleteRows, run'ları satır başına listelerle birlikte kaydırır: düzenleme noktasından önceki run parçası yerinde kalır, silinen pencere içindeki satırlar düşer, kalan eklenen ya da silinen sayı kadar taşınır. Bu yüzden bir eklemeye çapraz düşen run, aralarında boyutsuz satır boşluğu olan iki run'a, bir silmeye çapraz düşen run ise kısaya dönüşür
procedure ShiftAroundTheRun(Sheet: TXLSXWorksheet);
begin
Sheet.RowHeight[500000] := 36;
Sheet.ClearRowHeight(500001);
// 500000'e iki yeni satır: geçersiz kılma 500002'ye, delik 500003'e gider
Sheet.InsertRows(500000, 2);
Writeln(Sheet.RowHeight[500002]:0:2); // 36.00
Writeln(Sheet.HasRowHeight(500003)); // FALSE
Writeln(Sheet.HasRowHeight(500000)); // FALSE: eklenen satırların yüksekliği yok
// Tekrar silin: geçersiz kılma ve delik eski satır numaralarına döner
Sheet.DeleteRows(500000, 2);
Writeln(Sheet.RowHeight[500000]:0:2); // 36.00
Writeln(Sheet.HasRowHeight(500001)); // FALSE
Writeln(Sheet.UsedRange.Row2); // kaydırılan run'ın sonunu içerir
end;
Gidiş-dönüş kuyruğu tam olarak bıraktığı yerde bırakmaz. Ekleme, kuyruk run'ını 500.002..1.048.575'ten 500.004..1.048.577'ye iter; ızgara 1.048.576. satırda bittiği için o satırın ötesi kırpılır. Silme ardından kalanı iki yukarı taşır ve run 1.048.574'te biter: 1.048.575'in saklı yüksekliği kalmaz. Izgaranın son satırları sizin için önemliyse, bir ekle-sil çiftinden sonra yüksekliklerini yeniden atayın. Bir run'ın ortasına eklenen satırlar da onun yüksekliğini almaz: boyutsuz satır boşluğu açar, run yüksekliğini miras almaz — yukarıda HasRowHeight(500000)'un FALSE basması bu yüzden
Öteki satır taşıyan işlemler aynı kalıbı izler. TileRangeAxisMetadata, tüm döşenen aralıkta run'ları temizler ve sonra her kaynak run'ı kopya başına kendi ofsetinde bir kez kaydeder. TXLSXWorksheet.CopyFrom ile TXLSXSheets.AddCopy, yeni sayfaya run listesinin kendi kopyasını verir; böylece bir klon üzerindeki ClearRowHeights orijinalin yüksekliklerini yerinde bırakır. Okuma tarafında UsedRange her run'ın ilk ve son satırına dek uzar, XLSX yazıcısının satır turu run'lar boyunca uzar; böylece yalnızca yükseklik taşıyan satırlar da yazılır
Gizli satırlar ve anahat düzeyleri satır başına tutulur
Run'lara ve overlay'lere çöken iki şey satır yükseklikleriyle miras kalan boş stillerdir. Gizli bayrakları, anahat düzeyleri ve daraltılmış durumlar satır başına kaydedilir. Gizli bir yinelenen satır ya da <table:table-row-group> içinde oturan bir satır, o attribute'lerin her biri için yinelenen satır başına bir girdi üretir; kayıtta da her girdi kendi başına bir sınırdır, dolayısıyla gizli kuşak satır başına bir <table:table-row> yazılır
Elinizdeki şablonlarda, sayfanın gizli ve gruplanmış bölümünü verinin gerektirdiği kadar dar tutun. Raporun altındaki yirmi satırı gizlemek bedavadır. 46. satırdan ızgaranın dibine dek her şeyi gizlemek, yüklemede bir milyon girdi ve kayıtta bir milyon satır elemanı demektir; görünen tek fark, artık kimsenin raporun ötesine kaydıramamasıdır
Hangi ODS özelliğinin nesne modelinin hangi parçasına oturduğu dahil, biçimin çalışma kitabı düzeyindeki yanı ODS dosyalarını açma ve kaydetme gezintisindedir; büyük dosyalar için öteki kaldıraçlar büyük çalışma kitabı performansı altında toplanır. Satır elemanının kendisi, tekrar ve stil attribute'leriyle, ODF 1.3 Part 3'ün tablo bölümünde tanımlanır
Bir milyon satırlık tekrar, yerel bir okuyucunun iyi modellemesi gereken bir biçim ayrıntısıdır; arkada onu yutacak bir ofis paketi yoktur. HotXLS Delphi spreadsheet component sayfası, okuyup yazdığı XLS, XLSX ve ODS özelliklerini desteklenen Delphi ve C++Builder sürümleriyle listeler