CSV Datei unbekannter Größe einlesen (wxWidgets)



  • Hallo zusammen,

    zunächst weiß ich nicht genau ob es das Richtige Forum ist, also falls nicht bitte nicht schimpfen:-)

    Etwas zu mir, ich habe recht gute Kentnisse in C (aus dem Mikroprozessorbereich) jedoch kaum Kenntnisse in c++. Ich möchte mit wxWidgets daten eines Geräte grafisch darstellen. Die Daten kommen aus einer CSV Datei und sehen wie folgt aus:

    Date;Time;Station ID;Charger Mode;Ubat;Iin;Iout;Ibat;Uin;Batt. Temp.;Uabsoprtion;Ufloat;Ucutoff;Ucutoff_recovery;Ilim_bat;AH_counter;MEMfree;PWM;MPP_PWM;IP;AGPS_IP;Iout_offset;Iin_offste;ADCoffset;GPSlatitude;GPSlongitude;GPSaltitude;Batt_cells;Batt_Nr;Batt_Capacity;U_absortion_cell;t_absorption;Uabsorption_exit_cell;u_float_cell;t_float_exit;u_float_exit;u_cuttoff_cell;u_cutoff_recovery_cell;temp_coefficient;i_lim_c_x;u_equalize_cell;t_equalie_time;t_equalize_int;p_in_act;p_out_act;p_in_y;p_out_y;p_in_t;p_out_t;output_state;input1;input2;input3;input4;Last Log Message
    12/9/2011;10:1:1;3146323432340c02000900;2;25.98;00.00;00.38;-0.02;00.00;21.00;28.94;27.50;24.00;25.20;04.00;00.00;1739;0255;0255;010.170.141.015;000.000.000.000;170;167;168;000.000000;000.000000;000.000000;12;00;012;02.40;00180;2.28;2.28;00030;2.23;2.00;2.10;-0.004;00003;0.00;005;005;000000;000000;000000;000000;000000;000000;1;4095;4095;4095;4095
    12/9/2011;10:1:47;3146323432340c02000900;2;25.95;00.00;00.00;-0.02;00.00;21.00;28.94;27.50;24.00;25.20;04.00;00.00;1739;0255;0255;010.170.141.015;000.000.000.000;170;167;168;000.000000;000.000000;000.000000;12;00;012;02.40;00180;2.28;2.28;00030;2.23;2.00;2.10;-0.004;00003;0.00;005;005;000000;000000;000000;000000;000000;000000;1;4095;4095;4095;4095
    12/9/2011;22:59:31;3146323432340c02000900;2;25.93;00.00;00.00;-0.02;00.00;22.00;28.90;27.46;24.00;25.20;04.00;00.01;1739;0255;0255;010.181.134.218;000.000.000.000;172;167;163;000.000000;000.000000;000.000000;12;00;012;02.40;00180;2.28;2.28;00030;2.23;2.00;2.10;-0.004;00003;0.00;005;005;000000;000000;000000;000000;000000;000000;1;4095;4095;4095;4095
    12/9/2011;23:0:1;3146323432340c02000900;2;25.95;00.00;00.00;-0.02;00.00;22.00;28.90;27.46;24.00;25.20;04.00;00.01;1739;0255;0255;010.181.134.218;000.000.000.000;172;167;163;000.000000;000.000000;000.000000;12;00;012;02.40;00180;2.28;2.28;00030;2.23;2.00;2.10;-0.004;00003;0.00;005;005;000000;000000;000000;000000;000000;000000;1;4095;4095;4095;4095
    

    Also durch Semikolon getrennt. Die Größe der Datei kann unterschiedlich sein.
    Ich hab jetzt mal wie folgt angefangen:

    // Gespeicherte Datensätze laden
        // --------------------------------
    
        // CSV Datei öffnen
        wxTextFile file(log_file_path );
        if (file.Exists()==false)
        {
            wxMessageBox("Kann LOG Datei nicht öffnen");
            return;
        }
    
        struct Daten
        {
            wxDateTime date_time;
            wxString station_id[30];
            long mode;
            double u_batt;
            double i_in;
    
        };
    
        wxString Kopfzeile;
        Kopfzeile = file.GetFirstLine();
    
        wxString line;
    
        while(file.Eof()==false)
        {
            // Hier muss die Datei Zeilenweise gelesen werden und den Variablen
            // zugeordnet werden
           line = file.GetNextLine();
    
        }
    

    Wie mache ich es die Auftrennung der Zeile jetzt am besten?

    Und noch viel wichtiger, wie bekomme ich die Struktur in ein Array mit variabler Größe?

    Später soll aus einer anderen Klasse der Zugriff auf diese Datensätze möglich sein, irgenwie sowas in der art:

    BatterieSpannung =Station.GetDatensatz(Datensatznummer).u_batt;

    Wie macht man sowas? Hab was von vectoren gelesen aber komme nicht richtig klar damit...

    Gruß
    Falko



  • Koennte man in etwa so machen:

    int alt;
    int neu;
    bool success;
    
    wxVector<Daten> DatenVec;
    
    while (file.Eof() == false)
    {
       success = false;
       Daten Data;
    
       line = file.GetNextLine()
       neu = line.find(";");
    
       if (neu == wxString::npos)
          break;
    
       wxDateTime date;
       wxString::const_iterator end;
       if (!date.ParseDate(line.substr(alt+1, neu-alt+1), &end))   // +1 wegen Semikolon
          break;
       Data.date_time = date;
    
       bool succ2;
       for (size_t i=0; i<30; i++)
       {
          succ2 = false;
    
          alt = neu;
          neu = line.find(";", alt+1);
    
          if (neu == wxString::npos) break;
    
          Data.station_id[i] = line.substr(alt+1, neu-alt);
          succ2 = true;
       }
    
       if (!succ2) break;
    
       alt = neu;
       neu = line.find(";", alt+1);
    
       if (neu == wxString::npos) break;
    
       if (!line.substr(alt+1, neu-alt+1).ToLong(&Data.mode)) break;
    
       alt = neu;
       neu = line.find(";", alt+1);
    
       if (neu == wxString::npos) break;
    
       if (!line.substr(alt+1, neu-alt+1).ToDouble(&Data.u_batt)) break;
    
       alt = neu;
       neu = line.find(";", alt+1);
    
       if (neu == wxString::npos) break;
    
       if (!line.substr(alt+1, neu-alt+1).ToDouble(&Data.i_in)) break;
    
       success = true;
       DatenVec.push_back(Data);
    }
    
    if (!success)
    {
       wxMessageBox("Fehlerhafte Datei");
    }
    

    Ich habs nicht getestet und nur schnell "dahingeschmiert" 😃
    wxVector laesst sich wie std::vector verwenden.

    Die einzelnen Elemente koenntest du so durchgehen:

    for (size_t i=0; i<DatenVec.size(); i++)
    {
       // mach was mit DatenVec[i] (Daten)
    }
    


  • Vielen Dank erstmal 🙂 🙂

    Ich versuche gerade den code zu verstehen und umzusetzen. Leider hänge ich schon bei

    wxVector<Daten> DatenVec;
    

    Compiler sagt mir an dieser Stelle:
    error: 'wxVector' was not declared in this scope|

    Obwohl ich die include Datei:

    #include <wx/vector.h>
    

    eingebunden habe. Eine Idee woran das liegen kann?

    Ich habe dann auch versucht statt wxVector den STL vector zu verwenden:

    #include <vector>
    

    und

    vector<Daten> DatenVec;
    

    Dann bekomme ich allerdings den Fehler:

    error: template argument for 'template<class _Alloc> class std::allocator' uses local type 'Station::Station(wxString, wxString, interface_types_t, int)::Daten'
    E:\projekte\greenController\trunk\tools\kommTool\station.cpp|126|error: trying to instantiate 'template<class _Alloc> class std::allocator'|
    E:\projekte\greenController\trunk\tools\kommTool\station.cpp|126|error: template argument 2 is invalid|
    😡

    Wenn ich statt der Struktur "Daten" ein int benutze funktioniert es, aber das bringt mir nicht viel.... Funktioniert das mit dem Vektoren für Strukturen nicht???

    Gruß
    Falko

    Gruß



  • spacevoyager schrieb:

    wxVector<Daten> DatenVec;
    

    Compiler sagt mir an dieser Stelle:
    error: 'wxVector' was not declared in this scope|

    Obwohl ich die include Datei:

    #include <wx/vector.h>
    

    eingebunden habe. Eine Idee woran das liegen kann?

    Seltsam. Sicher das du die Header-Datei eingebunden hast bevor du wxVector verwendet hast?
    Ansonsten, wirf mal einen Blick in die Header-Datei. Da muesste wxVector definiert sein...

    spacevoyager schrieb:

    Ich habe dann auch versucht statt wxVector den STL vector zu verwenden:

    #include <vector>
    

    und

    vector<Daten> DatenVec;
    

    Dann bekomme ich allerdings den Fehler:

    error: template argument for 'template<class _Alloc> class std::allocator' uses local type 'Station::Station(wxString, wxString, interface_types_t, int)::Daten'
    E:\projekte\greenController\trunk\tools\kommTool\station.cpp|126|error: trying to instantiate 'template<class _Alloc> class std::allocator'|
    E:\projekte\greenController\trunk\tools\kommTool\station.cpp|126|error: template argument 2 is invalid|
    😡

    Wenn ich statt der Struktur "Daten" ein int benutze funktioniert es, aber das bringt mir nicht viel.... Funktioniert das mit dem Vektoren für Strukturen nicht???

    Doch. Aber du musst die Struktur im globalen Scope definieren. MinGW mag wohl keine lokal definierten Strukturen...

    Einpaar Erklaerungen zum Code:

    • wxDateTime::ParseDate liest ein Datum ein und teilt es den auf. Der const_iterator zeigt dann an die Stelle wo das Parsen aufgehoert hat (eigentlich koennte man hier noch pruefen ob der iterator auf end() zeigt...)

    • wxString::find sucht nach einem String. Optional kann man im zweiten Parameter angeben, wo die Suche gestartet werden soll. Wenn nichts gefunden wurde wird wxString::npos zurueckgegeben.

    • wxString::substr teil ein String auf. Der erste Parameter ist die Startposition und der zweite die Laenge (optional).

    • wxString::ToLong konvertiert einen String in einen long. Als Parameter wird ein Zeiger zur long-Variable benoetigt. Gibt true zurueck, wenn die Konvertierung erfolgreich war.

    - Im obigen Beispiel habe ich die Funktion wxString::ToDouble verwendet, die aehnlich funktioniert wie ToLong aber fuer einen double. Das ist aber falsch, weil ToDouble eine lokale Repraesentation der Zahl erwartet (Komma statt Punkt). Stattdessen koennen wir aber wxString::ToCDouble verwenden, die einen Punkt als Komma akzeptiert.

    Der Rest des Codes duerfte ziemlich selbsterklaerend sein.

    Ich hab noch einige Fehler in meinem Code entdeckt und hab sie ausgebessert.
    Das hier klappt (getestet):

    int alt;
    int neu;
    bool success;
    
    wxVector<Daten> DatenVec;
    
    while (file.Eof() == false)
    {
    	success = false;
    	Daten Data;
    
    	line = file.GetNextLine()
    	neu = line.find(";");
    
    	if (neu == wxString::npos)
    		break;
    
    	wxDateTime date;
    	wxString::const_iterator end;
    	if (!date.ParseDate(line.substr(0, neu), &end))
    		break;
    	Data.date_time = date;
    
    	bool succ2;
    	for (size_t i=0; i<30; i++)
    	{
    		succ2 = false;
    
    		alt = neu;
    		neu = line.find(";", alt+1);
    
    		if (neu == wxString::npos) break;
    
    		Data.station_id[i] = line.substr(alt+1, neu-alt-1);
    		succ2 = true;
    	}
    
    	if (!succ2) break;
    
    	alt = neu;
    	neu = line.find(";", alt+1);
    
    	if (neu == wxString::npos) break;
    
    	if (!line.substr(alt+1, neu-alt-1).ToLong(&Data.mode)) break;
    
    	alt = neu;
    	neu = line.find(";", alt+1);
    
    	if (neu == wxString::npos) break;
    
    	wxString test = line.substr(alt+1, neu-alt-1);
    	if (!line.substr(alt+1, neu-alt-1).ToCDouble(&Data.u_batt)) break;
    
    	alt = neu;
    	neu = line.find(";", alt+1);
    
    	if (neu == wxString::npos) break;
    
    	if (!line.substr(alt+1, neu-alt-1).ToCDouble(&Data.i_in)) break;
    
    	success = true;
    	DatenVec.push_back(Data);
    }
    
    if (!success)
    {
    	wxMessageBox("Fehlerhafte Datei");
    }
    


  • bin jetzt erst dazu gekommen weiter zu machen... es hat soweit alle geklappt, ein paar Sachen mußte ich noch umschreiben z.B. das mit dem wxVector hat nicht funktioniert, mit Vektor hats geklappt..ToCDouble gab es beiu mir nicht... vielleicht hab ich ne ältere Version von wxWidgets..aber auf jedefall klapps jetzt alles
    Vielen Dank!



  • wxStringTokenizer zum Splitten einer Zeile

    bei Qt gehts noch einfacher 😮
    QString zeile = "abc;defg;hijkl;mno";
    QStringList list = zeile.split( ";" );


Anmelden zum Antworten