csv Datei einlesen und verarbeiten



  • Hallo,

    Problem:

    Ich habe eine .csv Datei mit folgenden Inhalt:

    Max;Mustermann;18;m
    Anna;Test;22;w
    Nina;Muster;17;w

    Ich möchte diese Datei Schrittweiße auslesen d.h. Max wird in "vname" gespeichert, Mustrerman in "nname", 18 in "alter" und m in "geschlecht". Die Speicherung erfolgt jeweils in Strings. Nach dem Einlesen der ersten Zeile werden die Strings in einer Funktion verarbeitet. Dann soll das selbe mit den Nachfolgenden Zeilen geschehen, bis die .csv Datei am Ende ist.
    Ich weiß nicht wie ich das realisieren kann, das die Zeilen so ausgelesen werden.

    #include <iostream>
    #include <fstream>
    #include <string>
    using namespace std;
    
    int main(){
    	string vname, nname, alter, geschlecht;
    	ifstream csvread;
    	csvread.open("test.csv", ios::in);
    	if(csvread.is_open() != true){
    		cerr << "Fehler beim Lesen!" << endl;
    		csvread.close();
    	}
    	else{
    		getline(csvread, vname);
    		csvread.close();
    	}
    	return 0;
    }
    

    Dieser Code ließt jeweilst die ganze Zeile ein und speichert den Wert in "vname", aber wie realisier ich das, dass die Zeile ausgelesen wird und wenn ein ; kommt, ein anderer string zum speichert hergezogen wird?
    Und gibt es eine Funktion die testet, ob das Dateiende erreicht wurde?

    Hoffe ihr könnt mir helfen.

    MfG Domi



  • So könnte das Einlesen und trennen aussehen, du must die strings nur noch richtig zuweisen.

    #include <iostream>
    #include <fstream>
    #include <string>
    
    using namespace std;
    
    int main(){
        ifstream csvread;
        csvread.open("test.csv", ios::in);
        if(csvread){
    		//Datei bis Ende einlesen und bei ';' strings trennen
    		string s="";
    		while(getline(csvread, s, ';'))
    		{
    			cout << s << endl; //alle Strings getrennt ausgeben
    		}
    
            csvread.close();
        }
        else{
            cerr << "Fehler beim Lesen!" << endl;
        }
        return 0;
    }
    


  • Entweder stell ich mich grad bloß an, oder ich checks wirklich nicht...

    Dein Code speichert doch die einzelnen Werte nacheinander in den String s. Wie bekomme ich das jetzt hin, dass die erste Zeile eingelesen wird und in den einzelnen Strings gespeichert werden und danach das selbe mit der zweiten, dritten etc geschieht.

    getline(csvread, vname, ';');
    getline(csvread, nname, ';');
    getline(csvread, alter, ';');
    getline(csvread, geschlecht, '\n');
    // hier soll ein Funktionsaufruf kommen, der vname, nname, alter und geschlecht verarbeitet
    // jetzt soll mit der nächsten Zeile begonnen werden usw bis das Ende von der .csv Datei erreicht ist
    


  • #include <fstream>
    #include <iostream>
    #include <string>
    
    int main()
    {
    	std::ifstream file("test.csv");
    
    	if (!file)
    	{
    		return 1;
    	}
    
    	std::string line;
    	while (std::getline(file, line))
    	{
    		line += ";"; // Hack
    
    		size_t pos = 0;
    
    		while ((pos = line.find(';')) != std::string::npos)
    		{
    			std::string val = line.substr(0, pos);
    			line = line.substr(pos + 1);
    
    			std::cout << "'" << val << "'\n";
    		}
    
    		std::cout << std::endl;
    	}
    
    	return 0;
    }
    


  • Hallo Blacky666,

    mache es einfach so:

    #include <iostream>
    #include <fstream>
    #include <string>
    
    // --   Helferlein zum Überlesen eines Zeichens 'C'
    template< char C >
    std::istream& Char( std::istream& in )
    {
        char c;
        if( in >> c && c != C )
            in.setstate( std::ios_base::failbit );
        return in;
    }
    
    // --   Eine Person mit Einlesefunktion
    struct Person
    {
        enum Geschlecht { maennlich, weiblich };
        friend std::istream& operator>>( std::istream& in, Geschlecht& g )
        {
            char c;
            if( in >> c )
            {
                if( c == 'm' ) g = maennlich;
                else if( c == 'w' ) g = weiblich;
                else in.setstate( std::ios_base::failbit );
            }
            return in;
        }
        friend std::istream& operator>>( std::istream& in, Person& p )
        {   // Format: <Vor Name>;<Nach Name>;<Alter(int)>;<Geschlecht e{'w','m'}>
            using std::ws;
            return getline( getline( in >> ws, p.vname, ';' ) >> ws, p.nname, ';' )
                >> p.alter >> Char<';'> >> p.geschlecht;
        }
    
        // --   Members
        std::string vname;
        std::string nname;
        int alter;
        Geschlecht geschlecht;
    };
    
    int main()
    {
        using namespace std;
        ifstream csvread("test.csv");
        if( !csvread.is_open() )
        {
            cerr << "Fehler beim Oeffnen der Datei" << endl;
            return -1;
        }
        for( Person p; csvread >> p; ) // lese bis Fehler oder Ende
        {
            // hier kann ein Funktionsaufruf kommen, der p.vname, p.nname, p.alter und p.geschlecht verarbeitet
            ;
        }
        return 0;
    }
    

    Die Funktion zum Einlesen einer Zeile aus der csv-Datei ist in Zeile 33 und in Zeile 54 wird diese so lange immer wieder aufgerufen, bis ein Fehler passiert oder die Datei zu Ende ist.

    Gruß Werner



  • Puh, erstmal danke für die Hilfestellungen. Ich hätte evtl. dazuschreiben solln, dass ich Anfänger bin. @ Werner Salomon: Ich muss mich jetzt erstmal in deinen Code einlesen, da ich den noch nicht so ganz verstehe... 🙄



  • So, leider ist mir der Code von Werner Salomon (noch) zu kompliziert. Habe jetzt ein wenig rumprobiert und bin zu folgendem Ergebnis gekommen.

    #include <iostream>
    #include <fstream>
    #include <string>
    using namespace std;
    
    int main(){
        string vname, nname, alter, geschlecht;
        ifstream csvread;
        csvread.open("test.csv", ios::in);
        if(!csvread.is_open()) cerr << "Fehler beim Lesen!" << endl;
        else{
    		while(!csvread.eof()){
    			getline(csvread, vname, ';');
    			getline(csvread, nname, ';');
    			getline(csvread, alter, ';');
    			getline(csvread, geschlecht, '\n');
    		}
    		csvread.close();
        }
        return 0;
    }
    


  • Warum zum Teufel verwendest du nicht RAII?



  • 314159265358979 schrieb:

    Warum zum Teufel verwendest du nicht RAII?

    Weil ich RAII nicht kenne?! o.O



  • http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

    int main()
    {
        std::ifstream file("file.txt"); // File im Konstruktor öffnen
        //Tu was mit file
    } // Hier wird file automatisch im Destruktor geschlossen -> Scope Ende
    


  • RAII heißt "Ressource Aquisition is Initialisation" google mal danach.



  • 314159265358979 schrieb:

    Warum zum Teufel verwendest du nicht RAII?

    Wo benutzt er denn kein RAII? Nur weil er vorm Dtor explizit close aufruft, geht dadurch doch nicht das RAII flöten. Nur unnötig ist es vielleicht.



  • Files können RAII, das gehört genutzt, wenn man keinen guten Grund dagegen hat, basta.



  • Blacky666 schrieb:

    So, leider ist mir der Code von Werner Salomon (noch) zu kompliziert.

    Ok - macht nichts. Aber zwei Dinge solltest Du beherzigen:
    1.) packe Deine Variablen, die zusammen gehören, in eine Struktur (hier struct Person)
    2.) fasse das Einlesen dieser Struktur in einer Funktion zusammen (hier lese_ein)

    dann sähe das so aus:

    #include <iostream>
    #include <fstream>
    #include <string>
    
    // --   Eine Person
    struct Person
    {
        std::string vname;
        std::string nname;
        std::string alter;
        std::string geschlecht;
    };
    
    // --  diese Funktion liest eine Person vom std::istream
    std::istream& lese_ein( std::istream& in, Person& p )
    {
        getline( in, p.vname, ';');
        getline( in, p.nname, ';');
        getline( in, p.alter, ';');
        getline( in, p.geschlecht, '\n');
        return in;
    }
    
    int main()
    {
        using namespace std;
    
        ifstream csvread( "test.csv" );
        if(!csvread.is_open()) 
            cerr << "Fehler beim Oeffnen der Datei!" << endl;
        else {
            for( Person person; lese_ein( csvread, person ); ) {
                ; // tue was mit 'person'
            }
        }
        return 0;
    }
    

    Damit bist Du schon mal ziemlich sauber. Und sieht doch übersichtlicher aus - oder?

    Stelle einfach konkrete Fragen, wenn Du was nicht verstehst.

    Gruß
    Werner



  • Also ich habe mir das Mit RAII mal durchgelsen und bis jetzt sehr wenig verstanden. Ich arbeite mich aber weiter durch und versuche die Doku auf Wikipedia zu verstehen. Danke für die Links! 😉

    @ Werner Salomon:

    Diesen Code habe ich viel besser verstanden, aber ich habe noch Fragen dazu. Ich lernen C++ aus einem Buch (C++ von A bis Z). Ich habe auch das Kapitel Strukturen durchgearbeitet, habe bis jetzt aber noch keinen Anwendungsbereich dafür gefunden.

    Warum sollte ich in meinem Beispiel struct in Verbindung mit einer Einlesefunktion verwenden? Ja es ist auf jedenfall übersichtlicher, aber gibt es noch weitere Gründe?

    Mir ist jetzt schon öfters aufgefallen, dass viele Codebeispiele (nicht nur von dir) ohne "using namespace std" gepostet werden. Wenn man "using namespace std" am Anfang des Codes setzt (also nach #inlcude...) erspart dies doch Schreibarbeit. Ist das eine weitere Stilefrage, wie man den Quellcode "formatiert", oder gibt es andere Gründe warum man "using namespace std" weglässt?

    Folgende Code Zeile:

    istream& lese_ein( istream& in, Person& p )
    

    Warum muss ich istream& schreiben und nicht istream? Mit dem Zusatz "&" erhalte ich doch die Speicheradresse, oder?

    Folgende Code Zeile:

    for( Person person; lese_ein( csvread, person ); )
    

    Was genau passiert bei Person person? Bei Person ist die Struktur gemeint, oder? Aber wo ist person definiert?

    Ich hoffe es sind nicht zu viele Fragen. 🙄

    #include <iostream> 
    #include <fstream> 
    #include <string>
    using namespace std; // <- warum nicht hier?!
    
    struct Person
    { 
        string vname; 
        string nname; 
        string alter; 
        string geschlecht; 
    }; 
    
    istream& lese_ein( istream& in, Person& p ) // <- warum Speicheradresse von istream?!
    { 
        getline( in, p.vname, ';');
        getline( in, p.nname, ';'); 
        getline( in, p.alter, ';'); 
        getline( in, p.geschlecht, '\n'); 
        return in;
    } 
    
    int main() 
    { 
        ifstream csvread( "test.csv" ); 
        if(!csvread.is_open()) 
            cerr << "Fehler beim Oeffnen der Datei!" << endl; 
        else { 
            for( Person person; lese_ein( csvread, person ); ) { // <- was genau passiert bei: Person person?! 
                cout << person.vname << endl;
    			cout << person.nname << endl;
    			cout << person.alter << endl;
    			cout << person.geschlecht << endl;
    			getchar();
            } 
        } 
        return 0; 
    }
    


  • Blacky666 schrieb:

    Ich lernen C++ aus einem Buch (C++ von A bis Z).

    Das Buch hat hier im Forum einen ziemlich schlechten Ruf. Warum es bei amazon.de so gut abschneidet, ist mir schleierhaft. Es tauchen hier immer wieder Leute auf, die Probleme mit C++ haben, weil sie sich an Aussagen aus diesem Buch gehalten haben. Dem einen oder anderen ist daraufhin schon die Hutschnur geplatzt.

    Blacky666 schrieb:

    Ich habe auch das Kapitel Strukturen durchgearbeitet, habe bis jetzt aber noch keinen Anwendungsbereich dafür gefunden.

    Warum sollte ich in meinem Beispiel struct in Verbindung mit einer Einlesefunktion verwenden? Ja es ist auf jedenfall übersichtlicher, aber gibt es noch weitere Gründe?

    Nun - streng genommen ist das der einzige Grund. Aber dieser Grund ist so wichtig, dass man deshalb ganze Programmiersprachen entwickelt hat - u.a. C++. Du hast z.Zt. nur mit Programmen zu tun, die wenige oder nur eine Seite Code umfassen. Aber sobald es etwas mehr wird, merkt man sehr schnell, dass so etwas wie Übersichtlichkeit und Wartbarkeit existenziell für ein Programm bzw.ein Projekt sein können. Die Entwicklung von professionellen Programmen ist sehr teuer und wenn man als Programmierer die Übersicht verlierst, können Aufwände schnell mal um Faktor 2 bis 10 ansteigen - oder im schlimmsten Fall gar nicht mehr zu stemmen sein. Und es ist eben nicht egal, ob die Entwicklung der neuen Release eine oder 10 Millionen Euro kostet.

    Blacky666 schrieb:

    Mir ist jetzt schon öfters aufgefallen, dass viele Codebeispiele (nicht nur von dir) ohne "using namespace std" gepostet werden. Wenn man "using namespace std" am Anfang des Codes setzt (also nach #inlcude...) erspart dies doch Schreibarbeit. Ist das eine weitere Stilefrage, wie man den Quellcode "formatiert", oder gibt es andere Gründe warum man "using namespace std" weglässt?

    namespaces dienen dazu, sogenante 'Namenskollisionen' zu vermeiden. Nehmen wir mal das Wort 'string' - viele werden den Gedanken haben, einer Zeichenkette den Namen string zu geben. Das gilt sowohl für Typen als auch für Variablen. Also hat man den std::string in den namespace std getan, damit er sich von allen anderen strings unterscheiden kann. Und 'std' ist eben reserviert. Mit 'using namesapce std' hebt man diese Eindeutigkeit von std::string wieder auf.

    Bei einem nicht trivialen Programm wird eine Struktur wie 'Person' irgendwann einmal in einem Header-File landen, da Person von mehreren Dateien aus genutzt werden wird. Wenn Du jetzt vor(!) die Person ein 'using namespace std' schreibst, bist Du evt. - und wenn nur aus Bequemlichkeit - gewillt, diese Zeile mit in den Header zu übernehmen, weil in der Person hast Du überall schon das std:: beseitigt! Das wird zunächst wahrscheinlich gut gehen. Aber mit wachsender Größe Deines Projekts wächst auch die Wahrscheinlichkeit, dass irgendwo ein string auftaucht, der eben kein(!) std::string ist. Und dann ist es sehr schwer, aus den Compiler-Fehlern (und sei froh, wenn es dann welche gibt) die eigentliche Ursache des Problems auszumachen.

    Blacky666 schrieb:

    Folgende Code Zeile:

    istream& lese_ein( istream& in, Person& p )
    

    Warum muss ich istream& schreiben und nicht istream? Mit dem Zusatz "&" erhalte ich doch die Speicheradresse, oder?

    ja, man erhält die Speicheradresse - genauer die Referenz auf das istream-Objekt. Solltest Du istream (ohne &) schreiben, so wird das istream-Objekt kopiert und Du liest Deine Daten aus einer Kopie von istream - das würde sogar zunächst mal gut gehen, da istream auf die eigentliche Datenquelle nur einen Pointer enthält - also die Datenquelle wird in diesem Fall nicht mit kopiert. Am Ende der Funktion würde diese Kopie dann gelöscht werden und mit ihr auch alle Zustände die istream derweil angenommen hat - insbesondere der Status - Fehler und/oder EOF. Eine folgende Leseaktion würde also auf Zustände reagieren, die vor Deiner Funktion aktuell waren - im Allgemeinen wird das dann nicht funktionieren.

    Blacky666 schrieb:

    Folgende Code Zeile:

    for( Person person; lese_ein( csvread, person ); )
    

    Was genau passiert bei Person person? Bei Person ist die Struktur gemeint, oder? Aber wo ist person definiert?

    Es passiert im Prinzip das selbe wie bei

    for( int i=0; lese_ein( csvread, i ); )
    

    Es wird ein Objekt person (i) vom Typ Person (int) angelegt. Mit Person ist die 'struct Person' genmeint. 'person' ist das Objekt und seine Gültigkeit endet mit Ende der for-Schleife. Der kleine Unterschied zwischen int und Person liegt darin, dass int ein sogenannter POD ist (plain old data) - so eine Art 'nacktes' Datum, wohingegen Person implizit einen Konstruktor besitzt, da es Member besitzt, die explizit einen Konstruktor haben (std::string). Dieser Konstruktor wird beim Anlegen automatisch aufgerufen und sorgt dafür, dass die internen Daten des std::string korrekt initialisiert sind. Beim int ist das nicht der Fall, erst mit dem anschließenden '=0' wird i mit 0 initialisiert.

    Gruß
    Werner



  • Das Buch hat hier im Forum einen ziemlich schlechten Ruf. Warum es bei amazon.de so gut abschneidet, ist mir schleierhaft. Es tauchen ...

    Das habe ich jetzt auch schon öfters gehört und jetzt bin ich auch soweit, um mir ein neues Lern und Nachschlagewerk zu besorgen. Könntest du mir ein bestimmtes empfehlen?

    Nun - streng genommen ist das der einzige Grund. Aber dieser Grund ist so wichtig, dass man deshalb ganze Programmiersprachen entwickelt hat - u.a. C++. Du hast ...

    Jetzt leuchtet mir die Funktionalität von Strukturen immer mehr ein. Ein weiteres Negatives Beispiel, was der Autor von C++ Von A bis Z meiner Meinung nach falsch macht. Er erklärt vieles sehr umständlich und sehr ungenau. Nur allein durch deine Erklärung leuchtet es mir ein, für was Strukturen da sind.

    namespaces dienen dazu, sogenante 'Namenskollisionen' zu vermeiden. Nehmen wir mal das Wort 'string' - viele werden den Gedanken haben, einer Zeichenkette den Namen ...

    Also heißt es man sollte grundsätzlich expliziet bei Funktion, Strukturen, Klassen ... std:: verwenden? <- auf bezug des Buches C++ von A bis Z, geht der Autor auf diese Thematik fast gar nicht bzw. sehr wenig darauf ein...

    [/code]ja, man erhält die Speicheradresse - genauer die Referenz auf das istream-Objekt. Solltest Du istream (ohne &) ...[quote]

    Ist dann wenn ich die Speicheradresse erhalte die Call by Reference Variante und wenn ich & weg lasse die Call by value Variante?

    Danke für deine sehr guten Ausführlichen Erklärungen!



  • auf bezug des Buches C++ von A bis Z, geht der Autor auf diese Thematik fast gar nicht bzw. sehr wenig darauf ein...

    Hier steht sogar warum:
    http://www.c-plusplus.net/forum/272350

    Das Buch kann man fürs Lernen leider nicht empfehlen.



  • Blacky666 schrieb:

    ... und jetzt bin ich auch soweit, um mir ein neues Lern und Nachschlagewerk zu besorgen. Könntest du mir ein bestimmtes empfehlen?

    Wenn ich DAS Buch für C++-Anfänger kennen würde, hätte ich es Dir bereits vorgeschlagen. Volkard empfiehlt den Breymann. Ich persönlich finde auch das Buch Accelerated C++ von Andrew Koenig & Barbara Moo recht gut (ist aber in englisch).
    Du kannst Dich auch selber hier im Forum bei den Büchern informieren oder auch bei ACCU(suche nach C++). Letzteres wieder alles in englisch.
    Vielleicht hilft Dir auch dieser Thread über Breymanns Buch und die darin enthaltenen Links weiter.

    Blacky666 schrieb:

    Nur allein durch deine Erklärung leuchtet es mir ein, für was Strukturen da sind.

    Das freut mich, wenn die Erklärung hilfreich ist.

    Blacky666 schrieb:

    Also heißt es man sollte grundsätzlich expliziet bei Funktion, Strukturen, Klassen ... std:: verwenden?

    Man sollte ein 'using namespace' nicht im Header verwenden, damit nicht irgendwo 'aus Versehen' ein namespace ausgehebelt wird. Daraus folgt dann, dass man bei Deklaration einer Funktion, Struktur oder Klasse den Prefix (hier std:: ) mit angeben sollte, wenn nötig, da eine Deklaration meistens in einem Header-File landet.

    Blacky666 schrieb:

    Werner Salomon schrieb:

    ja, man erhält die Speicheradresse - genauer die Referenz auf das istream-Objekt. Solltest Du istream (ohne &) ...

    Ist dann wenn ich die Speicheradresse erhalte die Call by Reference Variante und wenn ich & weg lasse die Call by value Variante?

    ja - genau so.

    Gruß
    Werner



  • Ok jetzt habe ich das mit 'namespace' auch endlich mal verstanden.

    Zu den Büchern: Ich weiß das Englische Programmierbücher immer besser sind, aber ich habe noch ein wenig Respekt vor diesen, da ich mir in meinem Englisch nicht so sicher bin.
    Das Problem ist jetzt wirklcih ein gutes Buch für mich zu finden, da manche Bewertungen von 'C++ Lernen und professionell anwenden' auch nicht so vielversprechend sind. Gerade Anfänger werden davon abgehalten dieses Buch zu kaufen...
    Wie schaut es mit diesem Exemplar aus?
    http://www.amazon.de/Einführung-die-Programmierung-mit-C/dp/3868940057/ref=sr_1_9?ie=UTF8&qid=1299150238&sr=8-9


Log in to reply