Frage zu fstream ignore()



  • Aloha,

    ich bräuchte nochmal kurz eure Hilfe bei einem Programm von mir. Und zwar folgendes:

    #include <iostream>
    #include <vector>
    #include <fstream>
    #include <string>
    #include <sstream>
    #include <limits>
    
    using namespace std;
    
    int main()
    {
        ifstream csv_file;
        csv_file.open("test.csv", ios::in);
    
        if(csv_file){
            vector<float> all_values(40);
            string s;
            int i=0;
            csv_file.ignore(numeric_limits<streamsize>::max(),'\n');                    
            csv_file.ignore(2);
            while(getline(csv_file, s, ';')){
                istringstream(s)>>all_values[i];
                cout<<all_values[i]<<endl;
                i++;
            }
    
            csv_file.close();
        }
        else{
            cerr<<"Fehler"<<endl;
        }
    
        return 0;
    }
    

    Es geht sich um die ignore() Befehle. Besser gesagt nur um den zweiten.

    Also wie ihr vielleicht sehen könnt, öffne ich eine .csv Datei und lese die Werte ein. Die erste Zeile möchte ich nicht lesen, da dort nichts wichtiges für mich steht. So nun hatte ich spaßeshalber etwas herumprobiert mit dem ignore() Befehl. Als ich da jetzt eine 2 eingetragen habe, liest das Programm alle Werte der ersten Spalte nicht mehr ein. Meine Frage wäre: Warum?
    Ich verstehe das irgendwie gerade nicht und stehe auf dem Schlauch 😕

    Vielen Dank schonmal für eure Hilfe!


  • Mod

    Gib mal Beispieldaten an, welche Ausgabe du erwartest, welche Ausgabe tatsächlich erfolgt.



  • Also die csv Tabelle sieht wie folgt aus:

    1;3.5;4.9
    2;8.2;5.6
    3;10.4;2.3
    4;3.1;2.0

    In der allerersten Zeile steht nur etwas wie "CAD Daten". Diese Zeile brauche ich nicht. Rauskommen tut so etwas:

    3.5
    4.9
    8.2
    5.6
    10.4
    2.3
    3.1
    2.0

    Die Werte stehen halt alle untereinander, weil ich ja jedes mal ein endl; mit einbaue.

    Erwartet hätte ich eigentlich, dass nur die ersten 2 Werte fehlen würden. So hatte ich die Funktion verstanden. Also die 1 und die 3.5
    Stattdessen fehlt die komplette erste Spalte.



  • Gib doch mal s aus. Und schau ob's dann einen Sinn ergibt.

    while(getline(csv_file, s, ';')){
          std::cout << '\"' << s << "\"\n";
          istringstream(s)>>all_values[i];
          cout<<all_values[i]<<endl;
          i++;
        }
    


  • RHCP_Drums schrieb:

    Also die csv Tabelle sieht wie folgt aus:

    1;3.5;4.9
    2;8.2;5.6
    3;10.4;2.3
    4;3.1;2.0

    In der allerersten Zeile steht nur etwas wie "CAD Daten". Diese Zeile brauche ich nicht. Rauskommen tut so etwas:

    3.5
    4.9
    8.2
    5.6
    10.4
    2.3
    3.1
    2.0

    Die Werte stehen halt alle untereinander, weil ich ja jedes mal ein endl; mit einbaue.

    Erwartet hätte ich eigentlich, dass nur die ersten 2 Werte fehlen würden.

    ignore kennt keine csv-Datei oder das csv-Format - woher auch! ignore(2) überliest genau zwei Zeichen. In diesem Fall die '1' und das ';'. Ab der 3.5;4.9;... wird ganz normal weiter gelesen. Und das ist auch das, was Du ausgibst.

    Lesen aus csv-Dateien wird hier öfter nachgefragt - siehe z.B. http://www.c-plusplus.net/forum/p2364466#2364466

    Gruß
    Werner


  • Mod

    ifstream csv_file;
        csv_file.open("test.csv", ios::in);
    

    ➡

    ifstream csv_file("test.csv");
    

    Übrigens: Was du eigentlich willst, ist zuerst die Zahl extrahieren, dann alle Zeichen bis und inklusive dem nächsten Semikolon zu überspringen. Da brauchst du keinen Stringpuffer.



  • Werner Salomon schrieb:

    In diesem Fall die '1' und das ';'. Ab der 3.5;4.9;... wird ganz normal weiter gelesen. Und das ist auch das, was Du ausgibst.

    Nein eben nicht. Wie ich schon sagte: die 2, 3, und 4 fehlen auch. Die ganze erste Spalte. Und ich weiß nicht wieso.


  • Mod

    RHCP_Drums schrieb:

    Nein eben nicht. Wie ich schon sagte: die 2, 3, und 4 fehlen auch. Die ganze erste Spalte. Und ich weiß nicht wieso.

    Ich glaube es zu wissen. Ist der Code im OP mit Copy&Paste in diesem Thread gelandet?



  • RHCP_Drums schrieb:

    Werner Salomon schrieb:

    In diesem Fall die '1' und das ';'. Ab der 3.5;4.9;... wird ganz normal weiter gelesen. Und das ist auch das, was Du ausgibst.

    Nein eben nicht. Wie ich schon sagte: die 2, 3, und 4 fehlen auch. Die ganze erste Spalte. Und ich weiß nicht wieso.

    Wie ich schon schrieb: sieh Dir den Inhalt von s an.
    Da siehst Du schön, dass immer bis zum nächsten Semikolon gelesen wird (und dass das ignore(2) ein falsche Fährte ist).



  • Furble Wurble schrieb:

    Gib doch mal s aus. Und schau ob's dann einen Sinn ergibt.

    while(getline(csv_file, s, ';')){
          std::cout << '\"' << s << "\"\n";
          istringstream(s)>>all_values[i];
          cout<<all_values[i]<<endl;
          i++;
        }
    

    Ich habs jetzt mal so ähnlich wie du geschrieben und da kommt das raus:

    3.5
    4.9
    2
    8.2
    5.6
    3
    10.4
    2.3
    4
    3.1
    2

    Hier ist die erste Zeile, bis auf die 1, vorhanden.

    Arcoth schrieb:

    ifstream csv_file;
        csv_file.open("test.csv", ios::in);
    

    ➡

    ifstream csv_file("test.csv");
    

    Übrigens: Was du eigentlich willst, ist zuerst die Zahl extrahieren, dann alle Zeichen bis und inklusive dem nächsten Semikolon zu überspringen. Da brauchst du keinen Stringpuffer.

    Oh. Danke für den Tipp 😃 Ich habs bis jetzt immer so gemacht, aber das werd ich mir in Zukunft merken, danke!



  • Furble Wurble schrieb:

    RHCP_Drums schrieb:

    Werner Salomon schrieb:

    In diesem Fall die '1' und das ';'. Ab der 3.5;4.9;... wird ganz normal weiter gelesen. Und das ist auch das, was Du ausgibst.

    Nein eben nicht. Wie ich schon sagte: die 2, 3, und 4 fehlen auch. Die ganze erste Spalte. Und ich weiß nicht wieso.

    Wie ich schon schrieb: sieh Dir den Inhalt von s an.
    Da siehst Du schön, dass immer bis zum nächsten Semikolon gelesen wird (und dass das ignore(2) ein falsche Fährte ist).

    Ja, aber wieso macht es das dann bei jeder neuen Zeile?



  • RHCP_Drums schrieb:

    Ich habs jetzt mal so ähnlich wie du geschrieben und da kommt das raus:

    3.5
    4.9
    2
    8.2
    5.6
    3
    10.4
    2.3
    4
    3.1
    2

    Hier ist die erste Zeile, bis auf die 1, vorhanden.

    "So ähnlich" reicht nicht.
    Der Witz ist eigentlich, dass Du weisst wo der String s anfängt und wo er endet - deswegen die doppelten Anführungszeichen.



  • Na gut.
    Jetzt sieht es so aus:

    "3.5"
    3.5
    "4.9
    2"
    4.9
    "8.2"
    8.2
    "5.6
    3"
    5.6
    "10.4"
    10.4
    "2.3
    4"
    2.3
    "3.1"
    3.1
    "2
    "
    2

    und wenn ich die Zeile "cout<<all_values[i]<<endl;" weglasse sondern nur s ausgebe siehts wie folgt aus:

    "3.5"
    "4.9
    2"
    "8.2"
    "5.6
    3"
    "10.4"
    "2.3
    4"
    "3.1"
    "2
    "



  • RHCP_Drums schrieb:

    Na gut.
    Jetzt sieht es so aus:

    Und welche Schlußfolgerung ziehst Du daraus?
    Insebesondere das Verhalten von getline() am Zeilenende betreffend?



  • Ehrlich gesagt stehe ich noch immer irgendwie auf dem Schlauch.

    Also wenn ich mir das anschaue, dann sieht man ja, dass in s die erste Spalte (2,3,4) vorhanden ist. Im Vektor stehen die aber nicht. Und es scheint, als würde z.B. die 2 noch im String von 4.9 stehen. Aber irgendwie...verstehe ich es noch immer nicht.



  • Kenn ich - manchmal sieht man den Wald vor lauter Bäumen nicht.

    Sieh Dir mal nur den Anfang Deiner csv Datei an:

    1;3.5;4.9
    2;8.2;5.6
    

    Und die dazugehörigen strings:

    "3.5" 
    "4.9
    2"
    "8.2"
    "5.6
    3"
    

    Die ersten beiden Zeichen 1; werden übersprungen - durch das ignore(2) .
    Dann wird bis zum nächsten Semikolon gelesen. Also 3.5
    Dann wird bis zum nächsten Semikolon gelesen, das ist zwar erst in der nächsten Zeile, aber getline liest das newline ein und speichert es im string:
    "4.9
    2".
    Aus dem string bastelst Du den stringstream und schiebst die erste Gleitkommazahl in Dein Array - die 2 lässt Du im stringstream.
    Dann wird bis zum nächsten Semikolon gelesen: 8.2.
    Dann wird bis zum nächsten Semikolon gelesen - wieder über das newline:
    "5.6
    3",
    d.h. 5.6 speicherst Du, die 3 lässt Du im stringstream.
    usw. usf.

    Capisce?

    Ich wette, wenn Du Werners link folgst (Posting von 22:58) findest Du umfangreiche Informationen, wie Du csv besser einlesen kannst, als an einem Zeilenumbruch zu scheitern.

    Alles keine Raketentechnik kann aber beliebig kompliziert werden - und als absolute Notlösung bleibt auch immer noch die Möglichkeit jede Zeile in einen string zu lesen und dann weiterzuverarbeiten.



  • Diese Diskussion hier ist doch wieder ein schönes Beispiel für meine These
    "getline ist doof" (einfach mal bei Google eingeben)

    😉



  • Oh man haha. Ja jetzt hab ichs.

    Aber wenn ich jetzt theoretisch die erste Spalte auch komplett nicht brauchen würde, würde das so funktionieren oder? Ok, ist sicherlich keine schöne Lösung, aber rein theoretisch müsste das doch auch so gehen oder?

    PS: Danke nochmal an Werner für die Links!



  • RHCP_Drums schrieb:

    Aber wenn ich jetzt theoretisch die erste Spalte auch komplett nicht brauchen würde, würde das so funktionieren oder? Ok, ist sicherlich keine schöne Lösung, aber rein theoretisch müsste das doch auch so gehen oder?

    Was heisst "theoretisch müsste das gehen"? Du hast doch gerade schon gezeigt, dass es auch praktisch funktioniert.
    Einlesen und dann verwerfen ist üblich. Häufig geht es gar nicht anders. (Es geht allerdings immer mit weniger Umwegen als hier.)

    Ob wir das "Phänomen" wirklich als Feature umdeuten können...ich weiss nicht...
    Etwas eingeschränkt in den Anwendungsmöglichkeiten ist das. 🙂



  • Na gut, dann sind meine Fragen damit alle beantwortet. Wieder was gelernt 😃 Ich danke euch!


Log in to reply