Sonja: CSV-Import mit fgetcsv

Hallo,

ich habe ein Problem mit der Anwendung und/oder Verständnis von fgetcsv. Wenn ich mir meine CSV zB. mit Notepad++ anschaue sehe ich die Datei wie folgend:

Öffentlicher Identifikationscode;Privater Identifikationscode  
676b156bd8574d1e904c663687a66ed5;01952566952840098761a16159c90f56  
152cd3a4c21e4e398076f25b47b81255;04350e1648114f1095a16b3d03882e78

Schaue ich mir das ganze im Windowseditor an:

Öffentlicher Identifikationscode;Privater Identifikationscode676b156bd8574d1e904c663687a66ed5;01952566952840098761a16159c90f56152cd3a4c21e4e398076f25b47b81255;04350e1648114f1095a16b3d03882e78  

also ohne Zeilenumbruch.

Wie kann ich nun den fgetcsv dazu bringen den Inhalt wie in Notepad sichbar korrekt zu trennen. Das ";" taugt ja nur als Feld-Trennzeichen, oder?

Mein Versuch mit:

$row = 0;  
$handle = fopen ("import-test.csv","r");  
while ($data = fgetcsv ($handle, 99999, ';')) {  
	var_dump($data);  
}  
fclose ($handle);

Brachte leider nichts gutes dabei heraus:

array(102) {  
  [0]=>  
  string(33) "Öffentlicher Identifikationscode"  
  [1]=>  
  string(61) "Privater Identifikationscode  
676b156bd8574d1e904c663687a66ed5"  
  [2]=>...

Dankeschön

Sonja

  1. Tach!

    Schaue ich mir das ganze im Windowseditor an:
    also ohne Zeilenumbruch.

    Der Windows-Editor möchte ein Windows-Zeilenende haben, bestehend aus \r\n. Unter Unixoiden ist nur \n üblich.

    Wie kann ich nun den fgetcsv dazu bringen den Inhalt wie in Notepad sichbar korrekt zu trennen.

    fgetcsv() liest nur, das schreibt nichts.

    dedlfix.

    1. Hello Dedlfix,

      Der Windows-Editor möchte ein Windows-Zeilenende haben, bestehend aus \r\n. Unter Unixoiden ist nur \n üblich.
      fgetcsv() liest nur, das schreibt nichts.

      Ist dir da unterschiedliches Verhalten von fgetcsv() bekannt, je nachdem auf welcher Plattform es verwendet wird? Mir ist da bisher nichts aufgefallen. Ich war immer der Meinung, dass alle üblichen Zeilenendezeichen erkannt werden, sogar bei gemischter Verwendung.

      Sollte man das nochmal ausprobieren oder im Source-Code nachsehen?

      Liebe Grüße aus dem schönen Oberharz

      Tom vom Berg

      --
       ☻_
      /▌
      / \ Nur selber lernen macht schlau
      http://bergpost.annerschbarrich.de
      1. Ist dir da unterschiedliches Verhalten von fgetcsv() bekannt, je nachdem auf welcher Plattform es verwendet wird? Mir ist da bisher nichts aufgefallen. Ich war immer der Meinung, dass alle üblichen Zeilenendezeichen erkannt werden, sogar bei gemischter Verwendung.

        fgetcsv() interessiert sich gar nicht für die Zeilenenden ;) das macht fopen() - aber dem ist das Zeilenende beim Lesen egal, nur beim Schreiben eben nicht, da muss man sie per Hand setzten. Beim Lesen von Binärdaten kann man fopen dazu bringen, die Zeilenenden zu ignorieren (oder Codefragmente die dort so aussehen) indem man das b-Flag nutzt.

        1. Hello,

          Ist dir da unterschiedliches Verhalten von fgetcsv() bekannt, je nachdem auf welcher Plattform es verwendet wird? Mir ist da bisher nichts aufgefallen. Ich war immer der Meinung, dass alle üblichen Zeilenendezeichen erkannt werden, sogar bei gemischter Verwendung.

          fgetcsv() interessiert sich gar nicht für die Zeilenenden ;) das macht fopen() - aber dem ist das Zeilenende beim Lesen egal, nur beim Schreiben eben nicht, da muss man sie per Hand setzten. Beim Lesen von Binärdaten kann man fopen dazu bringen, die Zeilenenden zu ignorieren (oder Codefragmente die dort so aussehen) indem man das b-Flag nutzt.

          Das musst Du mir jetzt bitte mal näher erklären. fopen() sollte es doch egal sein, was im File drinsteht. Lesen muss ein fread() innerhalb des fgetcsv() erst nach dem Einlesen eines Datenblockes kann dieser auf seinen Inhalt hin (z.B. Zeilenendezeichen) untersucht werden.

          Außerdem kann man auch nach dem Öffnen (Rückgabe _ein_ Handle) noch den Inhalt über ein anderes Handle verändern. Das würde eine Erkennung per fopen() ad absurdum führen.

          Liebe Grüße aus dem schönen Oberharz

          Tom vom Berg

          --
           ☻_
          /▌
          / \ Nur selber lernen macht schlau
          http://bergpost.annerschbarrich.de
      2. Tach!

        Der Windows-Editor möchte ein Windows-Zeilenende haben, bestehend aus \r\n. Unter Unixoiden ist nur \n üblich.
        fgetcsv() liest nur, das schreibt nichts.
        Ist dir da unterschiedliches Verhalten von fgetcsv() bekannt, je nachdem auf welcher Plattform es verwendet wird?

        Ist mir nichts bekannt, das PHP-Handbuch erwähnt nur alte Mac-Zeilenenden als problematisch.

        fgetcsv() und der Windows-Editor sind zwei verschiedene Paar Schuhe. Die beiden zitierten Zeilen stehen nicht im Zusammenhang miteinander. fgetcsv() wird nicht das Problem sein, sondern dass die Daten anderenorts mit für den Windows-Editor nicht verwendbaren Zeilenenden erzeugt wurden. Die Lösung wäre, den Windows-Editor zu meiden und einen zu verwenden, der mit (den höchstwahrscheinlich verwendeten) Unix-Zeilenumbrüchen umgehen kann, oder die CSV-Daten gleich mit DOS-Zeilenenden zu erzeugen.

        Man könnte höchstens fputcsv() vorwerfen, dass es nur \n als Zeilenumbruch schreibt (falls diese Funktion Verwendung fand).

        dedlfix.

  2. Hello,

    ich habe ein Problem mit der Anwendung und/oder Verständnis von fgetcsv. Wenn ich mir meine CSV zB. mit Notepad++ anschaue sehe ich die Datei wie folgend:

    Öffentlicher Identifikationscode;Privater Identifikationscode

    676b156bd8574d1e904c663687a66ed5;01952566952840098761a16159c90f56
    152cd3a4c21e4e398076f25b47b81255;04350e1648114f1095a16b3d03882e78

    
    >   
    > Schaue ich mir das ganze im Windowseditor an:  
    > ~~~xml
    
    Öffentlicher Identifikationscode;Privater Identifikationscode676b156bd8574d1e904c663687a66ed5;01952566952840098761a16159c90f56152cd3a4c21e4e398076f25b47b81255;04350e1648114f1095a16b3d03882e78  
    
    > 
    
    

    also ohne Zeilenumbruch.

    Dann solltest Du als erstes mal die Datei in einer Hex-Darstellung ansehen und feststellen, was wirklich drin ist.

    • Welche Zeilenumbruchzeichen?  "\n" oder "\r\n", ...
    • Welche Codierung?  ISO-8859-1, UTF, ...

    Und dann lies dir die Anleitung nochmal aufmerksam durch:
    http://de2.php.net/manual/en/function.fgetcsv.php

    Dort wird auch von eventuellen Problemem mit Zeilenenden gesprochen.
    http://de2.php.net/manual/en/filesystem.configuration.php#ini.auto-detect-line-endings

    Liebe Grüße aus dem schönen Oberharz

    Tom vom Berg

    --
     ☻_
    /▌
    / \ Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. Hallo Tom,

      Dann solltest Du als erstes mal die Datei in einer Hex-Darstellung ansehen und feststellen, was wirklich drin ist.

      • Welche Zeilenumbruchzeichen?  "\n" oder "\r\n", ...

      Laut Hexeditor 0D - Dezimalwert 13

      • Welche Codierung?  ISO-8859-1, UTF, ...

      Wo kann ich das sehen? In der Dateininfo steht nichts. Notepad++ zeigt mir bei dem Klick auf Codierung aber UTF8 ohne BOM.

      Dort wird auch von eventuellen Problemem mit Zeilenenden gesprochen.
      http://de2.php.net/manual/en/filesystem.configuration.php#ini.auto-detect-line-endings

      Das verstehe ich nicht. Soll ich den ini_set("auto_detect_line_endings", true); jetzt vor dem Script einfügen? Ich habe zu dem ini_set jetzt keine weiteren Inhalte gefunden.

      1. Hello,

        Dann solltest Du als erstes mal die Datei in einer Hex-Darstellung ansehen und feststellen, was wirklich drin ist.

        • Welche Zeilenumbruchzeichen?  "\n" oder "\r\n", ...
          Laut Hexeditor 0D - Dezimalwert 13

        Also eine alte MAC-Datei. Dedlfix hatte sowas ja schon angedeutet.

        • Welche Codierung?  ISO-8859-1, UTF, ...
          Wo kann ich das sehen? In der Dateininfo steht nichts. Notepad++ zeigt mir bei dem Klick auf Codierung aber UTF8 ohne BOM.

        Schau Dir an, durch welche Bytes das große Ö in "Öffentlicher Identifikationscode" repräsentiert wird. Daraus kannst Du (annähernd) schließen, welche Codierung verwendung findet. Dein Notepad++ macht das ja so ähnlich. Es sucht nach typischen Bytefolgen.

        Dort wird auch von eventuellen Problemem mit Zeilenenden gesprochen.
        http://de2.php.net/manual/en/filesystem.configuration.php#ini.auto-detect-line-endings

        Das verstehe ich nicht. Soll ich den ini_set("auto_detect_line_endings", true); jetzt vor dem Script einfügen? Ich habe zu dem ini_set jetzt keine weiteren Inhalte gefunden.

        Versuch es mal. Hinterher wieder zurückstellen.

        Alternativ kannst Du die Datei auch konvertieren, indem Du alle "\r" zu "\r\n" ergänzt.

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
         ☻_
        /▌
        / \ Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
        1. Vielen Vielen Dank. Auf die Idee mit dem Mac wäre ich sicher nicht gekommen. 100 Küsse an dich TOM und den Dedlfix.

          eure Sonja

          1. Hello,

            Vielen Vielen Dank. Auf die Idee mit dem Mac wäre ich sicher nicht gekommen. 100 Küsse an dich TOM und den Dedlfix.

            *rotwerd*
            Bitte, gerne geschehen.

            Aber erzähl der Nachwelt ruhig noch, ob und wie Du das Problem jetzt gelöst hast.

            Liebe Grüße aus dem schönen Oberharz

            Tom vom Berg

            --
             ☻_
            /▌
            / \ Nur selber lernen macht schlau
            http://bergpost.annerschbarrich.de
            1. ah, sehr gut, hier kennt man sich mit Kodierungen aus. Vielleicht könnte man mir auch eine kleine Frage beantwortet, bzw. mir auf die Sprünge helfen?

              auf einer WebSeite, die als utf-8 kodiert ist, möchte ich per formular daten an eine PHP-Datei schicken, damit diese diese Daten weiterverarbeit. Das übliche also.

              Zwei Fragen dazu: die Daten werden ja beim Versand urlkodiert. Das macht der Browser vermutlich automatisch? Und die Kodierung der Daten bleibt erhalten?

              Nun möchte ich aber, dass der User auf meiner UTF-8 kodierten Homepage beim Formular auswählen kann, ob die Daten ASCII oder UTF-8 sind, ob es also ein-Byte oder Mehrbyte-Daten sind... geht das denn? Er kopiert also Ascii-Daten (aus einer Ascii-kodierten Datei) in das Formular meiner UTF-8 kodierten Webseite und schickt diese Daten ab. Zusätzlich die Information, dass es nicht UTF-8 Daten sind, die da kommen, sondern (ein-Byte) ASCII.

              Wie macht man das?

              1. Nun möchte ich aber, dass der User auf meiner UTF-8 kodierten Homepage beim Formular auswählen kann, ob die Daten ASCII oder UTF-8 sind, ob es also ein-Byte oder Mehrbyte-Daten sind... geht das denn? Er kopiert also Ascii-Daten (aus einer Ascii-kodierten Datei) in das Formular meiner UTF-8 kodierten Webseite und schickt diese Daten ab. Zusätzlich die Information, dass es nicht UTF-8 Daten sind, die da kommen, sondern (ein-Byte) ASCII.

                Wie macht man das?

                oder muss man einfach wie folgt agieren:

                wenn sollAsciisein, dann UTF-DECODE (ascii nach utf-8), ansonsten so lassen, wie es ist?

              2. Tach!

                die Daten werden ja beim Versand urlkodiert. Das macht der Browser vermutlich automatisch? Und die Kodierung der Daten bleibt erhalten?

                Die URL-Kodierung muss dich nicht weiter interessieren, wenn du die Daten in $_GET/$_POST bereitgestellt bekommst, ist die Transportsicherung bereits wieder entfernt worden. Der Browser schickt üblicherweise die Daten in der Zeichenkodierung, die für die Webseite angegeben ist, in der das Formular liegt. Man kann dem Formular jedoch auch über accept-charset was anderes angeben (was nicht in allen Fällen funktionieren muss).

                Nun möchte ich aber, dass der User auf meiner UTF-8 kodierten Homepage beim Formular auswählen kann, ob die Daten ASCII oder UTF-8 sind, ob es also ein-Byte oder Mehrbyte-Daten sind... geht das denn?

                Nein.

                Er kopiert also Ascii-Daten (aus einer Ascii-kodierten Datei) in das Formular meiner UTF-8 kodierten Webseite und schickt diese Daten ab. Zusätzlich die Information, dass es nicht UTF-8 Daten sind, die da kommen, sondern (ein-Byte) ASCII.

                Er kopiert mit Betriebssystem-Mitteln, da hat er (in dem Moment) sowieso keinen Einfluss auf die dabei verwendete Kodierung. Der Browser bekommt die Daten vom Betriebssystem in einer definierten Form in das Eingabefeld gepastet und verwendet für den Transport die Seiten-/Formular-Kodierung (siehe oben). Wenn der Anwender die Daten aus einer Datei kopiert, muss/sollte er beim Öffnen bereits angeben, wie die Datei zu interpretieren ist.

                dedlfix.

                1. äh, ok, also mir gehts nur darum, auf Empfängerseite festzustellen, welche Bytefolge geschickt wurde. Das wäre aber gewährleistet, oder? die wäre immer die  gleiche, unabhängig, ob meine seite jetzt utf-8 kodiert ist oder was anderes?

                  1. Tach!

                    äh, ok, also mir gehts nur darum, auf Empfängerseite festzustellen, welche Bytefolge geschickt wurde.

                    Das geht vom Prinzip her nicht. Du kannst kodierte Daten nur dann lesen, wenn du den Schlüssel kennst. Im Falle von Zeichenkodierungen kann man (anhand von Indizien) raten, welche Kodierung es sein könnte. Das ist nicht in allen Varianten zuverlässig möglich.

                    Das wäre aber gewährleistet, oder? die wäre immer die  gleiche, unabhängig, ob meine seite jetzt utf-8 kodiert ist oder was anderes?

                    Du solltest dich darauf verlassen können, dass der Anwender dir keinen Zeichenmüll schickt. Wenn er die Zeichen richtig sieht, wird alles gut. Wenn er die Datei falsch öffnet, Müll sieht und dir den schickt, dann kommt Müll bei dir an. Wenn er das auf immer gleicher Weise tut, kannst du versuchen die Daten zu entmüllen, sprich durch Umkodierungsversuche die originalen Daten zu erhalten. Kann funktionieren, muss aber nicht.

                    dedlfix.

      2. Tach!

        Dann solltest Du als erstes mal die Datei in einer Hex-Darstellung ansehen und feststellen, was wirklich drin ist.

        • Welche Zeilenumbruchzeichen?  "\n" oder "\r\n", ...
          Laut Hexeditor 0D - Dezimalwert 13

        Nur 0D/13 wäre alte Mac-Kodierung.

        • Welche Codierung?  ISO-8859-1, UTF, ...
          Wo kann ich das sehen? In der Dateininfo steht nichts. Notepad++ zeigt mir bei dem Klick auf Codierung aber UTF8 ohne BOM.

        Wenn du nicht weißt, welche Kodierung beim Erzeugen der Daten verwendet wurde, kannst du nur anhand von Indizien raten. Die Kodierung ist aber für das Zeilenendeproblem nicht relevant, solange du nichts anderes als ASCII-basierendes verwendest.

        Dort wird auch von eventuellen Problemem mit Zeilenenden gesprochen.
        http://de2.php.net/manual/en/filesystem.configuration.php#ini.auto-detect-line-endings
        Das verstehe ich nicht. Soll ich den ini_set("auto_detect_line_endings", true); jetzt vor dem Script einfügen? Ich habe zu dem ini_set jetzt keine weiteren Inhalte gefunden.

        Wenn du keinen Einfluss auf die Datenerzeugung hast, dann kannst du bei Problemen mit fgetcsv() diese Option probieren. Am Windows-Editor ändert das aber nichts. Besser ist, die Zeilenenden für DOS zu erzeugen, oder \n zu verwenden und Notepad zu ignorieren.

        dedlfix.

  3. Der Zeilenumbruch ist schon da - er ist nur je nach Format entweder CR, CRLF oder LF. In Notepad++ kannst du unter "Ansicht" die "Nicht druckbare[n] Zeichen" anzeigen - in deinem Fall solltest du "Zeilenende".

    Wie kann ich nun den fgetcsv dazu bringen den Inhalt wie in Notepad sichbar korrekt zu trennen. Das ";" taugt ja nur als Feld-Trennzeichen, oder?

    ; ist in deinem Fall der Feld-Trenner, der Zeilentrenner ist vermutlich ein LF-Zeichen, welches von Windows Notepad nicht interpretiert wird, darum klebt es dort zusammen und du hast nur eine ewig lange Zeile.

    Mein Versuch mit:

    $row = 0;

    $handle = fopen ("import-test.csv","r");
    while ($data = fgetcsv ($handle, 99999, ';')) {
    var_dump($data);
    }
    fclose ($handle);

    
    >   
    > Brachte leider nichts gutes dabei heraus:  
      
    Mal kurz überarbeitet:  
    das 2. Argument mit einer unsinnig Großen Zahl zu füllen kannst du dir sparen, 0 bedeutet ab PHP 5.0.4 "unbegrenzt"  
      
    In der Schleife schreibst du dann jede Zeile (fopen R geht Zeilenweise durch) in ein Array welches du am Schluss ausgibst. Damit solltest du das Komplette CSV-File als mehrdimensionales Array vorliegen haben.  
      
    ~~~php
    if (($handle = fopen('import-test.csv', 'r')) !== false) {  
    	while (($row = fgetcsv($handle, 0, ';') !== false) {  
    		$data[] = $row;  
    	}  
    	fclose($handle);  
    	print_r($data);  
    }
    
    1. Tach!

      (fopen R geht Zeilenweise durch)

      Nein, es gibt nur den Modus t, der beim Schreiben etwas an den Zeilenenden bewirkt. Das Lesen und Interpretieren ist nicht fopens Aufgabe, das machen die anderen Lese-Funktionen, die dann auch Zeilenumbrüche als solche erkennen oder nicht, je nach ihrer Arbeitsweise.

      dedlfix.

    2. Der Zeilenumbruch ist schon da - er ist nur je nach Format entweder CR, CRLF oder LF. In Notepad++ kannst du unter "Ansicht" die "Nicht druckbare[n] Zeichen" anzeigen - in deinem Fall solltest du "Zeilenende".

      Danke, jetzt ist zu sehen es ist ein CR am Ende..

      In der Schleife schreibst du dann jede Zeile (fopen R geht Zeilenweise durch) in ein Array welches du am Schluss ausgibst. Damit solltest du das Komplette CSV-File als mehrdimensionales Array vorliegen haben.

      if (($handle = fopen('import-test.csv', 'r')) !== false) {

      while (($row = fgetcsv($handle, 0, ';') !== false) {
      $data[] = $row;
      }
      fclose($handle);
      print_r($data);
      }

        
      Es fehlte eine Klammer, korrigiert:  
      ~~~php
      if (($handle = fopen('import-test.csv', 'r')) !== false) {  
              while (($row = fgetcsv($handle, 0, ';') !== false)) {  
                      $data[] = $row;  
              }  
              fclose($handle);  
              print_r($data);  
      }
      

      Die Ausgabe gibt allerdings nur
      "Array ( [0] => 1 )" aus.