📦.xlsx – ein ZIP voller XML

Seit Excel 2007 ist die Standarddatei ein Office-Open-XML-Paket (ECMA-376, ISO/IEC 29500): ein ZIP-Archiv (50 4B 03 04) mit XML-Teilen, die über Beziehungen verbunden sind. Benenne eine .xlsx in .zip um – und du kannst hineinsehen.

🌳Paketbaum

Klicke auf einen Teil. Die Inhalte sind echt erzeugt (aus der kleinen Beispieltabelle) – so, wie sie in der ZIP-Datei stehen würden.
$ unzip -l umsatz.xlsx
xl/worksheets/sheet1.xml

Zellen des Blatts „Umsatz“: <sheetData> mit <row> und <c>.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
  <dimension ref="A1:D3"/>
  <sheetData>
    <row r="1">
      <c r="A1" t="s"><v>0</v></c>
      <c r="B1" t="s"><v>1</v></c>
      <c r="C1" t="s"><v>2</v></c>
      <c r="D1" t="s"><v>3</v></c>
    </row>
    <row r="2">
      <c r="A2" t="s"><v>4</v></c>
      <c r="B2"><v>4.99</v></c>
      <c r="C2"><v>2</v></c>
      <c r="D2"><f>B2*C2</f></c>
    </row>
    <row r="3">
      <c r="A3" t="s"><v>5</v></c>
      <c r="B3"><v>12.8</v></c>
      <c r="C3"><v>0.125</v></c>
      <c r="D3"><f>B3*C3</f></c>
    </row>
  </sheetData>
</worksheet>

📚 Quelle: ECMA-376 Teil 1 §18 (SpreadsheetML), Teil 2 (Open Packaging Conventions); Dateiliste abgeglichen mit einer von LibreOffice erzeugten .xlsx.

🔗Wie ein Leser die Zellen findet


 [Content_Types].xml ──► sagt, welcher Teil was ist (Content-Type je PartName)

 _rels/.rels
   └─ rId1  officeDocument ──► xl/workbook.xml
                                 └─ <sheet name="Umsatz" sheetId="1" r:id="rId1"/>
                                                                        │
 xl/_rels/workbook.xml.rels ◄───────────────────────────────────────────┘
   ├─ rId1  worksheet     ──► xl/worksheets/sheet1.xml   <c r="A1" t="s"><v>0</v></c>
   ├─ rId2  sharedStrings ──► xl/sharedStrings.xml       <si><t>Artikel</t></si>  ◄─ Index 0
   └─ rId3  styles        ──► xl/styles.xml              <cellXfs> ◄─ s="1"
🧭Keine festen Dateinamen
Nur [Content_Types].xml und _rels/.rels haben feste Namen. „sheet1.xml“ ist Konvention – maßgeblich ist die Beziehung.
🔢Reihenfolge der Blätter
Die Reihenfolge der Registerkarten bestimmt <sheets> in workbook.xml, nicht der Dateiname.
🗜️Komprimiert
XML ist geschwätzig, komprimiert (Deflate) aber sehr gut: Bei großen Tabellen ist eine .xlsx meist kleiner als die .xls. Winzige Mappen liegen gleichauf (Probe mit LibreOffice: 6 144 Byte .xls, 6 188 Byte .xlsx).

✍️Zellen → XML (Live-Editor)

Tippe in das Raster: Zahlen mit Komma, Texte, Formeln mit =, WAHR/FALSCH, Datumsangaben (TT.MM.JJJJ) oder Prozentwerte. Rechts entsteht das XML – die Zeile der gewählten Zelle ist markiert.
ABCD
1
2
3
4
5
leer (nicht gespeichert)t="s" Text → SSTZahl <v>Formel <f>t="b" Wahrheitswerts="1" Datum (Seriennummer)s="2" Prozent
Zelle D2
<c r="D2"><f>B2*C2</f></c>

Die Formel steht ohne „=“ in <f>. Ein Programm, das nicht rechnet (wie dieser Editor), lässt <v> weg und setzt in workbook.xml <calcPr fullCalcOnLoad="1"/> – dann rechnet Excel beim Öffnen alles neu (so macht es auch openpyxl).

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<dimension ref="A1:D5"/>
<sheetData>
<row r="1">
<c r="A1" t="s"><v>0</v></c>
<c r="B1" t="s"><v>1</v></c>
<c r="C1" t="s"><v>2</v></c>
<c r="D1" t="s"><v>3</v></c>
</row>
<row r="2">
<c r="A2" t="s"><v>4</v></c>
<c r="B2"><v>4.99</v></c>
<c r="C2"><v>2</v></c>
<c r="D2"><f>B2*C2</f></c>
</row>
<row r="3">
<c r="A3" t="s"><v>5</v></c>
<c r="B3"><v>12.8</v></c>
<c r="C3"><v>0.125</v></c>
<c r="D3"><f>B3*C3</f></c>
</row>
<row r="4">
<c r="A4" t="s"><v>4</v></c>
<c r="B4" t="b"><v>1</v></c>
<c r="C4" s="2"><v>0.15</v></c>
</row>
<row r="5">
<c r="A5" t="s"><v>6</v></c>
<c r="B5" s="1"><v>45292</v></c>
</row>
</sheetData>
</worksheet>

dimension ref="A1:D5" · Shared Strings: count=8 Verwendungen, uniqueCount=7 verschiedene Texte.

✅ Probe aufs Exempel
Das erzeugte XML ist nicht nur Anschauung: Zusammen mit [Content_Types].xml und den Beziehungen gezippt, öffnen openpyxl und LibreOffice die Datei und berechnen die Formeln (=B2*C2 → 9,98).

📚 Quelle: ECMA-376 Teil 1 §18.3.1.4 c (Cell), §18.18.11 ST_CellType, §18.4 Shared String Table, §18.8.30 numFmt (eingebaute Formate 0–49).

🏷️Zelltypen (Attribut t)

t=BedeutungBeispiel
nZahl (Standard, darf fehlen)<c r="B2"><v>4.99</v></c>
sShared String – <v> ist der Index<c r="A1" t="s"><v>0</v></c>
inlineStrText direkt in der Zelle<c r="A1" t="inlineStr"><is><t>Käse</t></is></c>
strText-Ergebnis einer Formel<c r="E1" t="str"><f>A1&amp;"!"</f><v>Artikel!</v></c>
bWahrheitswert 1/0<c r="B4" t="b"><v>1</v></c>
eFehlerwert<c r="F1" t="e"><f>1/0</f><v>#DIV/0!</v></c>
dDatum als ISO-8601-Text (selten, Excel schreibt es nicht standardmäßig)<c r="A9" t="d"><v>2024-01-01</v></c>

🎨Stile: vom s-Attribut zum Zahlenformat


 <c r="A5" s="1"><v>45292</v></c>
            │
            ▼  styles.xml
 <cellXfs>  xf[0] numFmtId="0"   (Standard)
            xf[1] numFmtId="14"  ──► eingebautes Format 14 = kurzes Datum (gebietsschemaabhängig)
            xf[2] numFmtId="9"   ──► eingebautes Format 9 = "0%"
 <numFmts>  eigene Formate ab numFmtId 164, z. B. <numFmt numFmtId="164" formatCode="yyyy-mm-dd"/>
            │
            ▼
 Anzeige: 01.01.2024  (gespeichert bleibt die Zahl 45292)
💡 Datum = Zahl + Format
In der Datei gibt es keinen Datumstyp in der Zelle: gespeichert wird die Seriennummer, das Zahlenformat macht daraus ein Datum. Wie die Seriennummer entsteht, zeigt der Datumsrechner.
⚠️ Formeln sind englisch
In <f> steht SUM(B2,B3) mit Komma als Argumenttrenner – auch wenn die deutsche Oberfläche=SUMME(B2;B3) anzeigt. Die Übersetzung passiert nur in der Anzeige.