Jörg: Import von fehlerhafter csv-Datei

Hallo zusammen,

ich versuche gerade, eine fehlerhafte csv-Datei einzulesen. Diese datei hat anscheinend innerhalb ihrer per Pipe (|) getrennten Felder unsichtbare Zeilentrenner drin.

So erhalte ich dann mit

$myCsvArray = explode("\n",file_get_contents($csv_datei));

anstelle von 25.000 Datensätzen nur noch 15.000, von den anderen problemchen, die das mit sich bringt, mal ganz abgesehen.

Wie gehe ich dieses Problem an?

Wie bringe ich php bei, alle zeilentrenner zu ersetzen, die nicht am (gewünschten) Zeilenende stehen? Die Wahrscheinlichkeit, dass die ungewünschten Zeilenenden direkt vor einer Pipe stehen, ist hoch. Aber ob sie 100%ig ist, weiß ich nicht.

Was tun?

Jörg

  1. Hallo

    ich versuche gerade, eine fehlerhafte csv-Datei einzulesen. Diese datei hat anscheinend innerhalb ihrer per Pipe (|) getrennten Felder unsichtbare Zeilentrenner drin.

    So erhalte ich dann mit

    $myCsvArray = explode("\n",file_get_contents($csv_datei));
    

    anstelle von 25.000 Datensätzen nur noch 15.000, von den anderen problemchen, die das mit sich bringt, mal ganz abgesehen.

    Wie gehe ich dieses Problem an?

    Benutze für das einlesen von CSV die dafür vorgesehene Funktion fgetcsv.

    Tschö, Auge

    --
    200 ist das neue 35.
    1. Hi Auge,

      Benutze für das einlesen von CSV die dafür vorgesehene Funktion fgetcsv.

      Habe jetzt nur mal auf die Schnelle dieselbe datei mit fgetcsv eingelesen und komme tatsächlich auf meine 25.000 Zeilen. Wie macht fgetcsv das?

      if (($handle = fopen($csv_datei, "r")) !== FALSE) {
          while (($data = fgetcsv($handle, 1000, "|")) !== FALSE) {
              $num = count($data);
              echo "<p> $num Felder in Zeile $row: <br /></p>\n";
              $row++;
              for ($c=0; $c < $num; $c++) {
                  echo $data[$c] . "<br />\n";
              }
          }
          fclose($handle);
      }
      

      Jörg

      1. Hallo

        Benutze für das einlesen von CSV die dafür vorgesehene Funktion fgetcsv.

        Habe jetzt nur mal auf die Schnelle dieselbe datei mit fgetcsv eingelesen und komme tatsächlich auf meine 25.000 Zeilen. Wie macht fgetcsv das?

        Ohne in den Quellcode von PHP zu schauen, kann ich dir das en detail auch nicht sagen. Da das aber die für den CSV-Import aus Dateien vorgesehene Funktion ist, sollte man – abgesehen von Bugs und prinzipiellen Unzulänglichkeiten – grundsätzlich davon ausgehen können, dass die so viele Möglichkeiten, wie geht, berücksichtigen. Die Floskel „so viele Möglichkeiten, wie geht“ deswegen, weil CSV alles andere als strikt spezifiziert ist und viele Anwender (as in „für ihre Programme CSV verwendetende Programmierer“) ihre eigenen, unterschiedlich gewürzten Süppchen kochen.

        Tschö, Auge

        --
        200 ist das neue 35.
  2. Hi,

    $myCsvArray = explode("\n",file_get_contents($csv_datei));
    

    anstelle von 25.000 Datensätzen nur noch 15.000,

    also Du hast 25.000 Zeilen, wenn Du mit \n die Datei zersplitterst, sind's nur 15.000

    Das hieße ja, daß da auch noch andere Zeilentrenner vorhanden sind.

    Heißester Kandidat wäre \r - wieviele Zeilen kommen raus, wenn Du damit explodierst?

    cu,
    Andreas a/k/a MudGuard

  3. @@Jörg

    Diese datei hat anscheinend innerhalb ihrer per Pipe (|) getrennten Felder unsichtbare Zeilentrenner drin.

    Was heißt „anscheinend“?

    Wie gehe ich dieses Problem an?

    Die unsichtbaren Zeichen sichtbar machen, z.B. mit diesem Unicode code converter.

    😷 LLAP

    --
    „Dann ist ja auch schrecklich, dass wir in einem Land leben, in dem nicht nur Bildungswillige leben, sondern auch hinreichende Zahlen von Bekloppten. Das darf ich so locker formulieren, ich bin ja jetzt Rentner und muss nicht mehr auf jedes Wort achten.“
    — Joachim Gauck über Impfgegner
  4. Hallo Jörg,

    CSV ist ein Format, das einem gerne mal um die Ohren fliegt, wenn man mit explode herangeht.

    Versuche, die Datei zeilenweise mit fgetcsv einzulesen. Das sollte für die meisten Zwecke die beste Lösung sein. Wie fgetcsv mit Zeilenumbrüchen innerhalb von Anführungszeichen umgeht,

    Wenn Du gezwungen bist, die Datei zunächst komplett in einen String zu saugen, kannst Du angeblich einen Trick verwenden, der im PHP Handbuch unter str_getcsv steht:

    $rows = str_getcsv($file, "\n", '"');

    Das soll den String mit der CSV Datei in Zeilen zerlegen und Zeilenumbrüche innerhalb von Feldbegrenzern korrekt verarbeiten. Meine eigenen Versuche zeigen aber, dass das nicht funktioniert.

    Es gibt diverse Alternativansätze, um damit umzugehen, aber ich habe noch keinen selbst verwendet. Die Kommentare der str_getcsv Seite im PHP Handbuch diskutieren einiges und verweisen auf weitere Ressourcen, z.B. StackOverflow. Man kann zum Beispiel auch einen eigenen Stream-Wrapper schreiben, um einen String mit fopen als Stream zu öffnen, aber das klingt schon recht abgehoben.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf,

      Meine eigenen Versuche zeigen aber, dass das nicht funktioniert.

      Habe mal Auges Tip mit fgetcsv ausprobiert, das scheint nicht schlecht zu funktionieren.

      Seltsamerweise habe ich das Problem überhaupt erst,, seitdem ich ein paar dateien dabei hatte, die Open Office nihct lesen konnte und ich mir dann ein Microsoft Office Paket gekauft habe.

      Dieses liest zwar diese dateien sehr gut, aber mit den exportierten csv-Daten habe ich so meine Sorgen. Ich habe z.b. auch exportierte dateien, in denen teilweise Anführungszeichen als texttrenner verwendet werden und in derselben datei in der gleichen Spalte nicht.

      Einstellen lässt sich das bei microsoft nicht und ich erkenne kein system, wann Texttrenner verwendet werden und wann nicht.

      Jörg

      1. Hallo

        Habe mal Auges Tip mit fgetcsv ausprobiert, das scheint nicht schlecht zu funktionieren.

        Seltsamerweise habe ich das Problem überhaupt erst,, seitdem ich ein paar dateien dabei hatte, die Open Office nihct lesen konnte und ich mir dann ein Microsoft Office Paket gekauft habe.

        Sowas (OO oder LO nicht, Excel schon) gibt's auch? Ich habe schon Fälle mit Texten inklusive Zeilenumbrüchen in einer Spalte gehabt, bei denen ich mit einem Texteditor nachhelfen musste, damit sie von Excel sauber eingelesen werden konnte. Die Datenquelle war in diesen Fällen ein Python-Skript, dass nach Ansicht der Rohdaten im Texteditor eigentlich wohlgeformte Daten lieferte. Aber Excel (allerdings auch Libre Office Calc) sah das halt anders.

        Tschö, Auge

        --
        200 ist das neue 35.
        1. Hi Auge,

          Sowas (OO oder LO nicht, Excel schon) gibt's auch? Ich habe schon Fälle mit Texten inklusive Zeilenumbrüchen in einer Spalte gehabt, bei denen ich mit einem Texteditor nachhelfen musste, damit sie von Excel sauber eingelesen werden konnte. Die Datenquelle war in diesen Fällen ein Python-Skript, dass nach Ansicht der Rohdaten im Texteditor eigentlich wohlgeformte Daten lieferte. Aber Excel (allerdings auch Libre Office Calc) sah das halt anders.

          Ja, ich merke auch, dass so manche csv eine echte Wundertüte ist. Bei mir liegts dann aber tatsächlich schon an Excel, denn meine CSVs bekomme ich von Kunden und diese erhalten die selber in Excel. Woher dann der Ursprung dieser Excellisten kommt, ist für mich nicht mehr nachvollziehbar. Vermutlich aus so allerhand verschiedener Software.

          Jörg

      2. Hallo Jörg,

        CSV von Office ist generell ein Kopfschmerzgenerator.

        Wenn ich eine Excel-Tabelle exportiere, wo Felder Zeilenumbrüche haben, dann sind die CSV Zeilen durch CR-LF getrennt, also \r\n, aber die Zeilenumbrüche innerhalb der Felder nur ein LF (\n). Anführungszeichen setzt es immer dann, wenn es nötig ist (also ein Zeilenumbruch, ein Anführungszeichen oder ein Feldtrenner im Feld). Der Feldtrenner scheint auch nicht wählbar zu sein.

        Du kannst Dich ja mal mit dem DIF Format befassen, vielleicht ist das besser handhabbar…

        Wenn Du richtig mutig bist und dein PHP unter Windows läuft, probier Dein Glück mit COM und verwende Excel direkt zum Lesen der XLS-Datei, statt zu exportieren.

        Rolf

        --
        sumpsi - posui - obstruxi
        1. Hi Rolf,

          CSV von Office ist generell ein Kopfschmerzgenerator.

          Ich dachte, mit MS würde es jetzt besser gehen, aber abgesehen vom Einlesen der datei selber ist der "Export" viel schlechter.

          Anführungszeichen setzt es immer dann, wenn es nötig ist (also ein Zeilenumbruch, ein Anführungszeichen oder ein Feldtrenner im Feld). Der Feldtrenner scheint auch nicht wählbar zu sein.

          Nee, isser nicht. Wie gehe ich denn damit beim Import um? Bisher habe ich immer radikal alle Anführungszeichen aus csv-Dateien heraus gelassen. Wenn nun aber teilweise welche drinne sind, muss ich das dann berücksichtigen oder macht mysqli_real_escape_string diesen Job mit?

          Du kannst Dich ja mal mit dem DIF Format befassen, vielleicht ist das besser handhabbar…

          Wenn Du richtig mutig bist und dein PHP unter Windows läuft, probier Dein Glück mit COM und verwende Excel direkt zum Lesen der XLS-Datei, statt zu exportieren.

          Lieber Beides nicht 😉

          Jörg

          1. Hallo

            Wie gehe ich denn damit beim Import um?
            Bisher habe ich immer radikal alle Anführungszeichen aus csv-Dateien heraus gelassen. Wenn nun aber teilweise welche drinne sind, muss ich das dann berücksichtigen oder macht mysqli_real_escape_string diesen Job mit?

            Wenn der Import der Daten in das PHP-Skript ordentlich eingelesen wurde und dabei (ebenfalls) ordentlich Anführungszeichen als Feldbegrenzer von Anführungszeichen als Datenbestandteil unterschieden wurde, gibt es jene als Feldbegrenzer nicht mehr. Dann hast du nur noch einen String mit seinem Inhalt, der von mysqli_real_escape_string für MySQL passend behandelt wird.

            Dazu ist es natürlich notwendig, dass beim erstellen der Datendatei beziehungsweise des CSV-Blobs die Anführungszeichen als Datenbestandteil maskiert wurden (zum Beispiel maskiert mit vorangestelltem weiteren Anführungszeichen ("") oder mit vorangestelltem Backslash (\")). Zumindest das sollte man den Tabellenkalkulationsprogrammen bei Strings zutrauen dürfen, auch wenn sie bei ihren automatischen Datenformaterkennungen allzu gern auch mal daneben liegen.

            Tschö, Auge

            --
            200 ist das neue 35.
          2. Hallo Jörg,

            macht mysqli_real_escape_string diesen Job mit?

            Der bestimmt nicht. Aber fgetcsv achtet drauf, wenn Du die Parameter richtig setzt.

            Rolf

            --
            sumpsi - posui - obstruxi
            1. Es liegt irgendwie definitiv an den zeilenumbrüchen in den Textfeldern der csv-Datei (analog in den Spalten der Exceldatei).

              Bei diesen 3 Zeilen:

              12345|zzzzz oooo ppppp||111
              
              22633|yyyyy 10 Liter 
              Ersetzt 198323||189,00
              
              22634|"yyyyy 20 Liter 
              Ersetzt 19abc4"||361,00
              

              die in meiner CSV so aussehen:

              12345|zzzzz oooo ppppp||111
              22633|yyyyy 10 Liter Ersetzt 198323||189,00
              22634|"yyyyy 20 Liter Ersetzt 19abc4"||361,00
              

              habe ich bei Zeile 2 und Zeile 3 Importprobleme. Ganz unabhängig davon, dass ja Zeile 2 durch die Feldtrenner den Zeilenumbruch verkraften sollte.

              Jörg

              1. Bei diesen 3 Zeilen:

                12345|zzzzz oooo ppppp||111
                
                22633|yyyyy 10 Liter 
                Ersetzt 198323||189,00
                
                22634|"yyyyy 20 Liter 
                Ersetzt 19abc4"||361,00
                

                Wo sehen diese Zeilen so aus?

                die in meiner CSV so aussehen:

                12345|zzzzz oooo ppppp||111
                22633|yyyyy 10 Liter Ersetzt 198323||189,00
                22634|"yyyyy 20 Liter Ersetzt 19abc4"||361,00
                

                Wo sehen diese Zeilen so aus?

                Wenn, wie oben bei Ziffer 22633, ein Zeilenumbruch innerhalb eines eigentlichen Strings ist, welcher (der Zeilenumbruch) auch laut der Definition Deines CSV eigentlich einen neuen Datensatz einleiten soll, dann hast Du kein definiertes ergo kein maschinell auswertbares CSV, ergo genau drei Möglichkeiten:

                • Bester und eigentlich einziger Weg: Erneuter, sauberer Datenexport. (Du hast keine „Daten“ sondern Datenmüll!)

                oder

                • Du gehst im Editor mit Suchen und Ersetzen

                oder

                • mit Programmen wie sed oder AWK „über die Datei“

                (und überprüfst die Ergebnisse) oder

                Du lässt Dir einen Parser einfallen, mit dem Du aus

                22633|yyyyy 10 Liter 
                Ersetzt 198323||189,00
                

                eben

                22633|"yyyyy 10 Liter 
                Ersetzt 198323"||189,00
                

                machst. Dabei viel Glück! Das braucht man nämlich sehr dringend, wenn man solchen Datenmüll hat.

                Weiter: Das Komma in „189,00“ muss ein Punkt sein.

                Hint: Der erneute und dieses Mal saubere Datenexport ist der billigste Weg. Auch für Deinen Kunde. Ohne saubere Daten, die in einem vereinbarten Verfahren mit formal stabilen Ergebnissen entstehen, kannst Du NICHTS garantieren. Selbst wenn Du es mit aktuellen Fehlern in den Dateien mit viel Mühe hinbekommst wird Dein Programm beim nächsten Mal wahrscheinlich crashen.

                Kommuniziere beim Kunde ganz klar, dass Du mit diesem Datenmüll weder arbeiten kannst noch wirst - sonst darfst Du bei jedem seiner Datenlieferungen auf dem Teppich stehen und seinen Müll „für lau“ reparieren, was bei 15.000 Datensätzen Tage dauert und fehlerträchtig ist (Und wer haftet dafür?)

                Wenn Deine Kunde einen neuen, sauberen Datenexport nicht mitmacht, dann ist der ein Spinner und Du solltest froh sein, ihn loszuwerden - denn der Kunde hat die Erfüllung unmöglich gemacht!

                Hint: Exportiere Beträge im Cent-Format. In diesem gehören die auch in die Datenbank...

                1. Hi Raketenwilli,

                  Wenn, wie oben bei Ziffer 22633, ein Zeilenumbruch innerhalb eines eigentlichen Strings ist, welcher (der Zeilenumbruch) auch laut der Definition Deines CSV eigentlich einen neuen Datensatz einleiten soll, dann hast Du kein definiertes ergo kein maschinell auswertbares CSV, ergo genau drei Möglichkeiten:

                  Unterm Strich sieht es genau so aus. Und Du hast viele richtige Dinge angesprochen. Leider ist der Datenimport nur ein sehr kleiner Teil meiner Leistung am Kunden, sodass ich deshalb niemals einen Kunden verprellen wollen würde. Ich mache die Risiken transparent und schließe jede Gewähr aus. Aber deshalb einen Kunden verlieren, wäre die Sache nicht wert. das mal vorab...

                  Zudem habe ich mir über die Jahre hinweg bereits einen recht stabilen Import geschrieben, der sich bisher ganz gut bewährt hat. Meine Importprobleme gestern waren einer Vorabkontrolle aufgefallen, die noch vor dem eigentlichen Import ansetzt und nur schaut, ob Datenmüll vorliegt oder eine in sich stabil zu importierende Datei.

                  • Bester und eigentlich einziger Weg: Erneuter, sauberer Datenexport. (Du hast keine „Daten“ sondern Datenmüll!)

                  oder

                  • Du gehst im Editor mit Suchen und Ersetzen

                  Genau das war übrigens gestern das "Mittel der Wahl". Da für mich Excel neu war, wußte ich nicht genau, was zu tun war. Letztlich kann man aber in Excel die komplette Tabelle markieren und über suchen und ersetzen alle Zeilenumbrüche der Tabelle entfernen, wobei dann eben nur die Zeilenumbrüche in den Spalten entfernt werden. Soll heißen, man hat später beim Export dan nur noch die vom Export erzeugten "neuen Zeilenumbrüche" der Tabellenzeilen übrig und genau das will ich ja.

                  Hierzu muss man in Excel bei "suchen und ersetzen" nach einem Zeilenumbruch (ALT + 0010) suchen und in ein zeichen seiner Wahl ersetzen.

                  Und anschließend erst die tabelle als csv-Datei speichern.

                  In meinem Importprogramm werden dann die Spalten "vorimportiert" und anschließend von mir selber manuell einer "Bedeutung" zugeordnet. Anschließen werden die Spalten gemäß ihrer Bedeutung dann noch eingehend untersucht und "bereinigt". Somit wird natürlich unter anderem auch die Betragsspalte ihrer Bedeutung entsprechend bereinigt.

                  Danke für Deine Codezeilen. leider nützen sie, wie Du selber erkannt hast, auch nur etwas für das Format, das ich hier als beispiel genommen hatte. ich bekomme aber Datenmüll in verschiedenster Form. Und mein Kunde kann nicts dafür, da er sie selber genau so erhält und keinen Einfluss darauf hat.

                  Insofern kann der Import auch nicht voll maschinell erfolgen, sondern nur vor dem Hintergrund manueller Mithilfe und eines sich ständig um Datenmüll rettende Maßnahmen erweiternden Code. Und natürlich auch sukzessive hinzukommende Erfvahrungen im Export selber, wozu meine gesterige Erfahrung mit Excel gehört. Ehrlich gesagt ist OO oder LO da deutlich besser gewesen, aber im Einlesen wurde es im Laufe der Zeit immer schwieriger, weshalb ich mir gestern (leider) ein MS-Office kaufen musste.

                  Btw. Im eigentlichen Datenimport hatte ich im Gegensatz zum "Vorimport" immer schon auf fgetcsv gesetzt. Der Vorimport ist u.a. dafür zuständig, zu prüfen, ob jede zeile dieselbe Anzahl Delimiter enthält. Das habe ich jetzt auch auf fgetcsv umgestellt.

                  Jörg

                  1. Hallo Jörg,

                    Leider ist der Datenimport nur ein sehr kleiner Teil meiner Leistung am Kunden, sodass ich deshalb niemals einen Kunden verprellen wollen würde.

                    das ist verständlich - aber dann sollte es auch deine Aufgabe sein, den Kunden zu beraten und ihm zu erklären, dass CSV kein gutes Austausch- oder Importformat ist - eben weil es so viele Fallstricke bereithält.

                    CSV ist okay, wenn man alle am relevanten Ökosystem beteiligten Programme kennt und sichergestellt hat, dass die alle miteinander können. Aber nur dann.

                    Zudem habe ich mir über die Jahre hinweg bereits einen recht stabilen Import geschrieben, der sich bisher ganz gut bewährt hat.

                    Ja, mag sein. Trotzdem wirst du vermutlich immer wieder auf Beispiele stoßen, in denen dein Import plötzlich wieder auf die Schnauze fällt, weil CSV nur sehr vage spezifiziert ist und viele Programme sich nicht einmal daran halten.

                    Wenn CSV als Austauschformat eine Kunden-Anforderung ist, musst du natürlich damit zurechtkommen. Aber dann solltest du versuchen, auch auf die Export-Seite Einfluss zu nehmen.

                    Möge der Kaffee gut und der Montag kurz sein
                     Martin

                    --
                    The taste of love: The more you get, the more you want
                    (aus The Lightning Seeds: Sense)
                  2. Ehrlich gesagt ist OO oder LO da deutlich besser gewesen, aber im Einlesen wurde es im Laufe der Zeit immer schwieriger, weshalb ich mir gestern (leider) ein MS-Office kaufen musste.

                    Ich hab gestern auf einem Surftörn in einer Bucht mit Piratenflagge eine virtuelle Maschine mit Windows 11 und Office 2021 gefunden :-). In einemExperiment habe ich gesehen, dass man das auch auf deutsch umstellen kann.

                    Aber benutzen würde ich das nicht für diesen Zweck. Im Leben nicht.

                    Übrigens kann PHP auch Excel(-2007) Dateien direkt einlesen.

                    Schreiben auch. Neuere Versionen kannst Du zumindest versuchen. Das sind nämlich nichts anderes als gezippte XML-Dateien und so lange sich nichts an den internen XML-Dateien und deren Namensstruktur verändert hat (Die Zips enthalten XML-Dateien für Daten, Formeln, Formate, Funktionen etc.) sollte das klappen.

                    Protip: Verpasse einer xlsx- oder xlsb-Datei einfach mal die Endung „.zip", packe sie aus und staune.

                    1. Protip: Verpasse einer xlsx- oder xlsb-Datei einfach mal die Endung „.zip", packe sie aus und staune.

                      Interessant 👍

              2. Um mal zu zeigen, was man machen könnte:

                Fehlerhafte „CSV“-Datei im Editor:

                12345|zzzzz oooo ppppp||111
                
                22633|yyyyy 10 Liter 
                Ersetzt 198323||189,00
                
                22634|"yyyyy 20 Liter 
                Ersetzt 19abc4"||361,00
                

                Offenbar sollen das 3 Datentupel („Zeilen“) zu je 4 Items („Spalten“) werden. Das der Zeilentrenner zwei Bedeutungen hat zerlegen wir am "|" und die vierte Spalte am (ersten) Zeilenumbruch, bauen daraus einen eindimensionalen Array mit allen Items, aus diesem dann den zweidimensionalen:

                <?php
                
                define( 'Delemiter', '|' );
                define( 'RowDelemiter', "\n" );
                define( 'Columns',   '4' );
                define( 'FileName', './test.txt');
                
                $text = trim( file_get_contents( FileName ) );
                $data = explode( Delemiter, $text );
                
                $helperArray=[];
                $helperIndex=0;
                
                foreach ( $data as $item ) {
                	if ( 0 == ( $helperIndex +1)  % Columns ) {
                		$a = explode( RowDelemiter, $item, 2 );
                		foreach ( $a as $s ) {
                			$helperArray[] = trim( $s, "\"\r\n");
                			$helperIndex ++;
                		}
                	} else {
                		$helperArray[] = trim( $item, "\"\r\n");
                		$helperIndex ++;
                	}
                }	
                
                $array = [];
                $dataRow = 0;
                $dataColumn = 0;
                
                
                foreach ( $helperArray as $item ) {
                     	
                	if ( $dataColumn == Columns ) {
                		$dataColumn = 0;
                		$dataRow ++;
                	}
                	
                	$array[$dataRow][$dataColumn]=$item;
                    $dataColumn ++;
                }
                
                print_r( $array );
                
                

                Das sieht jetzt nach dem aus, was Du wohl willst. Aber bei weiteren Fehlern in der Quelle versagt das natürlich „glänzend“:

                Array
                (
                    [0] => Array
                        (
                            [0] => 12345
                            [1] => zzzzz oooo ppppp
                            [2] => 
                            [3] => 111
                        )
                
                    [1] => Array
                        (
                            [0] => 22633
                            [1] => yyyyy 10 Liter 
                Ersetzt 198323
                            [2] => 
                            [3] => 189,00
                        )
                
                    [2] => Array
                        (
                            [0] => 22634
                            [1] => yyyyy 20 Liter 
                Ersetzt 19abc4
                            [2] => 
                            [3] => 361,00
                        )
                
                )
                
                

                Weitere Fehler machen die Sache weitaus komplizierter: Es bleibt also bei meiner Aussage, dass ein sauberer Datenexport eine weitaus bessere Lösung ist!

                Moderne Systeme können den übrigens als JSON. Das wieder lässt sich auch leichter einlesen.

                1. Moderne Systeme können den übrigens als JSON. Das wieder lässt sich auch leichter einlesen.

                  Die Daten in einem definierten System zu erhalten, wäre ein Traum 😀

                  Wie schon gesagt, danke für Deinen Code. Leider kan ich überhaupt keinen Code brauchen, der mich nicht manuell zuordnen lässt. Dazu braucht jede Spalte ihre ganz eigene Behandlung, um möglichst viele Fehler durch Datenmüll auszuschließen. Insb. die Betragsspalte ist hier hervorzuheben. Die muss ich manuell zuordnen und anschließend 100% sicherstellen, den Betrag, wie auch immer er formatiert ist, korrekt zu erfassen. Da verlasse ich mich aber ausschließlich auf die eigene Sichtkontrolle und (falls nötig) Korrektur/Abbruch.

                  Jörg

                  1. Moderne Systeme können den übrigens als JSON. Das wieder lässt sich auch leichter einlesen.

                    Die Daten in einem definierten System zu erhalten, wäre ein Traum 😀

                    Nein. Das ist ein MUSS.

                    Leider kan ich überhaupt keinen Code brauchen, der mich nicht manuell zuordnen lässt. Dazu braucht jede Spalte ihre ganz eigene Behandlung, um möglichst viele Fehler durch Datenmüll auszuschließen.

                    Das kann man ab der Zeile machen, die da print_r( $array ) lautet. Ab dort hast Du die Daten ja in PHP importiert.

                    Ansonsten wäre da noch der wichtige, aber ignorierte Hinweis von Rolf

                    Zitat:

                    Wenn ich eine Excel-Tabelle exportiere, wo Felder Zeilenumbrüche haben, dann sind die CSV Zeilen durch CR-LF getrennt, also \r\n, aber die Zeilenumbrüche innerhalb der Felder nur ein LF (\n). Anführungszeichen setzt es immer dann, wenn es nötig ist (also ein Zeilenumbruch, ein Anführungszeichen oder ein Feldtrenner im Feld). Der Feldtrenner scheint auch nicht wählbar zu sein.

                    Das wäre, wenn es sich als richtig erweist, ein sauber definierter Export. Nur können PHP und andere Programme das nicht mit deren CSV-Funktionen importieren, weil diese extrem zeilenweise arbeiten und sowohl "\n" als "\r" als auch "\r\n" als Zeilenumbruch und somit Zeilenende werten. Hintergrund ist die verwendete C/C++-Bibliothek. Und die ist älter als Excel.

                    Auf einem System mit sinnvollen Programmen würde ich wie folgt vorgehen:

                    In der gesamten Datei:

                    • Ersetzen aller "\r\n" durch "\r".
                    • Ersetzen aller "\n" durch "" (Nichts)
                    • Ersetzen aller "\r" durch "\r\n".
                    • Speichern unter neuem Name

                    In PHP gegossen:

                    <?php
                    $fileName= "daten.csv";
                    
                    $input  = file_get_contents( '$fileName' );
                    $output = str_replace( "\r\n", "\r", $input ) ;
                    $output = str_replace( "\n", "", $output ) ;
                    $output = str_replace( "\r", "\r\n", $output ) ;
                    
                    $newFileName = $fileName . date( '_Y-m-d_His' );
                    #Dateinamen mit ":" sind auf manchen Systemen ein Problem.
                    
                    file_put_contents( '$newFileName', $output );
                    
                    

                    Danach hast Du eine hoffentlich importierbare Daten in einer Datei mit einem neuen Dateinamen.

                    1. Hi Raketenwilli,

                      Ansonsten wäre da noch der wichtige, aber ignorierte Hinweis von Rolf

                      In der gesamten Datei:

                      • Ersetzen aller "\r\n" durch "\r".
                      • Ersetzen aller "\n" durch "" (Nichts)
                      • Ersetzen aller "\r" durch "\r\n".
                      • Speichern unter neuem Name

                      In PHP gegossen:

                      <?php
                      $fileName= "daten.csv";
                      
                      $input  = file_get_contents( '$fileName' );
                      $output = str_replace( "\r\n", "\r", $input ) ;
                      $output = str_replace( "\n", "", $output ) ;
                      $output = str_replace( "\r", "\r\n", $output ) ;
                      
                      $newFileName = $fileName . date( '_Y-m-d_His' );
                      #Dateinamen mit ":" sind auf manchen Systemen ein Problem.
                      
                      file_put_contents( '$newFileName', $output );
                      
                      

                      Mache ich an anderer Stelle auch so ähnlich (dann natürlich spaltenbezogen):

                                define (CR, chr(13));
                                define (LF, chr(10));
                      
                                $string = str_replace(CR.LF, LF, $string);
                                $string = str_replace(CR, LF, $string);
                                $string = str_replace(LF, " -->", $string);
                      

                      Noch besser scheint mir aber tatsächlich zu sein, es direkt in Excel zu machen (wie, habe ich ja beschrieben). Excel säubert wirklich recht zuverlässig.

                      Jörg

                      1.           define (CR, chr(13));
                                  define (LF, chr(10));
                        

                        Das geht mit aktuellen PHP-Versionen nicht mehr.

                                  define ('CR', chr(13));
                                  define ('LF', chr(10));
                        
                        1.           define (CR, chr(13));
                                    define (LF, chr(10));
                          

                          Das geht mit aktuellen PHP-Versionen nicht mehr.

                                    define ('CR', chr(13));
                                    define ('LF', chr(10));
                          

                          Oh... ab welcher version nicht und wie löst man diesen knoten?

                          Jörg

                          1.           define (CR, chr(13));
                                      define (LF, chr(10));
                            

                            Das geht mit aktuellen PHP-Versionen nicht mehr.

                                      define ('CR', chr(13));
                                      define ('LF', chr(10));
                            

                            Oh... ab welcher version nicht und wie löst man diesen knoten?

                            Jörg

                            Seit 7.2 wird das als „deprecated“ in Form einer Warnung „angemeckert“, schon in 7.1 gab es eine Notiz - in der nächsten Major-Version war es schon ein echter Syntaxfehler.

                            und wie löst man diesen knoten?

                            Hm. Error-Log durchsehen und, wie gezeigt, Quotas setzen?

  5. Hello,

    ich versuche gerade, eine fehlerhafte csv-Datei einzulesen. Diese datei hat anscheinend innerhalb ihrer per Pipe (|) getrennten Felder unsichtbare Zeilentrenner drin.

    So erhalte ich dann mit

    $myCsvArray = explode("\n",file_get_contents($csv_datei));
    

    anstelle von 25.000 Datensätzen nur noch 15.000, von den anderen problemchen, die das mit sich bringt, mal ganz abgesehen.

    Wie gehe ich dieses Problem an?

    Wie bringe ich php bei, alle zeilentrenner zu ersetzen, die nicht am (gewünschten) Zeilenende stehen? Die Wahrscheinlichkeit, dass die ungewünschten Zeilenenden direkt vor einer Pipe stehen, ist hoch. Aber ob sie 100%ig ist, weiß ich nicht.

    Dass PHP dafür zwei eigene Funktionen anbietet, wurde ja schon erwähnt.

    Vermutlich werden "\r\n" als Satzende ("Zeilentrenner") benutzt und "\n" innerhalb eines Satzes als Zeilenumbruch innehalb eines Feldes.

    Das wäre ein übliches Verhalten z. B. von Excel.

    Also erst die Zeilen anhand von "\r\n" trennen.
    Anschließend Dieses Array dann ggf. mit array_map() durchgehen und mit str_replace() die störenden Zeilenumbrüche "\n" beseitigen/ersetzen.

    Dann die Elemente des Array mittels foreach und explode() in Spalten zerlegen.

    Glück Auf
    Tom vom Berg

    --
    Es gibt nichts Gutes, außer man tut es!
    Das Leben selbst ist der Sinn.