Martin Franz: (CSV) Zeilenumbruch in letzter Zelle

Hallo,

ich habe ein Problem, bei ihr mir bestimmt helfen könnt.

Ich lese mit $zeile = fgets($handle, 4096); eine Datei zeilenweise ein und speichere die Datensätze in einer Datenbank.

Ich bekomme nun CSV-Dateien zum einlesen, die im Format vereinzelt einen Fehler enthalten können:

Es kann vorkommen, dass das letzte Datenfeld einen Zeilenumbruch enthält. Dadurch wird der umgebrochene Teil des Datenfelds natürlich als neuer Datensatz interpretiert. In etwa so:

Daten1 sind gut;Daten2 sind gut;Daten3
sind doof
Daten1 sind gut;Daten2 sind gut;Daten3 sind gut
Daten1 sind gut;Daten2 sind gut;Daten3 sind gut

Kann ich irgendwie überprüfen, ob die nächste einzulesende Zeile keinen kompletten Datensatz enthält und diese Daten an den aktuellen Datensatz anhängen?

  1. Hab noch was vergessen:

    Wenn ich die Datei im Editor öffne, wird an der Stelle wo sich der Zeilenumbruch befindet, kein Zeilenumbruch gemacht.

    1. Hi,

      Wenn ich die Datei im Editor öffne, wird an der Stelle wo sich der Zeilenumbruch befindet, kein Zeilenumbruch gemacht.

      dann ist der vermutlich anders kodiert, als Dein Editor (und Dein Script) es erwartet.
      Warum liest Du die Zeilen nicht einfach zeilenweise z.B. über file() ein?

      freundliche Grüße
      Ingo

      1. Ah stimmt, da ist was dran.
        Wenn ich file() verwende, müsste doch auch hier das gleiche Ergebnis bei rauskommen, oder? Also das der ungewollte Zeilenumbruch einen neuen Datensatz vorgaukelt. Oder sehe ich das jetzt falsch?

        1. Ach ja natürlich, jetzt kapier ichs. Danke für den Tipp mit file(). Da ist es ja viel einfacher auf den nächsten Datensatz zuzugreifen. Ich werd das auch gleich so umbauen.

          Ich danke dir Ingo!

      2. Hallo Ingo,

        Warum liest Du die Zeilen nicht einfach zeilenweise z.B. über file() ein?

        Das bringt doch auch nichts, weil file() mit dem voreingestellten Zeilenumbruch oder sogar mit autodetect_line_endings arbeitet. Das würde das Problem nur verlagern.

        Die Idee, die Restzeile anzufügen an die vorherige finde ich da schon ausgesprochen gut.

        Das geht doch mit der fgetcsv()-Funktion und einem foreach() fast automatisch, wenn die Datei komplett zweimal in den Speicher passt, also z.B. ca. 3MB nicht übersteigt. Wenn die Zeilen zu kurz werden aber dafür mehr, dann ist diese Grenze eher erreicht, weil PHP sehr viel Overhead für Arrays erzeugt.

        Also Datei mit fgetcsv() einlesen in ein Array. Es entsteht dann ein Array von Arrays (Zeilen und Spalten).

        Das geht man mit foreach() durch und immer dann, wenn die Anzahl Elemente zu einem Zeilenindex nicht passt, muss man seine Fallunterscheidung starten und schauen, ob es nach dem Zusammenklatchen mit dem Vorgänger oder Nachfolger-Zeilenelement dann stimmt.

        Das könnte klappen, wenn nur ein Zeilenumbruch in einer solchen Zelle enthalten ist.
        Wenn es zwei werden, oder aber in mehreren Zellen welche enthalten sind, dürften die Freiheitsgrade für einen reperaturversuch schon zu viele sein...

        LG
        Chris©

        1. Hi,

          Das bringt doch auch nichts, weil file() mit dem voreingestellten Zeilenumbruch oder sogar mit autodetect_line_endings arbeitet. Das würde das Problem nur verlagern.

          doch... in der Grundeinstellung findet file() alle möglichen Umbruchkodierungen, so dass schonmal ein Array mit den entsprechenden Zeilen vorliegt. So wie ich den Grund für das Problem  anhand des zweiten Posts sehe, sollte das alleine schon ausreichen.
          Ansonsten reicht eine simple Abfrage, ob ein Feld leer ist.

          Das geht doch mit der fgetcsv()-Funktion und einem foreach() fast automatisch, wenn die Datei komplett zweimal in den Speicher passt

          stimmt. Mit eben dieser Einschränkung...

          freundliche Grüße
          Ingo

          1. ReHi Ingo,

            Das bringt doch auch nichts, weil file() mit dem voreingestellten Zeilenumbruch oder sogar mit autodetect_line_endings arbeitet. Das würde das Problem nur verlagern.
            doch... in der Grundeinstellung findet file() alle möglichen Umbruchkodierungen, so dass schonmal ein Array mit den entsprechenden Zeilen vorliegt. So wie ich den Grund für das Problem  anhand des zweiten Posts sehe, sollte das alleine schon ausreichen.

            Es werden so aber aus einem Datensatz mehrere entstehen, und man kann nicht unbedingt erkennen, wieviele von den Datensätzen eigentlich noch zum letzten Feld des vorangegengenen DS gehören.

            Ansonsten reicht eine simple Abfrage, ob ein Feld leer ist.

            Leere felder sind in CSV-Dateien aber erlaubt. Da folgen dann einfach zwei Feldtrenner aufeinander. Oder wie meinst Du das jetzt?

            Schwieriger wird es dann noch, wenn plötzlich auch noch Feldtrennzeichen als Datenwerte im Datenstrom der Felder auftauchen. dann ist das Chaos perfekt.

            LG
            Chris©

            1. Hi,

              Es werden so aber aus einem Datensatz mehrere entstehen, und man kann nicht unbedingt erkennen, wieviele von den Datensätzen eigentlich noch zum letzten Feld des vorangegengenen DS gehören.

              wie sieht denn eine solche Zeile aus?

              Ansonsten reicht eine simple Abfrage, ob ein Feld leer ist.

              Leere felder sind in CSV-Dateien aber erlaubt. Da folgen dann einfach zwei Feldtrenner aufeinander. Oder wie meinst Du das jetzt?

              sorry, ich meinte Zeile, nicht Feld.

              freundliche Grüße
              Ingo

              1. Hi,

                Es werden so aber aus einem Datensatz mehrere entstehen, und man kann nicht unbedingt erkennen, wieviele von den Datensätzen eigentlich noch zum letzten Feld des vorangegengenen DS gehören.
                wie sieht denn eine solche Zeile aus?

                Da gibt es Alternativen.
                Wenn man Glück hat, enthält sie nur erlaubte Zeichen bis zum nächsten Return eben.
                Das entspricht dann einem Feld(-Fragmen) und bedeutet dann, dass die darauffolgende Zeile auch noch zum letzten Feld der vorletzten Zeile gehören kann, usw, usw.

                Wenn die Textzelle nun aber auch noch das Separator-Zeichen (also Komma, Tab, Semikolon, o. ä.) enthält, dann hat man meistens verloren.

                Ansonsten reicht eine simple Abfrage, ob ein Feld leer ist.

                Leere Felder sind in CSV-Dateien aber erlaubt. Da folgen dann einfach zwei Feldtrenner aufeinander. Oder wie meinst Du das jetzt?
                sorry, ich meinte Zeile, nicht Feld.

                Ok, dann habe ich den Teil jetzt verstanden.
                Leerzeilen sind erlaubt.
                Ob die dann laut erwähnter RFC gar keine Felder enthalten müssen, habe ich auch noch nicht nachgelsen.

                Nächste beliebte Fehlerquelle ist, dass die unterschiedlichen Zeilen unterschiedlich viele Spalten enthalten, also die "hinteren" einfach weggelassen werden, wenn sie leer sind. Dann kann man sie mit obiger Reparaturanleitung auch nur selten wiederherstellen.

                LG
                Chris©

                1. Hi,

                  Wenn die Textzelle nun aber auch noch das Separator-Zeichen (also Komma, Tab, Semikolon, o. ä.) enthält, dann hat man meistens verloren.

                  nicht unbedingt - dann müsste, wie schon gesagt wurde, eigentlich das ganze Feld in "" gesetzt worden sein. Ich habe mal so eine ähnliche Datei ausgewertet. Vielleicht etwas umständlich, aber es hatte geklappt:

                    
                    if(file_exists($Losungsdatei)) $Losungen = file($Losungsdatei);  
                    if(isset($Losungen) && count($Losungen)>$Losungstag) {  
                      $Losungen[$Losungstag] = str_replace(';','|',$Losungen[$Losungstag]);  
                      $Losungen[$Losungstag] = str_replace('| ','; ',$Losungen[$Losungstag]);  
                  # der Trenner war ein Semikolon, was ich geändert habe und danach evtl. fehlerhafte Änderungen wieder zurückgeommen habe.  
                    
                  # weiterer Code...  
                    
                      $Losung = explode("|", $Losungen[$Losungstag]);  
                      $Losung[6] = trim($Losung[6]);  
                  # den letzten Zeilenumbruch löschen.  
                    
                  # weiterer Code...  
                    
                      for($i=4; $i<=6; $i+=2) {  
                        if(strpos($Losung[$i],'"')===0) {  
                          $Losung[$i] = substr($Losung[$i],1,strlen($Losung[$i])-2);  
                          $Losung[$i] = str_replace('""','"',$Losung[$i]);  
                        }  
                  # Hier werden evtl. vorhandene doppelte Quotes eliminiert.  
                  
                  

                  Der Aufwand wäre aber mit den CSV-Funktionen vermutlich nicht nötig.

                  freundliche Grüße
                  Ingo

                  1. Lieber Ingo,

                    Wenn die Textzelle nun aber auch noch das Separator-Zeichen (also Komma, Tab, Semikolon, o. ä.) enthält, dann hat man meistens verloren.
                    nicht unbedingt - dann müsste, wie schon gesagt wurde, eigentlich das ganze Feld in "" gesetzt worden sein.

                    Darum ging es doch aber gerade, dass das Feld NICHT in Begrenzungszeichen verpackt ist. Wenn es das wäre, hätte man doch auch keine Probleme mit dem Zeilenumbruch, da der im "escapten", also quoteten Teil steht dann.

                    LG
                    Chris©

  2. echo $begrüßung;

    Ich bekomme nun CSV-Dateien zum einlesen, die im Format vereinzelt einen Fehler enthalten können:
    Es kann vorkommen, dass das letzte Datenfeld einen Zeilenumbruch enthält. Dadurch wird der umgebrochene Teil des Datenfelds natürlich als neuer Datensatz interpretiert.

    Ja, und wenn in dieser zweiten Zeile oder auch anderswo noch ; als Datenbestandteil drin sind, hast du ungewollt neue Felder. Lass einfach die CSV-Datei reparieren.

    echo "$verabschiedung $name";

    1. Hallo Dedlfix,

      das hatte ich neulich auch auf dem Tisch.
      Mein Chefchen kam rein und meinte, ich solle "mal eben schnell" die Kontakte aus dem OE-Adressbuch exportieren.

      Leider produziert das eine vollkommen kaputte CSV-Datei, die eben auch Zeilenumbrüche und Feldtrennzeichen im Datenfeld enthält. Da suche ich immer noch nach einer Möglichkeit.

      Ich kann nicht immer im kurzen Rock zur Arbeit gehen, nur damit der Chef abgelenkt ist und nicht nochmal nachfragt...

      LG
      Chris©

      1. Hi!

        Ich kann nicht immer im kurzen Rock zur Arbeit gehen, nur damit der Chef abgelenkt ist und nicht nochmal nachfragt...

        Oehm. Also das kommt drauf an, was in dem Rock steckt. Mir wuerde ich empfehlen grundsaetzlich nicht im kurzen Rock zur Arbeit zu gehen. Die Kollegen waeren sicher ZIEMLICH abgelenkt. (von spontaner Blindheit mal ab)

        Aber der Spruch war trotzdem gut! :D

      2. Hellihello

        Leider produziert das eine vollkommen kaputte CSV-Datei, die eben auch Zeilenumbrüche und Feldtrennzeichen im Datenfeld enthält. Da suche ich immer noch nach einer Möglichkeit.

        Aber csv gestattet das doch. Nur dass die Zelle dann in das Umgebungszeichen gemantelt werden muss und das Umgebungszeichen in der Zelle verdoppelt werden muss.

        Weiß jetzt aber nicht, was OE da ausspuckt.

        Dank und Gruß,

        frankx

        --
        tryin to multitain  - Globus = Planet != Welt
        1. Hallo Frankx

          Leider produziert das eine vollkommen kaputte CSV-Datei, die eben auch Zeilenumbrüche und Feldtrennzeichen im Datenfeld enthält. Da suche ich immer noch nach einer Möglichkeit.

          Aber csv gestattet das doch. Nur dass die Zelle dann in das Umgebungszeichen gemantelt werden muss und das Umgebungszeichen in der Zelle verdoppelt werden muss.

          Weiß jetzt aber nicht, was OE da ausspuckt.

          Leider eine Datei, deren Felder ausschließlich mit Trennzeichen getrennt sind.
          Trennzeichen ist ausschließlich das Komma (hard coded). Man kann nur noch einstellen, welche Felder man haben will, das ist dann alles an Komfort. Eben ein typisches Programm "geklaut by Microsoft" und nicht fertig programmiert.

          LG
          Chris©