Auslesen von komplexen Zahlen aus einer Datei



  • Hacker schrieb:

    Probier mal mit einem else vor dem if in zeile 7 (dein letzter Code) aus.

    Nöp.



  • 314159265358979 schrieb:

    Und wenn auf komplexe eine weitere komplexe folgt, krachts.

    Und wieso? Bzw. wie ist denn dieses Problem zu beheben zu umgehen?

    Wenn ich statt if(...), else hinschreibe ist das Ergebnis leider immer noch falsch.

    vector<complex<double> > readComplexNumbers(const char* filename,const char* tag, const char* attributeName){
        vector<complex<double> > complexNumbers(7770);
        ifstream f(filename);
    
            //solange Zeilen ignorieren, bis der Tag gefunden wurde
        while(!isAttributeTag(f,tag,attributeName)){
            f.ignore( numeric_limits< streamsize >::max(), '\n' );
        }
            //die Zeile des Tags ebenso ignorieren
        f.ignore( numeric_limits< streamsize >::max(), '\n' );
    
            //nun Werte lesen
        double real, imag;
        char plus, j, vorzeichen; 
        int a = 0;
        int b = 0;
         for(int p=0; p<7770; p++){   
               if(f>>real>>plus>>j>>imag){
                     if(plus == '-'){
                         imag *= -1;
                     }
                     a++;
                 }
                 else{
                     imag=0;
                     b++;
                 }
     //            cout<<complex<double>(real,imag);
                    complexNumbers[p]=complex<double>(real,imag);
         } 
        cout<<endl<<a<<endl<<b<<endl;
        return complexNumbers; 
    }
    


  • NAIN,

    else if(f>>real){
                     imag=0;
                     b++;
                 }
    

    mit der Bedingung von vorher.



  • Denk doch mal nach. Was ist das nächste, wenn eine reele Zahl eingelesen wurde? Entweder ein Imaginärteil, oder eine weitere Zahl. (reel oder komplex) Du musst irgendwie erkennen, was gerade gelesen wird.
    Hackers Vorschlag ist, wenn mich nicht gerade alles täuscht, falsch.



  • Hacker schrieb:

    NAIN,

    else if(f>>real){
                     imag=0;
                     b++;
                 }
    

    mit der Bedingung von vorher.

    Funktioniert leider alles nicht. Wie gesagt ich habe bereits alles versucht.

    Die Einleseroutine muss grundsätzlich falsch sein und ich weiß nicht warum?!

    Denk doch mal nach. Was ist das nächste, wenn eine reele Zahl eingelesen wurde? Entweder ein Imaginärteil, oder eine weitere Zahl. (reel oder komplex) Du musst irgendwie erkennen, was gerade gelesen wird.
    Hackers Vorschlag ist, wenn mich nicht gerade alles täuscht, falsch.

    Wenn eine reele Zahl eingelesen wurde, dann springt er doch in die nächste Zeile und schaut ist diese komplex oder reel usw. usw.

    Es ist ja immer eine Zahl pro Zeile vorhanden (also entweder komplex oder reel).



  • Nein, er schaut eben nicht, ob. Er liest solange ein, bis es fehlschlägt. Das ist dann der Zeitpunkt, an dem eine zweite komplexe Zahl kommt. Dann machts kabumm.



  • 314159265358979 schrieb:

    Nein, er schaut eben nicht, ob. Er liest solange ein, bis es fehlschlägt. Das ist dann der Zeitpunkt, an dem eine zweite komplexe Zahl kommt. Dann machts kabumm.

    Und wie krieg ich es hin, dass es funktioniert?!



  • Gleich mal vorweg: Eine Komplettlösung bekommst du von mir nicht. Aber ich kann dir Hilfestellung geben. Ich habe das Gefühl, du hast die C++ Streams noch nicht so ganz verstanden, insbesondere, was if(stream >> xy) tut, daher hier mal ein paar Erklärungen.

    Ein Stream hat, wie von Hacker schon erwähnt, 3 Zustände. Beim parsen ist aber letztendlich egal, ob der Fehler aufgrund eines Hardwareproblems oder des Dateiendes ist, daher werden wir uns nur um die Zustände "good" und "bad" kümmern.

    Ein if, wie dieses hier...

    double x;
    if(is >> x)
    

    ...bedeutet: Lies vom Stream einen Wert vom Typ double nach x ein, danach überprüfe auf Fehler. Wenn alles gut gegangen ist, wird der Block nach dem if ausgeführt (der Stream befindet sich im good-Zustand) Bei einem Lesefehler bleibt der Stream, was die Daten betrifft, unverändert, lediglich das failbit wird gesetzt. In einem else-Zweig könntest du eine Exception werfen, oder andere Arten von Fehlerbehandlung durchführen.

    Weiters: Du kannst Zeichen nur ein einziges mal aus einem Stream lesen. Was du rausnimmst, bekommst du kein zweites mal heraus. Zurücklegen geht im Normalfall auch nicht. Deshalb funktioniert dein Ding mit 2 if nicht, ebensowenig das von Hacker mit else if.

    Auch zu empfehlen ist die Referenz: http://www.cplusplus.com/reference/iostream/istream/
    Interessante Funktionen könnten für dich get und getline sein. Vielleicht auch noch putback, es gibt viele verschiedene Herangehensweisen.

    Und hier noch die Erklärung, was bei deinem Code passiert. (Ich habe alles etwas verkürzt, um die relevanten Teile hervorzuheben.)

    double real, imag;
    char plus, j;
    
    // Wir nehmen an, im Stream befindet sich "-1.0+j2.0\n3.0-j4.0"
    for(int p = 0; p < 7770; ++p)
    {  
        // hui, hier passiert viel. realteil weg: "+j2.0\n3.0-j4.0"
        // plus weg: "j2.0\n3.0-j4.0"
        // j weg: "2.0\n3.0-j4.0"
        // und schließlich der Imaginärteil: "\n3.0-j4.0"
        // bis hier stimmts
        if(f >> real >> plus >> j >> imag)
        {
            ...
        } // ein else-zweig würde nur ausgeführt werden, wenn beim einlesen ein fehler auftreten würde
    
        // im stream steht: "\n3.0-j4.0"
        // realteil weg (\n wird einfach überlesen): "-j4.0"
        // "-j4.0" bleibt übrig und kann beim nächsten schleifendurchlauf nicht geparst werden. mist.
        if(f >> real)
        {
            ...
        }
    }
    

    Ich hoffe ich habe damit alle Klarheiten beseitigt. Wenn was klar sein wollte, einfach nachfragen 😉



  • Ersteinmal vielen Dank für die ausführliche Antwort. Ich denke ich habe es jetzt tatsächlich ein wenig besser verstanden. Eigentlich müsste der Quellcode so meiner Ansicht nach funktionieren, aber das tut er nicht.

    Besonders verblüfft mich, dass er nur 389 Mal in die if-Bedingung springt (also a=389) und b=7381. Jedoch müsste es eigentlich genau umgekehrt sein!

    Es sind zu 95% komplexe Zahlen, die restlichen 5% sind reele Zahlen, von daher stimmt wieder irgendetwas nicht...

    Mein Quellcode sieht nun so aus:

    double real, imag;
        char plus, j; 
        int a = 0;
        int b = 0;
         for(int p=0; p<7770; p++){
    // f>>real findet jedes Mal statt, ganz egal ob die kommende Zahl reel oder komplex ist, falls sie komplex ist, dann wird im "if" weiter gemacht, ansonsten springt er zu else
               if(f>>real>>plus>>j>>imag){
                     if(plus == '-'){
                         imag *= -1;
                     }
                     a++;
                 }
                 else{
                     imag=0;
                     b++;
                 }
                    complexNumbers[p]=complex<double>(real,imag);
         }
    


  • Finde es ja lustig, dass ich den Code hier wider sehe. Ich dachte, da sProgrmam würde jetzt im Müll liegen und niemand würde es mehr brauchen 🙄

    und lass dir nichts von Hacker einreden. du brauchst kein reserve. push_back war toll. Ich weiß schon, was ich tue 🙄

    um dir bei deinem Problem zu helfen:

    Was glaubst du, was passiert, wenn deine Eingabe so aussieht:

    1.345
    1.678+j901
    

    und du

    f>>real>>plus>>j>>imag;
    

    anwendest?
    naja:
    real=1.345
    plus=1
    j=.
    imag=678

    und ab dann ließt du nur noch Müll ein.

    Tipp: ließ zuerst real ein und dann schau nach, ob im übernächstne zeichen ein j steht. du kannst mit peek() schauen, wie das nächste zeichen aussieht ohne es zu lesen.



  • Und wie liest man genau den übernächsten Wert ein?

    Mit peek() lese ich ja immer den nächsten Wert, aber wie komme ich denn zum übernachsten. Konnte auch in der Referenz nichts dazu finden.



  • ...

    Und parallel dazu, schau dir mal get() an.



  • Hacker schrieb:

    ...

    Verstehe nicht wirklich was Du mir damit sagen willst?!

    Du hast mir die Referenz zu "seekg" gesendet. Damit gibt man doch die Postion für den Dateizeiger an?! Nur in wie weit hilft mir das weiter? Ich weiß ja nicht in welcher Zeile ..... ich gerade bin.

    Ich will doch nur auf das übernächste Zeichen zugreifen ohne das nächste und das übernächste Zeichen auszulesen.



  • Ja, du setzt also den get-Zeiger zwei(oder einen ? k.p. im moment) nach dem aktuellen stand, liest das Zeichen ein (oder peekst es) und gehst zurück. Das übernächste Zeichen so zu greifen, wie mit peek, funktioniert nicht ohne weiteres.

    Edit: hättest du dir mal wenigstens etwas angesehen, hättest du ios::cur gefunden. Komm schon.

    P.S.: In deinem Fall, nimm einfach ignore().



  • Hacker schrieb:

    Ja, du setzt also den get-Zeiger zwei(oder einen ? k.p. im moment) nach dem aktuellen stand, liest das Zeichen ein (oder peekst es) und gehst zurück. Das übernächste Zeichen so zu greifen, wie mit peek, funktioniert nicht ohne weiteres.

    Edit: hättest du dir mal wenigstens etwas angesehen, hättest du ios::cur gefunden. Komm schon.

    P.S.: In deinem Fall, nimm einfach ignore().

    Danke für die schnell Antwort. Jetzt hab ich es auch gesehen...Hab meinen Quellcode jetzt entsprechend angepasst. Nur leider bekomme ich jetzt den Fehler:

    ISO C++ forbids comparison between pointer and integer
    

    Und das an folgenden Stellen:
    if(var == "j")
    if(plus == "-")

    double real, imag;
        char plus, j, var; 
        int a = 0;
        int b = 0;
        for(int p=0; p<7770; p++){
                    f>>real;
                    f.seekg(ios::cur);
                    var = f.peek();
                    if(var == "j"){
                      f.seekg((ios::cur)-1);
                      f>>plus>>j>>imag;
                      if(plus == "-"){
                              imag *= -1;
                      }   
                    }
                    else{
                       f.seekg((ios::cur)-1);
                       imag = 0;  
                    }
                   complexNumbers[p]=complex<double>(real,imag);
        }
        return complexNumbers;
    


  • char-Literale schreibt man in normale Quotes. ''



  • 314159265358979 schrieb:

    char-Literale schreibt man in normale Quotes. ''

    Danke für den Tipp, jetzt funktioniert es ohne Fehlermeldungen.

    Aber es muss immer noch ein Fehler in der Einleseroutine stecken, da nun 7770 Mal die gleiche Zahl gelesen wird.

    Hmm...



  • Du weißt, dass

    f.seekg(ios::cur);
    

    Den get-Zeiger auf die aktuelle Position setzt, also rein garnix bewirkt ?
    Die Variable var ist übrigens nur ein unnötiger Platzhalter.

    Du kannst gleich

    if(f.peek() == 'j'){ //...
    

    schreiben.

    Und statt

    f.seekg((ios::cur)-1); // oder  +1
    
    f.unget(); // oder entsprechend get()
    

    Du kannst natürlich auch ignore() einsetzen, mit dem man auch mehrere Zeichen überspringen kann.



  • Danke für die Tipps, aber es ändert leider nichts an der Situation. Es wird immer die gleiche Zeile, Zahl eingelesen.

    Vllt. hab ich ja auch nur ein Verständnisproblem damit.

    Wenn f.seekg(ios::cur) jetzt auf die Stelle 0 zeigt, dann müsste es doch nach f>>xyz auf eine Stelle X zeigen oder etwa nicht?

    f.seekg(ios::cur) zeigt jedoch immer auf die gleiche Stelle und ich verstehe nicht warum...



  • Javun schrieb:

    Wenn f.seekg(ios::cur) jetzt auf die Stelle 0 zeigt, dann müsste es doch nach f>>xyz auf eine Stelle X zeigen oder etwa nicht?

    f.seekg(ios::cur) zeigt jedoch immer auf die gleiche Stelle und ich verstehe nicht warum...

    Ahh... kennst du denn keinen get-Zeiger ?
    Der zeigt immer darauf wo du grad bist mit lesen.
    tellg() zeigt dir an, wo er ist, mit seekg() kannst du ihn setzten.

    ios::cur ist einfach die aktuelle Position des get-Zeigers.
    Wenn du

    DeinIfstream.seekg(ios::cur + 1);
    

    schreibst, heißt das, du inkrementierst den get-Zeiger der, bzw. deiner Datei.
    Stattdessen könntest du auch

    DeinIfstream.ignore();
    

    Schreiben. Damit liest und ignorierst du 1 Zeichen (wenns mehr sein sollen, guck in der Referenz).
    Die Funktion get() inkrementiert den get-Zeiger, und gibt dir den gelesenen Wert unformatiert zurück.
    peek() macht dasselbe, nur das er den Zeiger nicht inkrementiert.
    unget() liest ein Zeichen rückwärts, bzw. der get-Zeiger wird dekrementiert, und der eingelesene Wert wird unformatiert zurückgegeben.

    Der Shift-Operator nun, liest den gegebenen Wert ein, und inkrementiert den gZeiger jeweils um die Länge des Eingegebenen + 1.

    Bspw: "5050aergaerg!" steht in der Datei.

    Liest du einen Integer a mit

    DeinFileStream>>a;
    

    ein, ist tellg() (die Position des get-Zeigers) 5 (und a 5050).

    Hättest du alles in der Referenz nachlesen können. 😉


Anmelden zum Antworten