Teknisk artikel

Patcha ett kalkylblad i en stor XLSX-fil från Delphi

HotXLS kan skriva om ett kalkylblad inuti ett befintligt XLSX-paket utan att tolka eller omkomprimera resten av filen. TXLSDirectWriter.BeginPatch öppnar ett källpaket, kopierar varje post utom målbladet med sina komprimerade byte ordagrant, och låter dig omauktorera just det bladet genom de vanliga anropen AddSheet, AddRow och Write*. Diagram, pivotcachar, teman, stilar och delade strängar dekomprimeras aldrig alls

Arbetsflödet som löses här dyker upp inom rapportering och dataförnyelse. En arbetsbok anländer från en affärsavdelning och bär pivottabeller, skivare, villkorsstyrd formatering och ett decennium av ackumulerad formatering. Varje natt måste ett datablad ersättas med färska siffror. Att läsa in och spara om hela arbetsboken kostar minuter per fil och, viktigare, riskerar trohet på funktioner som inläsningsmotorn måste rekonstruera. Patchning kringgår båda problemen genom att inte röra det den inte behöver röra

Varför är det just kopiering av komprimerade byte som är det intressanta?

En zip-post som kopieras på komprimerad nivå kostar en strömkopiering. Samma post som förs genom en normal skrivväg kostar en dekompression på vägen in och en komprimering på vägen ut, och komprimeringen är den dyra halvan. På en arbetsbok med en stor pivotcache och ett par dussin inbäddade bilder är den skillnaden skillnaden mellan en patch som blir klar på den tid det tar att skriva det nya bladet och en som spenderar det mesta av sin tid på att omkomprimera byte den aldrig undersökte

HotXLS använder CopyCompressedFrom för detta, som skriver källpostens komprimerade byte rakt in i målarkivet. När en post inte kan kopieras på det sättet, eftersom den använder en annan komprimeringsmetod eller svag kryptering, faller skrivaren tillbaka på en dekomprimerad strömkopiering istället för att misslyckas. Katalogmarkörsposter hoppas över, eftersom skrivaren producerar sina egna

Ersätt på plats, eller skriv till en ny fil

Två överlagringar täcker de två former denna uppgift tar. Formen på plats mellanlagrar resultatet i en temporär fil intill originalet, stänger källhandtaget, och tar sedan bort och byter namn, så att en krasch mitt under skrivningen lämnar originalet intakt. Formen med explicit mål lämnar källan orörd och kan antingen ersätta ett blad eller lägga till ett nytt:

var
  W: TXLSDirectWriter;
begin
  W := TXLSDirectWriter.Create;
  try
    W.BeginPatch('monthly-dashboard.xlsx', 'Data');   // på plats
    W.AddSheet('Data');
    W.AddRow(1);
    W.WriteString(1, 'Region');
    W.WriteString(2, 'Revenue');
    W.AddRow(2);
    W.WriteString(1, 'North');
    W.WriteNumber(2, 184320.55);
    W.AddRow(3);
    W.WriteFormula(1, '=SUM(B2:B2)');
    W.Close;
  finally
    W.Free;
  end;
end;

Infogningsvarianten tar en käll- och en målsökväg plus InsertSheet:

  // Källan förblir orörd; målet får ett extra kalkylblad kallat Extra
  W.BeginPatch('template.xlsx', 'output.xlsx', 'Extra', True);
  W.AddSheet('Extra');
  W.AddRow(1);
  W.WriteString(1, 'appended by the nightly job');
  W.Close;

Infogning är den del som kräver verklig bokföringskirurgi. Skrivaren tolkar bladregistret i xl/workbook.xml och relationskartan som binder varje blad till sin del, och väljer sedan nästa lediga delnummer, bladidentifierare och relationsidentifierare. Relationstyper följer källpaketets konventioner, så att patcha en strikt ISO 29500-arbetsbok skriver ut strikta relationstyper och att patcha en övergångsvis en skriver ut övergångsvisa typer

Vad patchen medvetet tar bort och begränsar

Beräkningskedjan kasseras i båda lägena. I ersättningsläge beskriver dess poster celler i ett blad som inte längre finns i den formen; i infogningsläge gör bladindexförskjutningen den rätt och slätt ogiltig. Excel bygger om kedjan vid nästa omräkning, så att kasta den är korrekt snarare än förlustbringande. Delen utelämnas från kopian, och dess relationspost och innehållstypsöverskrivning tas bort kirurgiskt

Två semantiska regler för auktorering ändras inuti en patch, och båda följer från samma princip: patchen får inte störa delar den inte skrev om. Strängar skrivs infogat i bladet snarare än läggas till i den delade strängtabellen, eftersom källtabellen förs över orörd. Och StyleIndex refererar till poster i källpaketets cellXfs, inte till en stiltabell skrivaren bygger. Det betyder att du kan referera till format den ursprungliga arbetsboken redan definierar, vilket vanligtvis är precis vad en dataförnyelse vill ha, men det betyder också att du måste veta vilket index som bär vilket format

// Inuti en patch indexerar StyleIndex KÄLLPAKETETS cellXfs.
// Ett datum behöver ett explicit index som mappar till ett datumformat där:
W.WriteDateTime(3, EncodeDate(2026, 8, 22), DateStyleIndexFromTemplate);

// Den stilfria WriteDateTime-överlagringen avvisas i patch-läge,
// eftersom den förutsätter skrivarens egen stiltabell, som en patch
// aldrig skapar

Sex ingångspunkter för auktorering är spärrade: att lägga till tabeller, diagram, bilder, kommentarer, definierade namn och cellstilar kastar alla ett undantag i patch-läge, med ett andra säkerhetsnät vid stängningstillfället som misslyckas om någon av deras räknare är skild från noll. Var och en av dessa funktioner skulle kräva att redigera delar patchen kopierar ordagrant, och ett halvredigerat paket är värre än en avvisad operation. Exakt ett blad får patchas per operation

När ska du patcha och när ska du läsa in?

Patchning är rätt verktyg när arbetsboken är stor, ändringen är begränsad till ett blad, och resten av filen måste överleva bit för bit. Det är fel verktyg när ändringen sträcker sig över flera blad, när ny formatering eller nya objekt behövs, eller när filen är tillräckligt liten att en normal inläsning och sparning kostar ingenting. För massgenerering från grunden förblir strömningsvägen som beskrivs i den strömmande direktskrivaren det bättre valet, och den delar samma AddRow- och Write*-API, så att röra sig mellan de två är mekaniskt

Bladnivåmanipulation inuti en inläst arbetsbok, när du verkligen vill ha hela objektmodellen, täcks i duplicering av kalkylblad i XLSX-paket. Och om skälet till att du överväger en patch är att bearbetning av hela arbetsboken har blivit långsam, är mätningarna och minnesbeteendet i prestanda för stora arbetsböcker värda att läsa innan du väljer metod

Att verifiera att en patch verkligen gjorde det du tror

Tre kontroller fångar nästan alla misstag. Bekräfta att de delar du förväntade dig skulle överleva fortfarande finns i arkivet, att xl/calcChain.xml är borta, och att öppna filen igen via TXLSXWorkbook rapporterar det bladantal du förväntar dig, oförändrat för en ersättning och ökat med ett för en infogning. Att läsa tillbaka det patchade bladet och jämföra några värden och formler sluter loopen

En implementationsdetalj från utvecklingen av den här funktionen förtjänar att upprepas, eftersom den kan bita alla som skriver liknande zip-nivåkod. Kalkylbladsdelnamn matchas efter prefix, och ett fel med en enhet i prefixlängden gör att predikatet aldrig matchar, så en nyskriven del kolliderar med ett befintligt namn och läsare som tar den sista posten med ett givet namn väljer tyst fel blad. Om en patch verkar byta ut innehållet i två blad, titta på namnmatchningen innan du tittar på XML:en

Patchning på plats, strömmande skrivningar och hela arbetsboksobjektmodellen levereras i samma bibliotek för Delphi och C++Builder; funktionslistan finns på sidan för HotXLS Delphi-kalkylbladskomponent