Zahlen aus txt auslesen und in array speichern


  • Mod

    Ja, unget() funktioniert nicht, da der Buffer nicht groß genug sein muss, um alle Zeichen aus der vorigen Zeile zu halten. Stattdessen kann man einfach die aktuelle Position zwischenspeichern (per tellg()) und anschließend dahin zurückspringen.

    #include <iostream>
    #include <sstream>
    
    int main()
    {
    	std::istringstream stream(" 4 8 \n"
    	                          " 8 456\n"
    	                          "12 536\n"
    	                          "undef, undef");
    
    	std::string str;
    	std::streampos pos = -1, current = 0;
    
    	while( std::getline(stream, str) && str != "undef, undef" )
    	{
    		pos = current;
    		current = stream.tellg();
    	}
    
    	if( pos != -1 )
    	{
    		stream.seekg(pos);
    		int a, b;
    		stream >> a >> b;
    		std::cout << a << ' ' << b << '\n';
    	}
    }
    


  • ArtiTT schrieb:

    Ich habe eine .txt Datei die wie folgt aufgebaut ist:

    0, 0.3245
    0, 0.5566
    .
    .
    0, 0.9598
    undef, undef
    1, 0.0945
    1, 0.8748
    .
    .
    1, 0.8789
    undef, undef
    2, 0.035
    2, 0.9545
    .
    .
    .
    usw.

    Ich würde gerne folgendes erreichen: die letzten Werte vor dem undef, undef (also z.B. 0 und 0.9598) immer in ein Array abspeichern um sie später verwenden zu können; .

    Hallo ArtiTT,

    ich unterstelle mal, dass - wenn die Datei so aussieht wie oben - in dem Array später folgendes 2 Einträge stehen sollen:

    0, 0.9598
    1, 0.8789
    

    Weiter unterstelle ich, dass die erste Zahl ein Integer und die zweite eine Fließkommazahl ist.

    Zunächst mal liest man Zahlenwerte wie Integer und Fließkomma so, wie es Skym0sh0 schon gezeigt hat - siehe auch operator>>. Jetzt muss man nur noch ein wenig Code schreiben, der das 'undef, undef' erkennt und dann speichert man sich den zuletzt gelesen Eintrag (bestehend aus int und double) in dem Array ab.
    Das ist schon alles.

    Das ganze könnte etwa so aussehen:

    #include <iostream>
    #include <fstream>
    #include <vector>
    #include <limits> // numeric_limits
    
    struct EineStruct
    {
        EineStruct()
            : id(), number()    // Defaultkonstruktor sorgt für definierte Daten
        {}
        int id;
        double number;
    };
    // --   Funktion zum Einlesen einer Struktur 'EineStruct'
    //      Format "<int>, <double>"
    std::istream& operator>>( std::istream& in, EineStruct& s )
    {
        char komma;
        if( in >> s.id >> komma && komma != ',' )
            in.setstate( std::ios_base::failbit ); // Formatfehler -> dann fail
        return in >> s.number; 
    }
    
    int main()
    {
        using namespace std;
        vector< EineStruct > ein_array;
        ifstream datei("eineDatei.txt");
        if( !datei.is_open() )
        {
            cerr << "Fehler beim Oeffnen der Datei" << endl;
            return -2;
        }
        for( EineStruct eintrag; datei; )
        {
            char c;
            if( datei >> c && c == 'u' ) // "undef, undef"
            {
                ein_array.push_back( eintrag ); // zuletzt gelesenen Eintrag speichern da jetzt 'undef, undef' folgt
                datei.ignore( numeric_limits< streamsize >::max(), '\n' ); // überspringe diese Zeile
            }
            else 
                datei.putback( c ) >> eintrag; // Character 'c' wieder zurück stellen und Eintrag zunächst nur lesen
        }
        if( datei.eof() ) // EOF erreicht -> alles iO
        {
            cout << "es sind " << ein_array.size() << " Eintrage gelesen worden" << endl;
            // ... usw.
        }
        return 0;
    }
    

    Gruß
    Werner


  • Mod

    Ich würde gerne folgendes erreichen: die letzten Werte vor dem undef, undef (also z.B. 0 und 0.9598) immer in ein Array abspeichern um sie später verwenden zu können; .

    Habe ich das falsch interpretiert? Er wollte doch nur die letzten beiden Werte vor der besagte Zeile?



    • schrieb:

    Ich würde gerne folgendes erreichen: die letzten Werte vor dem undef, undef (also z.B. 0 und 0.9598) immer in ein Array abspeichern um sie später verwenden zu können; .

    Habe ich das falsch interpretiert? Er wollte doch nur die letzten beiden Werte vor der besagte Zeile?

    aus dem 'immer' und dem Beispiel schließe ich, dass ArtiTT alle Einträge vor allen 'undef, undef's einlesen möchte.

    Aber er möge es doch bitte selber mal bestätigen oder korrigieren
    @ArtiTT -> Feedback wanted!

    Gruß
    Werner



  • @ Werner: ja genau so habe ich es gemeint...

    dein Code ist sehr kompliziert für mich aber schein zu funktionieren 🙂
    ich kenne zwar Klassen aber was die genau machen weiß ich nicht mehr genau 😕

    wie lese ich bei deinem Code die Array aus ?

    mit cout << array_ein[i] << endl; scheint es nicht zu klappen, oder muss ich da i.wie auf die struct verweisen, sorry.

    Gruß, Danke schonmal, super Forum hier 🙂

    P.S. kann man das auch i.wie in einer Anfänger-freundlichen Version formulieren ohne Klassen?



  • ArtiTT schrieb:

    P.S. kann man das auch i.wie in einer Anfänger-freundlichen Version formulieren ohne Klassen?

    Ja, indem du die Methode von mir und Th69 probierst.



  • @Skym0sh0:

    dein Code funktioniert bei mir nicht wegen dieser Zeile:

    vec.emplace_back(a, b);
    

    Da kommt die Fehlermeldung:

    Error: Keine Instanz von Überladen Funktion

    Ist das nur bei mir so ? Verwende Microsoft Visual 2010



  • VS2010 ist etwas zu alt um C++11 vollständig zu unterstützten. Es gibt dort emplace_back nur mit einem Argument. Benutze stattdessen vec.push_back(make_pair(a, b));



  • bzw. ich hab das jetzt so probiert mit den Strings:

    #include <iostream> 
    #include <fstream>
    #include <string>
    #include <stdio.h>
    
    using namespace std;
    
    int main(){ 
    
    	ifstream datei;
    	datei.open("text.txt");
    
    	string gesucht = "undef, undef";
    	string line1, line2;
    
    	double a,b;
    	char komma;
    
    	if(datei.is_open())
    	{
    		while (datei.end){
    
    			getline(datei,line1);
    
    			if(line1 == gesucht)
    			{
    				cout << line2 << endl;
    				system("pause");
    			}
    
    			getline(datei,line2);			
    
    			if(line2 == gesucht)
    			{
    				cout << line1 << endl;
    				system("pause");
    			}
    
    		}
    	}
    
    	else cout << "Unable to open file" << endl;
    
    	system("pause");
    	return 0; 
    }
    

    Aber wie zerlege ich jetzt den String in zwei Zahlen: Also ich würde gerne die erste zahl int und zweite double, beide in eine Matrix speichern: Matrix[][]

    Hab schon nach dem strtok geschaut aber werde daraus nicht schlau.

    Gruß



  • VS 2010 unterstützt nur in Teilen C++11. VS 2012 ist da schon fortgeschrittener.

    Du kannst aber auch einfach

    vec.push_back(std::make_pair(a, b));
    

    schreiben.

    Edit: ups, zu spät 😉


  • Mod

    phanzy schrieb:

    ArtiTT schrieb:

    P.S. kann man das auch i.wie in einer Anfänger-freundlichen Version formulieren ohne Klassen?

    Ja, indem du die Methode von mir und Th69 probierst.

    Sowie meine. Funktioniert genauso für File-Streams:

    #include <iostream>
    #include <sstream>
    #include <vector>
    #include <limits>
    
    int main()
    {
    	std::stringstream stream("0, 0.3245\n0, 0.9598\nundef, undef\n1, 0.0945 \n1, 0.8748\n1, 0.8789\nundef, undef");
    
    	std::vector<std::pair<int, double>> values;
    
    	std::streampos pos = -1, current = 0;
    
    	while( stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n') )
    	{
    		pos = current;
    		current = stream.tellg();
    
    		if( stream.peek() == 'u' ) // Ich mache es mal wie Werner und gehe davon aus,
    		{                          // dass undef, undef die einzige mit 'u' beginnende Zeile ist
    			stream.seekg(pos);
    
    			std::pair<int, double> pair;
    			(stream >> pair.first).ignore() >> pair.second >> std::ws;
    
    			values.push_back(pair);
    
    			stream.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
    		}
    	}
    
    	/// C++11 - nur zum Test
    	for( auto const& pair : values )
    		std::cout << pair.first << ' ' << pair.second << '\n';
    }
    

    Die Zahlen werden aber, wie bei Werner, direkt extrahiert - nachträgliches Zerlegen ist also überflüssig und darum brauchst du dir keine Gedanken machen.



  • Hallo ArtiTT,

    ArtiTT schrieb:

    dein Code ist sehr kompliziert für mich aber schein zu funktionieren 🙂
    ich kenne zwar Klassen aber was die genau machen weiß ich nicht mehr genau 😕

    ja, ja - immer das gleiche Problem 😉

    Es wäre mir lieb, wenn Du schreiben würdest, was Du genau nicht verstehst - statt "ist sehr kompliziert".

    ArtiTT schrieb:

    wie lese ich bei deinem Code die Array aus ?
    mit cout << array_ein[i] << endl; scheint es nicht zu klappen, oder muss ich da i.wie auf die struct verweisen, sorry.

    Im einfachsten Fall mit

    for( int i=0; i < ein_array.size(); ++i )
            cout << ein_array[i].id << ", " << ein_array[i].number << "\n";
    

    ArtiTT schrieb:

    kann man das auch i.wie in einer Anfänger-freundlichen Version formulieren ohne Klassen?

    Na ja - ich kann es mal versuchen. Es folgt eine Version, die ich für unstrukturiert und komplizierter halte, als das was ich schon gepostet habe. Aber ich denke ein Anfänger würde es eher so schreiben:

    #include <iostream>
    #include <fstream>
    #include <vector>
    #include <limits> // numeric_limits
    
    int main()
    {
        using namespace std;
        vector< int > ein_array_ints;
        vector< double > ein_array_double; // das so aufzutrennen, ist definitiv ein Designfehler, aber bitte ....
    
        ifstream datei("eineDatei.txt");
        if( !datei.is_open() )
        {
            cerr << "Fehler beim Oeffnen der Datei" << endl;
            return -2;
        }
    
        int id = 0;
        double number = 0.0; 
        while( datei.good() ) // zusätzliche Abbruchbedingung s.u.
        {
            char c;
            if( datei >> c && c == 'u' ) // "undef, undef"
            {
                ein_array_ints.push_back( id );
                ein_array_double.push_back( number );  // zuletzt gelesenen Eintrag speichern da jetzt 'undef, undef' folgt
    
                datei.ignore( numeric_limits< streamsize >::max(), '\n' ); // überspringe diese Zeile
            }
            else
            {   // --   hier versuchen, ein "id, number" zu lesen
                datei.putback( c ); // den zuviel (s.o.) gelesenen Character 'c' wieder zurück stellen
                datei >> id; // int lesen
                char komma;
                datei >> komma; // ',' lesen
                if( datei.fail() )
                    break; // es ist ein Fehler beim Lesen aufgetreten -> Abbruch der Schleife
                if( komma != ',' ) // da war kein Komma!
                    break; // Formatfehler -> dann ebenso Abbruch
                datei >> number; // double lesen.
                if( datei.fail() )
                    break; // irgendwas ist beim Lesen schief gegangen, hier Abbruch von for(;;)
            }
        }
        if( datei.eof() ) // EOF erreicht -> alles iO
        {
            cout << "es sind " << ein_array_ints.size() << " Eintrage gelesen worden" << endl;
            // ... usw.
    
            for( int i=0; i<ein_array_ints.size(); ++i  )
                cout << ein_array_ints[i] << ", " << ein_array_double[i] << endl;
        }
        return 0;
    }
    

    Gruß
    Werner

    PS.: IO in C++ ist nicht Anfänger freundlich



  • @Werner: funktioniert super... ich schaue mir heute nochmal den Code genau an und google die ganzen Sachen bevor ich hier noch mehr frage 🙂

    Danke 🙂


Anmelden zum Antworten