Einlesen einer Textdatei in eine Matrix



  • Hallo zusammen,

    ich stehe momentan vor einem Problem. Ich soll ein Programm schreiben.... komme aber einfach nicht mehr weiter.
    Vorliegen habe ich eine Textdatei, wie nachfolgend zu sehen ist. Dabei handelt es sich um Winddaten:

    2006-01-11 00:00:00; -179,5; -83,0; -3,04; 3,73;
    2006-01-11 00:00:00; -179,5; -82,5; -2,74; 3,57;
    2006-01-11 00:00:00; -179,5; -82,0; -2,40; 3,60;
    2006-01-11 00:00:00; -179,5; -81,5; -1,86; 3,50;
    2006-01-11 00:00:00; -179,5; -81,0; -0,93; 2,74;
    2006-01-11 00:00:00; -179,5; -80,5; -0,12; 2,24;
    2006-01-11 00:00:00; -179,5; -80,0; 0,44; 2,00;
    2006-01-11 00:00:00; -179,5; -79,5; 0,72; 2,14;
    2006-01-11 00:00:00; -179,5; -79,0; 0,85; 2,08;
    2006-01-11 00:00:00; -179,5; -78,5; 1,24; 1,60;

    Insgesamt beinhaltet die Textdatei fünf Spalten, die jeweils durch ein Komma getrennt wurden. Diese möchte ich in eine Matrix einlesen. Die Werte der letzten beiden Spalten stellen die beiden Komponenten eines Windvektors dar. Aus diesen beiden Werten muss ich jeweils den Windvektor in Länge und Richtung bestimmten. Dazu sind die beiden Funktion "windspeed" und "winddirection" da. Die Ergebnisse der beiden Funktionen müssen dann ebenfalls in der Matrix gespeichert werden - in einer 6. und 7. Spalte.
    Zum Schluss möchte ich auf die Matix zugreifen können. Einzelne Werte oder auch die gesamte Matrix sollen ausgegeben werden.
    Nachfolgend findet ihr die aktuelle Version meines Programms. Ausgabe der Matrix funktioniert bei mir nicht. Kann mir jemand von euch helfen? Wo liegt mein Fehler? Vielen Dank schon im voraus.

    Liebe Grüße
    Nicole

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <cmath>
    #include <vector>
    
    using namespace std;
    
    const float pi = 3.1415;
    
    //Function windspeed
    float windspeed (float u, float v) {
        float z;
        z = sqrt(u*u + v*v);
        return z;
    }
    
    //Function winddirection
    float direction (float u, float v) {
        float z;
        z = atan(v/u) * (180/pi);
        return z;
    }
    
    int main() {
        vector <string> V, W;                               //defining a vector
        string line;                                        //defining a string
        int i = 0;                                          //startposition of V
        size_t pos = 0;                                     //declare the position of "Semicolon"
        const int column = 5;                               //number of columns of the data
    
        ifstream read;                                      //class ifstream
        read.open("Testfile_Winddata.txt");                              //open data before use
        if(!read.is_open())                                 //check if data can be open
            cerr << "Failed to open data!" << endl;         //Data can not be open
        getline(read, line);                                
    
        while (getline(read,line)) {                        //read line by line of the data
            V.push_back(line);                              //vector V gets a textline
    
            while (!V[i].empty()) {                
                pos = V[i].find(";");                       //find the position of the first "semicolon"   
                if (pos == string::npos) {                  //if no "semicolon" can be found
                    cout << "Error!" << endl;
                    W.push_back(V[i]);
                    break;
                }
    
                W.push_back(V[i].substr(0,pos));            /*the line of V[i] is cut from the beginning 
                                                            to the first "Semicolon" and inserted in W*/
                V[i]=V[i].substr(pos+1);                    //string up to the first "Semicolon" is deleted
            }
            i++;                                            //get the next vector-entry
        }
    
        int j = 0;                                          //define j as the number of lines in the textdata
    
        int e = W.size()/column;
        float (*matrix) [column+2] = new float [e][column+2];    //define a matrix
    
        for (int t = 0; t < W.size(); t = t + column) {
            for (int k = 0; k < column; k++) {
                matrix[j][k] = atof(W[t+k].c_str());        //atof: change ascii to float
            }
    
            matrix[j][column] = windspeed(matrix[j][column-1], matrix[j][column-2]);    //use function windspeed
            matrix[j][column+1] = direction(matrix[j][column-1], matrix[j][column-2]);  //use function direction
            j++;                                                                        //go to the next line
        }
    
        read.close();                                       //close data after use
    
        cout << matrix[2][4];
    
        cin.get();
        return 0;
    }
    

  • Mod

    Sind da wirklich Kommas statt Dezimalpunke? 😮

    Allgemein hast du hier ein paar Fehler in der Herandgehensweise:
    1. Datenstrukturen:
    Hier ist keine Matrix gefragt. Die Daten in jeder Spalte haben jeweils eine völlig unterschiedliche Bedeutung, auch wenn sie auf den ersten Blick alle wie Zahlen aussehen. Es wäre wohl eher ein komplexer Datentyp gefragt:

    class winddata
    {
      double x,y; // Keine Ahnung, was die ersten beiden Spalten sein sollen, ich habe einfach mal geraten
      double vx, vy;
      double angle, speed;
    };
    

    Dann schreibst du eine saubere Lesefunktion für diesen Datentyp und machst einen vector von diesem Datentyp.
    2. Nicht-Benutzung der Standardbibliothek: Du kennst offensichtlich vector und streams, da gibt es keine Entschuldigung für Frickeleien mit new und atof. Sein lassen.
    3. Falsch gerechnet/nicht gekannt: Google: atan2; viele Implementierungen kennen auch pi; Winkel in Grad sind ungewöhnlich, da unpraktisch
    4. Schon in 1. angedeutet: Kapselung! Wie die Datei gelesen wird, steht in einer passenden Lesefunktion, nicht alles in der main. Optimalerweise steht dort nur noch so etwas wie

    vector<winddata> data;
    for (winddata w; stream >> w; ) data.push_back(w);
    

    5. Viele Umwege: Anstatt aus dem Stream in einen string zu lesen und diesen zu zerlegen, lies doch einfach direkt aus dem Stream. Dieser ist dafür gemacht, formatierte Daten (also zum Beispiel auch Zahlen mit Komma als Dezimaltrennstelle) zu lesen. Der kann das viel besser als du.



  • Hallo Nicole,

    Dein offensichtlicher Fehler liegt darin, dass Du die Matrix gar nicht ausgibst sondern nur das eine Element ' matrix[2][4] '. Ab Zeile 73 könnte es heißen:

    for( int ln = 0; ln < e; ++ln )
        {
            for( int sp = 0; sp < column+2; ++sp )
                cout << matrix[ln][sp] << "; ";
            cout << endl;
        }
    

    Du erhältst dann als Ausgabe:

    2006; -179; -82; -2; 3; 3.60555; -33.6911;
    2006; -179; -82; -2; 3; 3.60555; -33.6911;
    2006; -179; -81; -1; 3; 3.16228; -18.4355;
    2006; -179; -81; -0; 2; 2; -0;
    2006; -179; -80; -0; 2; 2; -0;
    2006; -179; -80; 0; 2; 2; 0;
    2006; -179; -79; 0; 2; 2; 0;
    2006; -179; -79; 0; 2; 2; 0;
    2006; -179; -78; 1; 1; 1.41421; 45.0013;
    

    Dir fällt vielleicht auf, dass bei allen Zahlen, die 'gelesen' wurden ( atof ), die Nachkommastellen fehlen. Das liegt daran, dass Du atof und nicht das Einlesen vom Stream nutzt. Versuche mal dieses Codeschnipsel:

    cin.imbue( locale("deu") ); // auf deutsche Locale umstellen
        for( float zahl; cin >> zahl; )
            cout << "--> " << zahl << endl;
    

    dann kann der Dialog z.B. so aussehen:

    -179,5
    --> -179.5
    3,50
    --> 3.5
    0,0002
    --> 0.0002
    

    wie Du siehst werden die Zahlen im deutschen Format (mit Komma als Dezimaltrenner) gelesen und im englischen Format (mit '.') wieder ausgegeben. Der Name der Locale (hier "deu") ist von Deiner Entwicklungsumgebung abhängig und kann anders lauten. "deu" ist korrekt für MS-VS.

    Ansonsten stimme ich allen Aussagen, die SeppJ aufgelistet hat, zu. Ich gehe bei der class winddata sogar noch einen Schritt weiter und würde die Member angle und speed weglassen und statt dessen zwei Methoden anbieten, die das aus dem Windvektor bestimmen.

    Beim bestimmen der Windrichtung solltest Du genau darauf achten, wie die beiden Windkomponenten definiert sind, und die Tatsache bedenken, dass Windrichtung immer die Richtung ist aus der der Wind kommt. Der Windvektor zeigt wahrscheinlich in die Richtung in die der Wind weht!

    Zum Einlesen so einer Struktur schaue Dir mal die kleine Übung in dem Beitrag weiter unten an und zum Überlesen von ';' kann dieser Thread nützlich sein. Falls Du darüber hinaus Fragen hast, so melde Dich noch mal.

    Gruß
    Werner



  • Hallo zusammen,

    ich habe nun etwas an meinem Programm gebastelt. Es sieht nun so aus - also wie ihr sehen könnt etwas abgespeckt:

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <cmath>
    #include <vector>
    #include <iterator>
    
    using namespace std;
    
    const float pi = 3.1415;
    
    //Class Winddata
    class winddata {               
        string date;                //defining a string representing the date
        double lon, lat;            //defining longitude and latitude
        double UGRD, VGRD;          //defining the two components of the windvector 
        double speed, angle;        //defining windspeed and -angle
    };  
    
    //Funktion readfile
    void readfile() {
        vector <string> V;                  //defining a vector 
        string t;                           //defining a text
        ifstream read;
        read.open("Testfile_Winddata.txt"); //open file
        while(getline(read, t,';')) {
        V.push_back(t);
        }
        copy(V.begin(), V.end(), ostream_iterator<string>(cout, " "));      //Output of the vector
    
        read.close();                       //close file
    }
    
    //Function windspeed
    float windspeed (float u, float v) {
        float z;
        z = sqrt(u*u + v*v);
        return z;
    }
    
    //Function winddirection
    float direction (float u, float v) {
        float z;
        z = atan2(v,u) * (180/pi);
        return z;
    }
    
    int main() {
    
        readfile();
    
        system("PAUSE");
        return 0;
    }
    

    Als Ausgabe erhalte ich dann folgendes:

    2006-01-11 00:00:00     -179.5  -83.0   -3.04   3.73
    2006-01-11 00:00:00     -179.5  -82.5   -2.74   3.57
    2006-01-11 00:00:00     -179.5  -82.0   -2.40   3.60
    2006-01-11 00:00:00     -179.5  -81.5   -1.86   3.50
    2006-01-11 00:00:00     -179.5  -81.0   -0.93   2.74
    2006-01-11 00:00:00     -179.5  -80.5   -0.12   2.24
    2006-01-11 00:00:00     -179.5  -80.0   0.44    2.00
    2006-01-11 00:00:00     -179.5  -79.5   0.72    2.14
    2006-01-11 00:00:00     -179.5  -79.0   0.85    2.08
    2006-01-11 00:00:00     -179.5  -78.5   1.24    1.60
    

    Ich habe nun eine Klasse "winddata" angelegt und die Lesefunktion "readfile" angelegt. Nun weiß ich leider nicht, wie man die einzelnen Daten den entsprechenden Klassenelementen zuweist. Pro Zeile müsste dann ja ein Objekt der Klasse "winddata" angelegt werden. Wie lese ich direkt aus dem Stream?

    Vielen Dank und liebe Grüße
    Nicole



  • Hallo Nicole,

    nickey1989 schrieb:

    //Class Winddata
    class winddata {               
        string date;                //defining a string representing the date
        double lon, lat;            //defining longitude and latitude
    // ...
    

    wenn das die geographische Länge und Breite sein soll, dann ist bei Lon=-179,5 und Lat=-83,0 aber ziemlich viel Eis in der Nähe 😉

    nickey1989 schrieb:

    Als Ausgabe erhalte ich dann folgendes:

    2006-01-11 00:00:00     -179.5  -83.0   -3.04   3.73
    2006-01-11 00:00:00     -179.5  -82.5   -2.74   3.57
    ...
    

    das widerspricht sich mit dem Inhalt Deines ersten Beitrags. Du liest Zeichen ein und gibst Zeichen aus, demnach stehen die Zahlen mit '.' als Dezimaltrenner in der Datei, oben steht es mit ',' - Du musst Dich schon entscheiden.

    nickey1989 schrieb:

    Ich habe nun eine Klasse "winddata" angelegt und die Lesefunktion "readfile" angelegt. Nun weiß ich leider nicht, wie man die einzelnen Daten den entsprechenden Klassenelementen zuweist. Pro Zeile müsste dann ja ein Objekt der Klasse "winddata" angelegt werden. Wie lese ich direkt aus dem Stream?

    Hast Du den Beitrag hinter diesem Link gelesen?

    Werner Salomon schrieb:

    Zum Einlesen so einer Struktur schaue Dir mal die kleine Übung in dem Beitrag weiter unten an ....

    dort ist es im Prinzip erklärt.

    Hier Dein Programm etwas aufgemotzt:

    #include <cmath> // std::atan2, std::sqrt
    #include <fstream>
    #include <iostream>
    #include <string>
    #include <vector>
    
    template< char C >
    std::istream& Char( std::istream& in )
    {   // Helferlein, um ein Zeichen 'C' zu überlesen
        char c;
        if( in >> c && c != C )
            in.setstate( std::ios_base::failbit );
        return in;
    }
    
    namespace
    {
        const double PI = std::acos( -1.0 );
        const double RAD2GRAD = 180./PI;
    }
    
    //Class Winddata
    class winddata {
    public:
        double speed() const
        {
            return std::sqrt( UGRD*UGRD + VGRD*VGRD );
        }
        double angle() const
        {
            // Bem.: um hier als Ergebnis die Windrichtung (Kompasskurs) zu bekommen, muss
            //  UGRD die Südkomponente und VGRD die Westkomponente des Windes sein!
            //  - ist das so?
            return std::atan2(VGRD, UGRD) * RAD2GRAD;
        }
        friend std::istream& operator>>( std::istream& in, winddata& d );
    
    private:
        std::string date;           //defining a string representing the date
        double lon, lat;            //defining longitude and latitude
        double UGRD, VGRD;          //defining the two components of the windvector
    };  
    
    // --   Lesefunktion für winddata
    std::istream& operator>>( std::istream& in, winddata& d )
    {
        getline( in >> std::ws, d.date, ';' );
        in >> d.lon >> Char<';'> >> d.lat >> Char<';'>;
        in >> d.UGRD >> Char<';'> >> d.VGRD >> Char<';'>;
        return in;
    }
    
    int main()
    { 
        using namespace std;
        vector< winddata > W;
        ifstream read( "Testfile_Winddata.txt" );
        if( !read.is_open() )
        {
            cerr << "Failed to open data!" << endl;
            return -2;
        }
        read.imbue( locale("deu") ); // <== deutsches Zahlenformat, falls die Zahlen mit ',' als Dezimaltrenner geschrieben werden 
        for( winddata w; read >> w; )
            W.push_back( w );
        cout << W.size() << " Eintraege gelesen" << endl;
        // usw.
        cin.get();
        return 0;
    }
    

    falls Du Fragen hast, bitte frage.

    Gruß
    Werner



  • Hallo Werner,

    ein paar Fragen hätte ich schon noch zu deinem Programm:
    Die Template-Funktion ist dazu dar, ein Zeichen, also den Semikolon zu überlesen. Die Zeile 12 verstehe ich jedoch nicht. Was bewirkt die Funktion "setstate"? Die Google-Suche hat mir nicht wirklich weitergeholfen.

    Die Einteilung der Klasse in public und private verursacht bei mir Probleme. Es kommt die Fehlermeldung: Auf "Member "winddata::date" (deklariert in Zeile 30)" kann nicht zugegriffen werden. Deshalb habe ich das Schlüsselwort "private" gelöscht. Ich habe nun die gesamten Elemente der Klasse als "public" deklariert.

    Ich habe dann ein Objekt winddata_object erzeugt, wie man im Code unten sehen kann. Ich wollte dann einzele Elemente dieses Objektes ausgeben lassen. Aber ich erhalte keine bzw. falsche Werte. Wo liegt mein Fehler?

    winddata winddata_object;
    cout << winddata_object.date << endl;
    cout << winddata_object.lat << endl;
    

    Liebe Grüße
    Nicole

    Und Dankeschön Werner. Du hast mir schon ein großes Stück weitergeholfen 🙂



  • nickey1989 schrieb:

    Die Zeile 12 verstehe ich jedoch nicht.

    Ein Stream (hier ifstream ) hat immer einen Zustand. Einfach gesagt: Entweder ist der Zustand des Streams
    - gut ==> Die nachfolgende Operation, die du auf den Stream anwendest, könnte funktionieren.
    - oder schlecht ==> Alle Operationen, die du auf den Stream anwendest, schlagen fehl.

    Dein Format nutzt ja einen ; als Trennzeichen. Wenn das zu überlesende Zeichen ein ; ist, ist alles gut, das Format stimmt. Ist es aber kein ; dann ist ein Formatfehler aufgetreten. Wenn das passiert, sagen wir einfach, der Stream ist schrott und überführen (setstate) ihn deswegen in den Zustand schlecht (ios_base::fail). Somit verhindern wir weitere Operationen, da offensichtlich etwas nicht stimmt.

    nickey1989 schrieb:

    Die Einteilung der Klasse in public und private verursacht bei mir Probleme. Es kommt die Fehlermeldung: Auf "Member "winddata::date" (deklariert in Zeile 30)" kann nicht zugegriffen werden. Deshalb habe ich das Schlüsselwort "private" gelöscht. Ich habe nun die gesamten Elemente der Klasse als "public" deklariert.

    Kann eigentlich nicht sein (da friend). Hast du den Quellcode von Werner wirklich kopiert?

    nickey1989 schrieb:

    Ich habe dann ein Objekt winddata_object erzeugt, wie man im Code unten sehen kann. Ich wollte dann einzele Elemente dieses Objektes ausgeben lassen. Aber ich erhalte keine bzw. falsche Werte. Wo liegt mein Fehler?

    winddata winddata_object;
    cout << winddata_object.date << endl;
    cout << winddata_object.lat << endl;
    

    Na, du musst winddata_object ja erstmal einlesen. So hat es ja total willkürliche Werte.


Anmelden zum Antworten