Einlesen von daten file



  • Wenn ich jetzt aber diese jeweils letzte Ziffer jeder Zeile in einen eigenen Vector der Länge 30000 abspeichern wolle, muss ich das auch in den operator Überladungs Funktionen machen geschickterweise oder?



  • geschickter weise willst du die datei bzw. jede einzelnen zeile nur einmal einlesen/verarbeiten, ja.

    die beste lösung dafür, wäre wohl statt

    using record_t = vector<double>;
    

    eine eigene klasse zu verwenden:

    struct record_t {
      vector<double> values;
      double first; //willst du vielleicht auch
      double last;
    };
    

    aber wie genau dieser selbstdefinierte typ aussehen sollte, hängt natürlich am meisten davon ab, was genau du damit weiter tun willst.



  • Also die erste Zahl in jeder Zeile ist tatsächlich nur nen Zeilenindex und da später die Zeilen sowieso randomisiert werden, kann ich die verfallen lassen.
    Alle weiteren Zahlen in den 30k Zeilen will ich wie bereits geschehen in nem 2 dimensionalen vector abspeichern. Nur zusätzlich möchte ich jetzt noch die letzten Zahlen einer jeden Zeile in einer Art Spaltenvektor abspeichern



  • Hab es jetzt folgendermaßen gelöst, gibt es von Eurer Seite aus Verbesserungsvorschläge?

    #include <fstream>
    #include <iostream>
    #include <sstream>
    #include <string>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    using record = vector <float>;
    
    struct recordStruct {
    	record rec;
    	int lbl;
    };
    
    struct dataStruct {
    	vector <record> dsgnMat;
    	vector<int> labels;
    };
    //-----------------------------------------------------------------------------
    // Let's overload the stream input operator to read a list of CSV fields (which a CSV record).
    // Remember, a record is a list of doubles separated by commas ';'.
    istream& operator >> (istream& ins, recordStruct& recordLine)
    {
    	recordLine.rec.clear();
    	string line;
    	getline(ins, line);
    	istringstream ss(line);
    
    	//ignoriere den ersten wert (bis zum ersten ';') 
    	ss.ignore(std::numeric_limits<std::streamsize>::max(), ';');
    
    	while (ss) {
    		float f{ 0.f };
    		char tmp;
    		if (ss >> f >> tmp) 
    			recordLine.rec.push_back(f); //ignoriert den letzten wert 
    		else 
    			if (f == 1) 
    				recordLine.lbl = 1;
    			else if (f == 2)
    				recordLine.lbl = -1;
    	}
    	return ins;
    }
    
    //-----------------------------------------------------------------------------
    // Let's likewise overload the stream input operator to read a list of CSV records.
    // This time it is a little easier, just because we only need to worry about reading
    // records, and not fields.
    istream& operator >> (istream& ins, dataStruct& data)
    {
    	// make sure that the returned data only contains the CSV data we read here
    	// clear once
    	data.dsgnMat.clear();
    	data.labels.clear();
    	ins.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    	// For every record we can read from the file, append it to our resulting data
    	recordStruct recordLine;
    	while (ins >> recordLine)
    	{
    		data.dsgnMat.push_back(recordLine.rec);
    		data.labels.push_back(recordLine.lbl);
    	}
    
    	// Again, return the argument stream as required for this kind of input stream overload.
    	return ins;
    }
    
    //-----------------------------------------------------------------------------
    // Now to put it all to use.
    int main()
    {
    	// Here is the data we want.
    	dataStruct data;
    
    	// Here is the file containing the data. Read it into data.
    	ifstream infile("RadarDataset.txt");
    	infile >> data;
    
    	// Complain if something went wrong.
    	if (!infile.eof())
    	{
    		cout << "Could not find data file!\n";
    		return 1;
    	}
    
    	infile.close();
    
    }
    


  • hallo,

    ja, ich würde den operator>> für die einzelne zeile unter diesen umständen als eigene funktion implementieren. die ist außerdem ja eh nur dazu da, eine einzelne zeile einzulesen und sollte am ende nicht öffentlich sichtbar sein.

    du verschwendest ja ein bisschen speicherplatz in dem du zwischen record und recordStruct unterscheidest. das ist aber gar nicht nötig.

    using record_t = vector <float>; 
    using label_t = int; //der symmetrie wegen
    
    struct dataStruct { 
        vector <record_t> dsgnMat; 
        vector<label_t> labels; 
    }; 
    
    std::pair<record_t, label_t> read_record(istream& ins) //statt operator>>
    //std::pair, weil die funktion zwei rückgabewerte hat - einen record und ein label
    { 
        string line; 
        getline(ins, line); 
        istringstream ss(line); 
    
        //ignoriere den ersten wert (bis zum ersten ';') 
        ss.ignore(std::numeric_limits<std::streamsize>::max(), ';'); 
    
        record_t recordLine;
    
        while (ss) { 
            float f{ 0.f }; 
            char tmp; 
            if (ss >> f >> tmp) 
                recordLine.push_back(f); //ignoriert den letzten wert 
            else {
                if (f == 1) 
                    return std::make_pair(std::move(recordLine), 1);
                else if (f == 2) 
                    return std::make_pair(std::move(recordLine), -1);
            }
        } 
    
        throw std::runtime_error{"wrong format"}; //evtl auftretende fehler
       //könntest du noch berücksichtigen. mit rückabwicklung und so, ist eine 
       //gute denkaufgabe.
    } 
    
    istream& operator >> (istream& ins, dataStruct& data) 
    { 
        // make sure that the returned data only contains the CSV data we read here 
        // clear once 
        data.dsgnMat.clear(); 
        data.labels.clear(); 
    
        ins.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 
        // For every record we can read from the file, append it to our resulting data 
        record_t recordLine; 
        label_t label;
    
        for (std::tie(recordLine,label) = read_record(ins); ins;
             std::tie(recordLine, label) = read_record(ins))
        { 
            data.dsgnMat.push_back(std::move(recordLine)); 
            data.labels.push_back(label); 
        } 
    
        // Again, return the argument stream as required for this kind of input stream overload. 
        return ins; 
    }
    


  • Danke vielmals. Wozu dienen move und tie an der Stelle?



  • wenn eine funktion mit mehreren rückgabewerten (std::pair, std::tuple) arbeitet, dann kann man mit std::tie die einzelnen rückgabewerte in einzelne variablen packen.

    ohne tie:

    pair<int, int> some_function () {
      return make_pair(1, 2);
    }
    
    //...
    
    pair<int, int> result = some_function();
    cout << result.first << ", " << result.second; //nicht schön.
    

    mit tie:

    pair<int, int> some_function () {
      return make_pair(1, 2);
    }
    
    int result, status;
    tie(result, status) = some_function();
    cout << result << ", " << status;
    

    (mit C++17 wird das noch üblicher werden)

    move dient dazu, zu verhindern, dass eine kopie angelegt wird. mit move sage ich dem compiler: "mach keine kopie, sondern stiehl dem vector den inhalt, ich verspreche dir, ich rühre den original-vector nicht mehr an" - das was nach einem move von der variable übrig bleibt, ist sozusagen nur mehr eine "leere hülle".

    bei push_back ebenso, weil dort auch sonst eine kopie des originaldatensatzes erstellt würde, obwohl du ja gar keine kopie brauchst.



  • Danke, ich habe durch diesen Thread schon unwahrscheinlich viel gelernt 🙂

    Mh habe deinen Code statt meinem jetzt in mein Projekt eingebunden und jetzt crasht es beim ausführen "Abort() has been called"

    #include <fstream>
    #include <iostream>
    #include <sstream>
    #include <string>
    #include <vector>
    #include <algorithm>
    #include <tuple> //std::pair
    #include <utility> //std::tuple
    using namespace std;
    
    using record_t = vector <float>;
    using label_t = int; //der symmetrie wegen 
    
    struct dataStruct {
    	vector <record_t> dsgnMat;
    	vector<label_t> labels;
    };
    
    std::pair<record_t, label_t> read_record(istream& ins) //statt operator>> 
    													   //std::pair, weil die funktion zwei rückgabewerte hat - einen record und ein label 
    {
    	string line;
    	getline(ins, line);
    	istringstream ss(line);
    
    	//ignoriere den ersten wert (bis zum ersten ';') 
    	ss.ignore(std::numeric_limits<std::streamsize>::max(), ';');
    
    	record_t recordLine;
    
    	while (ss) {
    		float f{ 0.f };
    		char tmp;
    		if (ss >> f >> tmp)
    			recordLine.push_back(f); //ignoriert den letzten wert 
    		else {
    			if (f == 1)
    				return std::make_pair(std::move(recordLine), 1);
    			else if (f == 2)
    				return std::make_pair(std::move(recordLine), -1);
    		}
    	}
    
    	throw std::runtime_error{ "wrong format" }; //evtl auftretende fehler 
    												//könntest du noch berücksichtigen. mit rückabwicklung und so, ist eine 
    												//gute denkaufgabe. 
    }
    
    istream& operator >> (istream& ins, dataStruct& data)
    {
    	// make sure that the returned data only contains the CSV data we read here 
    	// clear once 
    	data.dsgnMat.clear();
    	data.labels.clear();
    
    	ins.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    	// For every record we can read from the file, append it to our resulting data 
    	record_t recordLine;
    	label_t label;
    
    	for (std::tie(recordLine, label) = read_record(ins); ins;
    		std::tie(recordLine, label) = read_record(ins))
    	{
    		data.dsgnMat.push_back(std::move(recordLine));
    		data.labels.push_back(label);
    	}
    
    	// Again, return the argument stream as required for this kind of input stream overload. 
    	return ins;
    }
    
    //-----------------------------------------------------------------------------
    // Now to put it all to use.
    int main()
    {
    	// Here is the data we want.
    	dataStruct data;
    
    	// Here is the file containing the data. Read it into data.
    	ifstream infile("test.txt");
    	infile >> data;
    
    	// Complain if something went wrong.
    	if (!infile.eof())
    	{
    		cout << "Could not find data file!\n";
    		return 1;
    	}
    
    	infile.close();
    
    }
    


  • sorry, war mein fehler - die funktion wirft die exception (die ich selbst ohne nachzudenken eingefügt hab), weil eine zeile zu viel eingelesen wird.

    das liegt am ende daran, dass das eofbit zwar gesetzt ist, aber das keine auswirkung auf "fail()" hat und damit auf die bedingung in der schleife. musste ich jetzt selbst erst testen. die lösung besteht darin, die schleife so zu formulieren:

    do {
      std::tie(recordLine, label) = read_record(ins); 
      data.dsgnMat.push_back(std::move(recordLine)); 
      data.labels.push_back(label); 
    } while (!ins.eof());
    

    immer mit der dokumentation der funktionen arbeiten; bei solchen fehlern den debugger oder eigene debug-funktionen schreiben. dann kommt man schnell drauf, was los ist.



  • Also bei mir besteht das Problem weiterhin und beim Ausführen stürzt es ab



  • mm? bei mir auch. jetzt.
    liegt wohl daran, dass ich hier bei mir zum testen eine etwas andere version hatte und nicht aufgepasst, was ich wo noch geändert hatte...

    hab jetzt den code etwas aufgeräumt und umstrukturiert:

    using record_t = vector <float>; 
    using label_t = int;
    
    struct dataStruct { 
        vector <record_t> dsgnMat; 
        vector<label_t> labels; 
    }; 
    
    std::pair<record_t, label_t> record_from(string const& line) 
    //hier jetzt ohne den ursprünglichen stream zu übergeben.
    //zuviele streams machen mir den kopf ganz wirr.
    {  
        istringstream ss(line); 
    
        //ignoriere den ersten wert (bis zum ersten ';') 
        ss.ignore(std::numeric_limits<std::streamsize>::max(), ';'); 
        record_t recordLine; 
    
        float f{ 0.f }; 
        char tmp; 
        while (ss >> f >> tmp) {  
            recordLine.push_back(f); //ignoriert den letzten wert 
        }
    
        if (f == 1) {
            return std::make_pair(std::move(recordLine), 1); 
        } else if (f == 2) {
            return std::make_pair(std::move(recordLine), -1); 
        }
    
        throw std::invalid_argument{"wrong line format given to record_from" }; 
    } 
    
    istream& operator >> (istream& ins, dataStruct& data) 
    { 
        data.dsgnMat.clear(); 
        data.labels.clear(); 
    
        ins.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); 
    
        //so, besser einfach als kompliziert:
        string line;
        while (getline(ins, line)) {
    
            record_t recordLine; 
            label_t label; 
            std::tie(recordLine, label) = record_from(line); 
    
            data.dsgnMat.push_back(std::move(recordLine)); 
            data.labels.push_back(label); 
    
        }
        return ins; 
    } 
    
    //----------------------------------------------------------------------------- 
    // Now to put it all to use. 
    int main() 
    { 
        // Here is the data we want. 
        dataStruct data; 
    
        // Here is the file containing the data. Read it into data. 
        ifstream infile("test.txt"); 
    
        try { //jetzt mit ausnahmebehandlung.
          infile >> data; 
        } catch (...) {
          for (int i = 0; i < 100; ++i) 
             cout << "ich hätte exceptions nicht ansprechen sollen.";
          return -1;
        }
    
        // Complain if something went wrong. 
        if (!infile) //nicht eof! - eof steht für "end of file" 
        { 
            cout << "something went wrong!\n"; 
            return 1; 
        } 
    
        //zum testen:
        for (auto const& row: data.dsgnMat) {
            for (auto&& value: row) {
                cout << value << "; ";
            }
            cout << endl;
        }
    
        //das close am ende ist unnötig, die arbeit übernimmt der destruktor von infile
    }
    


  • Hallo Sewing,

    du solltest aber dann schnellstens lernen mit dem Debugger zu arbeiten, um solche Fehler zu analysieren und dann beheben zu können. Welche IDE benutzt du denn?



  • Visual Studio Community. Ja das stimmt, ich habe da Nachholbedarf und werde mir das schnellstmöglich aneignen. Leider funktioniert der obige Code immer noch nicht 😕



  • Unter Debugging with Visual Studio 2005/2008, Part 1: Debugging Concepts ff. (besonders Part 3) kriegst du einen guten Überblick über die Möglichkeiten.

    Kurzhinweis:
    Breakpoint setzen (Links-Klick auf linken Zeilenrand oder F9)
    Programm im Debugmodus starten
    Warten bis Breakpoint erreicht wird
    Im Einzelschritt (F10) die Zeilen ausführen (dann kannst du den Ablauf verfolgen und siehst im Watch-Fenster die aktuellen Variableninhalte)


Anmelden zum Antworten