HotXLS kann ein Arbeitsblatt in einem bestehenden XLSX-Paket neu schreiben, ohne den Rest der Datei zu parsen oder neu zu komprimieren. TXLSDirectWriter.BeginPatch öffnet ein Quellpaket, kopiert jeden Eintrag außer dem Zielblatt mit seinen komprimierten Bytes wortgetreu hinüber und erlaubt es, genau dieses eine Blatt über die gewöhnlichen Aufrufe AddSheet, AddRow und Write* neu zu erstellen. Diagramme, PivotTable-Caches, Themes, Formatvorlagen und gemeinsame Zeichenketten werden dabei überhaupt nicht dekomprimiert
Der Arbeitsablauf, den dies löst, taucht bei Berichtswesen und Datenaktualisierung auf. Eine Arbeitsmappe kommt von einem Fachbereich mit PivotTables, Datenschnitten, bedingten Formatierungen und einem Jahrzehnt angesammelter Formatierung. Jede Nacht muss ein Datenblatt durch aktuelle Zahlen ersetzt werden. Die gesamte Arbeitsmappe zu laden und neu zu speichern kostet pro Datei Minuten und riskiert vor allem die Originaltreue bei Funktionen, die die Lade-Engine rekonstruieren muss. Patching umgeht beide Probleme, indem es nicht anfasst, was es nicht anfassen muss
Warum ist das Kopieren komprimierter Bytes der interessante Teil?
Ein Zip-Eintrag, der auf komprimierter Ebene kopiert wird, kostet eine reine Stream-Kopie. Derselbe Eintrag, der einen normalen Schreibpfad durchläuft, kostet ein Inflate beim Einlesen und ein Deflate beim Ausschreiben, und Deflate ist die teure Hälfte. Bei einer Arbeitsmappe mit einem großen PivotTable-Cache und einigen Dutzend eingebetteten Bildern ist dieser Unterschied der Unterschied zwischen einem Patch, der in der Zeit fertig wird, die zum Schreiben des neuen Blatts nötig ist, und einem, der die meiste Zeit damit verbringt, Bytes neu zu komprimieren, die er nie untersucht hat
HotXLS nutzt dafür CopyCompressedFrom, das die komprimierten Bytes des Quelleintrags direkt in das Zielarchiv schreibt. Kann ein Eintrag nicht auf diese Weise kopiert werden, etwa weil er eine andere Komprimierungsmethode oder schwache Verschlüsselung verwendet, greift der Writer auf eine dekomprimierte Stream-Kopie zurück, statt fehlzuschlagen. Verzeichnismarkierungs-Einträge werden übersprungen, da der Writer seine eigenen erzeugt
An Ort und Stelle ersetzen oder in eine neue Datei schreiben
Zwei Überladungen decken die beiden Formen ab, die diese Aufgabe annimmt. Die Inplace-Form legt das Ergebnis in einer temporären Datei neben dem Original an, schließt das Quellhandle, löscht dann und benennt um, sodass ein Absturz mitten im Schreibvorgang das Original unversehrt lässt. Die Form mit explizitem Zielpfad lässt die Quelle unangetastet und kann entweder ein Blatt ersetzen oder ein neues anhängen:
var
W: TXLSDirectWriter;
begin
W := TXLSDirectWriter.Create;
try
W.BeginPatch('monthly-dashboard.xlsx', 'Data'); // an Ort und Stelle
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;
Die Einfügevariante nimmt einen Quell- und einen Zielpfad sowie InsertSheet entgegen:
// Quelle bleibt unangetastet; Ziel erhält ein zusätzliches Arbeitsblatt namens 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;
Das Einfügen ist der Teil, der echte buchhalterische Feinarbeit erfordert. Der Writer parst die Blattregistrierung in xl/workbook.xml und die Beziehungszuordnung, die jedes Blatt an seinen Teil bindet, und wählt dann die nächste freie Teilnummer, Blattkennung und Beziehungskennung. Beziehungstypen folgen den Konventionen des Quellpakets, daher gibt das Patchen einer strikten ISO-29500-Arbeitsmappe strikte Beziehungstypen aus und das Patchen einer transitionalen entsprechend transitionale Typen
Was der Patch bewusst verwirft und einschränkt
Die Berechnungskette wird in beiden Modi verworfen. Im Ersetzungsmodus beschreiben ihre Einträge Zellen in einem Blatt, das in dieser Form nicht mehr existiert; im Einfügemodus macht die Verschiebung des Blattindex sie ohnehin ungültig. Excel baut die Kette bei der nächsten Neuberechnung wieder auf, daher ist ihr Weglassen korrekt und kein Verlust. Der Teil wird aus der Kopie ausgelassen, und sein Beziehungseintrag sowie seine Content-Type-Überschreibung werden gezielt entfernt
Zwei Erstellungssemantiken ändern sich innerhalb eines Patches, und beide folgen demselben Prinzip: Der Patch darf Teile, die er nicht neu geschrieben hat, nicht stören. Zeichenketten werden inline in das Blatt geschrieben statt der gemeinsamen Zeichenkettentabelle hinzugefügt, weil die Quelltabelle unangetastet hinüberwandert. Und StyleIndex verweist auf Einträge im cellXfs des Quellpakets, nicht auf eine vom Writer erstellte Formatvorlagentabelle. Das bedeutet, man kann Formate referenzieren, die die ursprüngliche Arbeitsmappe bereits definiert, was bei einer Datenaktualisierung meist genau das Gewünschte ist, bedeutet aber auch, dass man wissen muss, welcher Index welches Format trägt
// Innerhalb eines Patches indiziert StyleIndex das cellXfs des QUELLPAKETS.
// Ein Datum braucht einen expliziten Index, der dort auf ein Datumsformat verweist:
W.WriteDateTime(3, EncodeDate(2026, 8, 22), DateStyleIndexFromTemplate);
// Die formatlose WriteDateTime-Überladung wird im Patch-Modus abgewiesen,
// weil sie die eigene Formatvorlagentabelle des Writers voraussetzt, die
// ein Patch nie erstellt
Sechs Erstellungseinstiegspunkte sind gesperrt: das Hinzufügen von Tabellen, Diagrammen, Bildern, Kommentaren, definierten Namen und Zellformaten löst im Patch-Modus jeweils eine Exception aus, mit einem zweiten Sicherheitsnetz beim Schließen, das fehlschlägt, falls einer ihrer Zähler ungleich null ist. Jede dieser Funktionen würde das Bearbeiten von Teilen erfordern, die der Patch wortgetreu kopiert, und ein halb bearbeitetes Paket ist schlimmer als eine verweigerte Operation. Pro Vorgang darf genau ein Blatt gepatcht werden
Wann patchen und wann laden
Patchen ist das richtige Werkzeug, wenn die Arbeitsmappe groß ist, die Änderung auf ein Blatt beschränkt bleibt und der Rest der Datei bitgenau erhalten bleiben muss. Es ist das falsche Werkzeug, wenn die Änderung mehrere Blätter betrifft, wenn neue Formatierung oder neue Objekte nötig sind, oder wenn die Datei klein genug ist, dass ein normales Laden und Speichern nichts kostet. Für die massenhafte Erzeugung von Grund auf bleibt der in dem Streaming Direct Writer beschriebene Streaming-Pfad die bessere Wahl, und er teilt sich dieselbe AddRow- und Write*-API, sodass der Wechsel zwischen beiden mechanisch bleibt
Die Bearbeitung auf Blattebene innerhalb einer geladenen Arbeitsmappe, wenn wirklich das vollständige Objektmodell benötigt wird, wird in Duplizieren von Arbeitsblättern in XLSX-Paketen behandelt. Und falls der Grund, aus dem ein Patch erwogen wird, darin liegt, dass die Verarbeitung ganzer Arbeitsmappen langsam geworden ist, lohnt sich vor der Wahl eines Ansatzes ein Blick auf die Messwerte und das Speicherverhalten in Leistung großer Arbeitsmappen
Prüfen, ob ein Patch wirklich das Erwartete getan hat
Drei Prüfungen fangen fast jeden Fehler ab. Bestätigen, dass die Teile, die erhalten bleiben sollten, noch im Archiv vorhanden sind, dass xl/calcChain.xml verschwunden ist und dass das erneute Öffnen der Datei über TXLSXWorkbook die erwartete Blattanzahl meldet, unverändert bei einem Ersetzen und um eins erhöht bei einem Einfügen. Das gepatchte Blatt zurückzulesen und einige Werte und Formeln zu vergleichen schließt den Kreis
Ein Implementierungsdetail aus der Entwicklung dieser Funktion verdient es, wiederholt zu werden, weil es jeden treffen kann, der ähnlichen Code auf Zip-Ebene schreibt. Namen von Worksheet-Teilen werden per Präfix abgeglichen, und ein Off-by-one-Fehler in der Präfixlänge bedeutet, dass das Prädikat nie zutrifft, sodass ein neu geschriebener Teil mit einem vorhandenen Namen kollidiert und Reader, die den letzten Eintrag mit einem gegebenen Namen nehmen, still das falsche Blatt auswählen. Wenn ein Patch scheinbar die Inhalte zweier Blätter vertauscht, sollte zuerst der Namensabgleich geprüft werden, bevor man sich die XML ansieht
Inplace-Patching, Streaming-Schreibvorgänge und das vollständige Arbeitsmappen-Objektmodell sind in derselben Bibliothek für Delphi und C++Builder enthalten; die Funktionsliste findet sich auf der HotXLS-Delphi-Tabellenkalkulationskomponentenseite