Andreas Dölling: Zeichenkodierung in CSV-Dateien

Hallo,

ich erzeuge aus einer Datenbanktabelle CSV-Dateien zum Datenexport.
Die Daten sind UTF8-kodiert.
Wenn ich die heruntergeladene CSV-Datei nun beispielsweise mit OpenOffice oder Firefox öffne, dann erkennt die Anwendung allerdings nicht, daß es sich um UTF-8 handelt, sondern zeigt das Dokument zunächst als ISO-xxx-Dokument an, also mit fehlenden Sonderzeichen.
Ich muß die richtige Kodierung dann immer erst auswählen (dann werden natürlich alle Zeichen korrekt angezeigt).

Gibt es eine Möglichkeit, der CSV-Datei direkt den Zeichensatz mitzugeben?
Untenstehend mein bisheriger Code.

Und noch eine kleine Zusatzfrage: kann es sein, daß utf8_decode() native Zeilenumbrüche verändert, so daß nl2br() darauf nicht mehr anwendbar ist?

Thanx und ciao,
ein immer wieder vom Zeichensatzkram verwirrter Andreas

Der Code:
ob_start();
$csv_data = $db_handler->get_csv_dump($_POST["data_table"], stripslashes($_POST["field_separator"]), stripslashes($_POST["text_delimiter"]));
$file_name = $_POST["data_table"]."_".date("Y"."_"."m"."_"."d", time()).".csv";
$file_handle = fopen("/tmp/".$file_name, "w+");
fputs($file_handle, $csv_data);
fclose($file_handle);
$content_length = (int) filesize("/tmp/".$file_name);

// For more information on headers see user comments on
// http://de3.php.net/manual/en/function.header.php
header('Pragma: public');
header('Last-Modified: '.gmdate('D, d M Y H:i:s') . ' GMT');
header('Cache-Control: no-store, no-cache, must-revalidate'); // HTTP/1.1
header('Cache-Control: pre-check=0, post-check=0, max-age=0'); // HTTP/1.1
//header('Content-Transfer-Encoding: none');
header('Content-Transfer-Encoding: utf-8');
header('Content-Type: text/csv; name="'.$file_name.'"');
header("Content-Disposition: attachment; filename="$file_name"");
header("Content-length: $content_length");

readfile("/tmp/".$file_name);
unlink("/tmp/".$file_name);
ob_flush();
exit;

  1. hi,

    Gibt es eine Möglichkeit, der CSV-Datei direkt den Zeichensatz mitzugeben?

    zutreffende Byte Order Mark für UTF-8 an den anfang der datei schreiben ...?

    Und noch eine kleine Zusatzfrage: kann es sein, daß utf8_decode() native Zeilenumbrüche verändert, so daß nl2br() darauf nicht mehr anwendbar ist?

    nicht, dass ich wüsste.

    gruß,
    wahsaga

    --
    /voodoo.css:
    #GeorgeWBush { position:absolute; bottom:-6ft; }
    1. Hallo,

      zutreffende Byte Order Mark für UTF-8 an den anfang der datei schreiben ...?

      das ist doch mal eine Anregung! Na klar, der berühmte "BOM".
      ;)

      Habe unter http://de.php.net/mbstring folgendes gefunden:
      $unicode_str_for_Excel = chr(255).chr(254).mb_convert_encoding( $utf8_str, 'UTF-16LE', 'UTF-8');

      Damit funktioniert es (Umlaute sofort korrekt dargestellt). Allerdings wird dann die Kodierung an sich von Firefox gar nicht erkannt und von OpenOffice "Unicode" und nicht "UTF-8".

      Ist das denn überhaupt richtig so?
      Würde es nicht reichen, wenn ich meinen UTF-8-kodierten Daten nur den BOM voranstelle? Also auf das mb_convert() verzichte?

      Und sind die Zeichenwerte 255 und 255 als BOM überhaupt richtig?

      Thanx und ciao,
      Andreas

      1. Hello,

        CSV ist auch nicht gleich CSV

        Allegemein akzeptiert ist z.B:

        "wert1","wert2","dies ist ein ""zitat"" CRLFeines Textes","","","wert6"CRLF

        wobei CRLF für Zeilenumbruch steht.
        Allerdings wird nur das letzte CRLF als Satzende aufgefasst, weil es außerhalb der Delimiter steht.

        PHP hat übrigens die Begriffe des SDF (Standard Data Foramt) verschandelt.

        SDF          PHP
        -----------+--------------
        Seperators => Delimiter
        Delimitors => Enclosures

        Viele Datenbanken nutzen aber die Sprechweise:

        export .... to .... seperated by ',' delimeted with """

        Wenn Du CSV-Datein so erzeugst, wie oben gezeigt (Delimiter durch Doppelung maskieren), dann sind diese auch von Excel, Access, dBase, etc lesbar.

        Harzliche Grüße aus http://www.annerschbarrich.de

        Tom

        --
        Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
        Nur selber lernen macht schlau
        1. Hallo,

          Wenn Du CSV-Datein so erzeugst, wie oben gezeigt (Delimiter durch Doppelung maskieren), dann sind diese auch von Excel, Access, dBase, etc lesbar.

          Harzliche Grüße aus http://www.annerschbarrich.de

          Tom

          danke für den Hinweis.
          Die Auswahl der Text- und Felddelimiter habe ich aber in meiner Anwendung dem Benutzer überlassen (ähnlich wie bei phpMyAdmin).
          Nur das Datensatzende habe ich festgelegt mit "\n".

          Ciao,
          Andreas

          1. Hello,

            Die Auswahl der Text- und Felddelimiter habe ich aber in meiner Anwendung dem Benutzer überlassen (ähnlich wie bei phpMyAdmin).
            Nur das Datensatzende habe ich festgelegt mit "\n".

            Das funktioniert trotzdem oft, wenn DU nur beachtest, dass Du den Felddelimiter (sdf-Sprache), wenn er IM Feldinhalt vorkommt,  durch Doppelung maskierst und nicht durch Backslash.

            Harzliche Grüße aus http://www.annerschbarrich.de

            Tom

            --
            Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
            Nur selber lernen macht schlau
            1. Hallo,

              Das funktioniert trotzdem oft, wenn DU nur beachtest, dass Du den Felddelimiter (sdf-Sprache), wenn er IM Feldinhalt vorkommt,  durch Doppelung maskierst und nicht durch Backslash.

              Aus:
              Dies ist ein "Test"!
              wird also:
              "Dies ist ein ""Test""!"?

              Das wußte ich nicht. Danke für den Hinweis! Habe mein Script entsprechend um einen regulären Ausdruck erweitert, der die Verdoppelung von innerhalb der Textdaten vorkommenden Feldbegrenzern (geht doch) vornimmt.

              Thanx,
              Andreas

              1. Hello,

                Aus:
                Dies ist ein "Test"!
                wird also:
                "Dies ist ein ""Test""!"?

                Ja. So wird das auch von der PHP fgetcsv()-Funktion gehandhabt.

                CSV-Dateien kann man nicht mehr mit file() oder fget() einlesen, da diese an den Zeilenumbrüchen, die innerhalb der Begrenzer stehen könnten (also im Feldinhalt) scheitern würden. Man hätte dann zwar "getrennte Inhalte" aber man wüsste nicht mehr, welche Daten zusammengehören.

                Harzliche Grüße aus http://www.annerschbarrich.de

                Tom

                --
                Fortschritt entsteht nur durch die Auseinandersetzung der Kreativen
                Nur selber lernen macht schlau
      2. OK, habe jetzt eine sehr aufschlußreiche Seite gefunden: http://www.unicode.org/faq/utf_bom.html#22.

        Entsprechend sieht die betreffende Zeile meines Scripts jetzt so aus:
        $csv_data = chr(0xef).chr(0xbb).chr(0xbf).$csv_data;

        Damit klappt alles wie am Schnürchen.

        Danke noch mal für den Hinweis mit dem BOM!

        Ciao,
        Andreas