Sljedbenik shared formule u XLSX-u ne nosi tekst formule. Njegov element <f t="shared" si="N"/> pokazuje na master ćeliju negdje drugdje u listu, a čitač mora ponovno izgraditi tekst pomicanjem master formule za razliku u retku i stupcu. HotXLS Component za Delphi i C++Builder obavlja to širenje pri otvaranju, tako da svaki sljedbenik prijavljuje potpunu formulu
Ako ste ikad učitali stvaran XLSX u biblioteci trećih strana i otkrili da stupac od tisuću formula ima tekst u točno jednoj ćeliji, a prazne nizove u ostalih 999, susreli ste ovu značajku s pogrešne strane. Ništa nije oštećeno. Datoteka radi ono što ECMA-376 dopušta, a čitač je jednostavno stao na mjestu gdje je XML stao
Zašto je ćelija shared formule prazna?
Zato što format namjerno pohranjuje formulu jednom. U ECMA-376 dijelu 1 i ISO/IEC 29500-1, element <f> (§18.3.1.40) nosi atribut t tipa ST_CellFormulaType, a vrijednost shared znači da ova ćelija sudjeluje u skupini identificiranoj atributom si. Točno jedna ćelija u skupini, master, nosi i atribut ref koji daje raspon na koji se skupina primjenjuje, i samo ta ćelija nosi tekst formule kao sadržaj elementa. Svaka druga ćelija u skupini je sljedbenik. Ponavlja t="shared" i isti si, a sadržaj njezina elementa je prazan. Excel agresivno piše te skupine, jer se popunjavanje prema dolje preko stupca od 200 000 redaka sažima s 200 000 nizova formula na jedan niz plus 199 999 sićušnih rezervnih elemenata. Ušteda je stvarna, a trošak u potpunosti pada na čitača: bez širenja, sljedbenik nema značenje sam po sebi
Pomak je prijevod, a ne kopija teksta
HotXLS razrješava sljedbenika lociranjem mastera registriranog pod istim si, izračunavanjem delte retka i stupca od sidra mastera do trenutne ćelije, i prevođenjem svake reference u master formuli za tu deltu. Relativne dimenzije se pomiču, apsolutne ne, a mješovite reference pomiču samo svoju neapsolutnu polovicu. Literali nizova se u potpunosti preskaču, pa formula koja slučajno sadrži tekst "A1" zadržava taj tekst nepromijenjen u svakom sljedbeniku
const
// xl/worksheets/sheet1.xml, trimmed to the interesting cells
SheetXml: WideString=
'<row r="1"><c r="A1"><v>1</v></c>'+
'<c r="B1"><f t="shared" si="4" ref="B1:B3">'+
'A1+$A$1+A$1+$A1+"A1"+SUM(A1:A2)</f><v>7</v></c></row>'+
'<row r="2"><c r="B2"><f t="shared" si="4"/><v>8</v></c></row>'+
'<row r="3"><c r="B3"><f t="shared" si="4"></f><v>9</v></c></row>';
var
Wb: TXLSXWorkbook;
Sh: TXLSXWorksheet;
begin
Wb:= TXLSXWorkbook.Create;
try
Wb.Open(FileName);
Sh:= Wb.Sheets[1];
// Master, verbatim
// B1 -> A1+$A$1+A$1+$A1+"A1"+SUM(A1:A2)
// Follower one row down: relative row moves, absolute row frozen,
// the mixed A$1 keeps its row, and the literal stays a literal
// B2 -> A2+$A$1+A$1+$A2+"A1"+SUM(A2:A3)
ShowMessage(Sh.Cells[2, 2].Formula);
finally
Wb.Free;
end;
end;
Atribut ref je vrata, ne ukras. Sljedbenik čije koordinate padaju izvan primjenjivog raspona mastera se ne širi, jer datoteka tada daje tvrdnju koju skupina ne podržava. Isto tako, kad bi pomak gurnuo referencu iznad retka jedan ili lijevo od stupca A, HotXLS emitira #REF! za taj token umjesto tihog stezanja, što je ono što bi sam Excel proizveo za istu izmjenu. Ovaj prijevod je bliski rođak, ali ne i ista stvar, kao ponovno pisanje referenci koje se događa kad umećete ili brišete retke. Taj put ima vlastita pravila o tome što raspon radi kad ga uređivanje presiječe, i opisan je zasebno u članku o prilagodbi referenci formula pri umetanju i brisanju. Shared širenje je jednostavnije: to je čist pomak od poznatog sidra, primijenjen jednom, pri parsiranju
Koje oblike referenci pomicač mora pokriti?
Sve, ili je širenje bug gubitka podataka u krinci. Naivan pomicač koji razumije samo A1 i A1:B2 oštetit će ili odbaciti egzotičnije oblike, a prave radne bilježnice pune su njih. HotXLS prevoditelj shared formule prepoznaje cijelu A1 obitelj prije nego odluči što pomaknuti. Vanjske reference radne bilježnice poput [Book.xlsx]Sheet1!A1 i 3D reference poput Sheet1:Sheet3!A1 zadržavaju svoj prefiks netaknut dok se prateća referenca ćelije pomiče. Imena listova u navodnicima prežive, uključujući ružan slučaj gdje je list doslovno nazvan A1, pa se 'A1'!A1 pomiče samo u dijelu nakon uskličnika. Referenca cijelog stupca A:A pomiče svoju dimenziju stupca i ništa drugo; referenca cijelog retka 1:1 pomiče svoju dimenziju retka i ništa drugo; $A:$A se uopće ne pomiče. Strukturirane reference tablice poput Table[A1] ostaju netaknute, jer je dio u uglatim zagradama ime stupca, ne koordinata
// One master, expanded two columns to the right and zero rows down.
// Master D1: A1+A:A+$A:$A
// F1 : C1+C:C+$A:$A
//
// One master, expanded three rows down and zero columns across.
// Master A1: B1+$C$1+"A1"+A:A+1:1+'Data'!A1+LOG10(A1)+Table[A1]+'A1'!A1
// A3 : B3+$C$1+"A1"+A:A+3:3+'Data'!A3+LOG10(A3)+Table[A1]+'A1'!A3
//
// Note what did NOT move in the second line: the absolute $C$1, the
// string literal "A1", the whole column A:A under a pure row delta,
// the function name LOG10, and the structured reference Table[A1]
Imena funkcija su tiha zamka ovdje. Skener tokena koji zgrabi slova praćena znamenkama rado bi prepisao LOG10 u LOG11 jedan redak niže. HotXLS zahtijeva granicu reference prije kandidatnog tokena i nakon njega, pa identifikator koji se nastavlja u slovo, znamenku, podvlaku, točku ili otvarajuću zagradu nije referenca ćelije. Ako radite u drugoj obitelji notacija, isti problem granice pojavljuje se drukčije, a članak o R1C1 notaciji pokriva gdje se dva modela razilaze
Zašto samozatvarajući element f proguta sljedeću vrijednost?
Zato što samozatvarajući element ne proizvodi nikakav događaj kraja elementa. Ovo je jednostavno najskuplji bug u cijeloj značajki, i nije specifičan ni za jedan XML parser. U TXMLReader, <f t="shared" si="4"/> podiže točno jedan događaj Element s postavljenim IsEmptyElement na True, i nikad ne podigne odgovarajući EndElement. Parser koji zatvara svoje stanje hvatanja formule samo na EndElement stoga ostaje unutar formule, a sljedeći tekst koji vidi, koji je predmemorirani rezultat unutar <v>, dodaje se u spremnik formule. Gore, stanje preživljava granicu ćelije, pa sljedeća ćelija koja posjeduje pravi <f> ima svoj tekst formule progutan prethodnom ćelijom. Popravak je završiti stanje formule na samom događaju Element kad god je IsEmptyElement True, i pokrenuti cijelo razrješavanje sljedbenika ondje umjesto čekanja. To znači čitanje t, si, ref, aca i ca iz atributa, primjenu shared širenja, pisanje atributa ponovnog izračuna na ćeliju, i čišćenje shared stanja, sve unutar grane koja obrađuje prazan element. Primijetite da format dopušta oba pravopisa, <f t="shared" si="4"/> i <f t="shared" si="4"></f>, a drugi doista podiže EndElement. Ispravan čitač mora obraditi taj par identično, zato HotXLS pokriva oba pravopisa u istoj regresijskoj datoteci
Rijetki, nesortirani si vrijednosti i red čekanja na čekanju
Atribut si je nepredznačen cijeli broj koji nudi datoteka, ne pozicija polja koju kontrolirate. Ništa u shemi ne zahtijeva da shared indeksi budu gusti, počinju od nule, ili se pojavljuju uzlaznim redoslijedom, i ništa ne sprječava neprijateljsku ili tek čudnu datoteku da koristi si="4294967290" na prvoj ćeliji. Određivanje veličine polja pretrage prema najvećem opaženom si je stoga primitiv iscrpljivanja memorije, ne optimizacija. HotXLS umjesto toga drži put otvaranja radne bilježnice na sortiranoj rijetkoj tablici: shared skupine se registriraju pod svojim cjelobrojnim ključem u sortiranoj TStringList, što pretragu čini binarnim pretraživanjem preko koliko god skupina zapravo postoji, bez veze s brojčanom veličinom indeksa. Redoslijed je druga polovica problema. Master obično prethodi svojim sljedbenicima u redoslijedu dokumenta, ali to je konvencija, a ne pravilo, pa svaki sljedbenik koji ne može razriješiti svoj si u trenutku parsiranja ide u red čekanja na čekanju. Kad list završi, red se ponovno reproducira prema sad potpunoj tablici, i kasni masteri razrješavaju svoje siročadi. Ćelije koje nikad ne pronađu mastera zadržavaju praznu formulu, što je pošten ishod za datoteku koja referencira skupinu koju nikad nije definirala
Širenje shared formula bez učitavanja radne bilježnice
Strujni čitači suočavaju se s istim zahtjevom pod mnogo tješnjim proračunom memorije, i rješavaju ga tablicom lokalnom za list. TXLSDirectReader i TXLSRowCursor oba šire sljedbenike u potpune formule po ćeliji dok čuvaju svoje ograničeno-memorijsko i projekcijsko ponašanje, tako da čak i prolaz unaprijed preko lista od 300 MB predaje pravi tekst formule
var
Reader: TXLSDirectReader;
Cursor: TXLSRowCursor;
begin
// Projection: only rows 2..3, only column A. The master lives in row 1,
// outside the projection, and is still parsed so the followers resolve
Reader:= TXLSDirectReader.Create;
try
Reader.FirstRow:= 2;
Reader.LastRow:= 3;
Reader.IncludeColumn(1);
Reader.OnCell:= HandleCell; // Cell.Formula is fully expanded here
Reader.ReadFile(FileName);
finally
Reader.Free;
end;
// Forward-only row traversal, same expansion
Cursor:= TXLSRowCursor.Create;
try
Cursor.Open(FileName);
if Cursor.FindFirst then
repeat
if Cursor.CellCount > 0 then
WriteLn(Cursor.RowIndex, ': ', Cursor.Cells[0].Formula);
until not Cursor.FindNext;
finally
Cursor.Free;
end;
end;
Dva ograničenja proizlaze iz tog dizajna. Prvo, projekcija nikad ne smije preskočiti mastera. Filtar retka postavljen s FirstRow i LastRow, ili filtar stupca izgrađen s IncludeColumn, može preskočiti emitiranje master ćelije vašem povratnom pozivu, ali parser i dalje mora zabilježiti njezin si, koordinate sidra, primjenjivi raspon i tekst formule, inače se svaki sljedbenik unutar projekcije razrješava ni na što. Samo je posao na strani sljedbenika, pomak i dekodiranje vrijednosti, siguran za preskočiti. Drugo, tablica je po listu, a njezin životni vijek se mora eksplicitno upravljati: TXLSRowCursor drži jednu instancu za trajanje prolaska kroz list i briše je pri ponovnom pokretanju, promjeni lista, kraju datoteke, iznimci i zatvaranju, tako da skupina definirana na listu jedan nikad ne može procuriti na list dva. Budući da je strujni put vruća petlja, koristi hash cijelih brojeva s otvorenim adresiranjem umjesto sortirane tablice nizova, što izbjegava pretvorbu cijeli broj-u-niz po ćeliji
Što se događa pri spremanju i gdje su granice
Kad je sljedbenik jednom proširen, on je obična formula, a HotXLS je zapisuje natrag kao neovisan element <f> bez t="shared" i bez si. Round-trip je stabilan, a predmemorirani rezultati <v> prežive, ali izlaz je veći od ulaza za jako dijeljeni list, i grupiranje koje je Excel stvorio ne rekonstruira se pri spremanju. Ako vam je bajtna vjernost shared skupina važnija od stvarnog teksta formule u svakoj ćeliji, to je razmjena koju prihvaćate. XLS strana je drukčija, usput budi rečeno: BIFF8 zapis SHRFMLA ima vlastito kodiranje i vlastiti pisac, s prekidačem shared skupine na radnoj bilježnici
Dvije povezane stvari nisu shared formule iako dijele element <f>. Naslijeđene CSE formule polja koriste t="array" s ref koji pokriva usidreni raspon, a dinamička polja koriste isti pravopis t="array", ali se identificiraju atributom cm koji lanči kroz cellMetadata do zapisa XLDAPR. Tretiranje ćelije prelijevanja dinamičkog polja kao shared ili CSE sljedbenika je prava greška ispravnosti, a razdvajanje je pokriveno u članku o dinamičkim poljima i formulama prelijevanja. Čitajte tri slučaja kao tri parsera koja slučajno dijele ime oznake, i kôd ostaje iskren
Širenje shared formula, strujni čitači i prevoditelj referenci opisani ovdje isporučuju se kao dio HotXLS Excel komponente za Delphi i C++Builder; produktna stranica sadrži potpunu referencu formula i API-ja izravnog čitanja, uključujući svojstva projekcije korištena gore