Kategorien
Development

PHP-generierte Dateien Excel-konform ausgeben

Dieser Post sollte ursprünglich zur Laravel-Kurztipp-Serie gehören, hat aber mit viel mehr als nur mit Laravel zu tun.

CSV-Dateien (Comma-Separated Values) lassen sich mit PHP relativ leicht erzeugen. Sie sind quasi Excel-Tabellen, wobei jede Zeile durch eine Textzeile dargestellt wird und die Spalten durch ein Komma (oder ein anderes definiertes Zeichen) separiert werden. Solch eine Datei kann zB. so aussehen:

Name;Vorname;Alter
Mustermann;Max;24
Müller;Marlies;39

Diese Datei kann man mit PHP theoretisch leicht erzeugen und ausgeben. In Realität begegnet man aber zahlreichen Problemen, auf die ich im Nachfolgenden eingehen will:

Der richtige Zeichensatz

Nun kommen wir zum ersten Problem. Excel (auf dem Mac) unterstützt kein UTF-8. Aus diesem Grund müssen wir unseren Inhalt in UTF16LE umwandeln:

mb_convert_encoding($csv, 'UTF-16LE', 'UTF-8');

Es gibt im Internet eine längere Diskussion, welcher Zeichensatz nun der Beste ist und welche Vorgehensweise am besten funktioniert. Falls ihr mit meiner Lösung Probleme haben solltet, könnt ihr das zum einen gerne in den Kommentaren zum Ausdruck bringen, könnt euch aber auch diese Diskussion durchlesen und die alternativen Lösungsansätze ausprobieren und mir hier über eure Erfolge berichten.

Header

Nun muss noch der richtige Header ausgegeben werden, damit der Browser weiß, dass es sich um eine CSV-Datei handelt. Da er diese nicht selber anzeigen kann, startet er den download.

header('Content-Type: text/csv; charset=UTF-16LE');
header('Content-Disposition: attachment; filename=PJT-Adressen.csv');
header('Pragma: no-cache');
header('Expires: 0');

BOM – Byte Order Mark

Der Zeichensatz und Header reichen aber noch nicht aus und so kann es noch immer zu Problemen kommen. Excel will am Anfang einer CSV-Datei eine sogenannte Bytereihenfolge-Markierung (engl. BOM: Byte Order Mark) um das Dokument richtig zu interpretieren. Der Grund dafür ist relativ simpel und hat damit zu tun, dass Zeichen in UTF16 mehrere Bytes lang sein können. Das näher zu erklären würde aber diesen Artikel sprengen, weshalb ich hier aus den Wikipedia-Artikel verweise (ist ja keine Doktorarbeit hier – außerdem ist der Artikel recht brauchbar).

Dieses BOM für UTF16LE gibt man wie folgt aus:

echo chr(255) . chr(254)

Alternative: UTF8 mit BOM

Excel für Mac hat Probleme mit UTF-8. Numbers und Google Spreadsheets aber scheinbar nicht. Kann man also auf Excel für Mac verzichten kann man das ganze auch als UTF-8 ausgeben:

// Header Ausgabe
header('Content-Type: text/csv; charset=utf-8');
header('Content-Disposition: attachment; filename=PJT-Adressen.csv');
header('Pragma: no-cache');
header('Expires: 0');

// BOM
echo chr(239) . chr(187) . chr(191);

// CSV-Date
echo $csv;

Ich persönlich habe mit dieser Variante gute Erfahrungen gemacht, da wenige Leute wirklich Excel auf dem Mac nutzen und die meisten auch Numbers oder ähnliches installiert haben.

Weitere Problemlösungen

Leider ist das ganze nicht so einfach. Mit der oben beschriebenen Lösung hat es bei mir auf den meisten Plattformen geklappt. Fakt ist allerdings, dass es scheinbar keine Lösung gibt, die überall funktioniert.

Diese Tabelle (Quelle) zeigt die Kompatibilität von Excel auf verschiedenen Plattformen mit verschiedenen Zeichensätzen an:

Encoding  BOM      Win                            Mac
--------  ---      ----------------------------   ------------
utf-8     --       scrambled                      scrambled
utf-8     BOM      WORKS                          scrambled
utf-16    --       file not recognized            file not recognized
utf-16    BOM      file not recognized            Chinese gibberish
utf-16LE  --       file not recognized            file not recognized
utf-16LE  BOM      characters OK,                 same as Win
                   row data all in first field

Eine Lösung die ich gefunden habe ist es, einen TAB anstatt des Semicolons zu verwenden. Das Löst die Probleme mit UTF16LE, führt aber beim Abspeichern usw. eventuell zu neuen Problemen.

Andere empfehlen WINDOWS-1252 als Zeichensatz zu verwenden. Hier fehlt dann allerdings das €-Zeichen.

Hilfreiche Diskussionen finden sich hier, hier, und hier.

Die optimale Lösung

.. habe ich leider nicht! Falls jemand von euch eine Lösung gefunden hat, die auf beiden Plattformen gut funktioniert darf er sie gerne teilen. Bisher fehlt mir diese leider. Ich persönlich bin bei UFT8 geblieben, da in meiner Anwendung keine Mac-User mit Excel das System benutzen. Müsste ich beide Systeme bedienen würde ich vermutlich eine Weiche einbauen, die je nach Betriebssystem die Datei als UFT8+BOM (für Windows) oder UFT16LE+BOM mit Tabs als Begrenzer (für Mac) ausgibt.

Von Luke

Blogautor, Webdesigner, Programmierer, Tontechniker, Kameramann, Musiker, Christ, und vieles mehr

1 Antwort auf „PHP-generierte Dateien Excel-konform ausgeben“

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.