Auslesen von komplexen Zahlen aus einer Datei



  • Hey,

    also hör mal auf SeppJ,

    ansonsten solange:

    A) Du kennst anfang und ende deiner Zahlenspalte
    --- Dann kopier die doch in einen seperaten string

    😎 Du weißt das deine Spalte vieeeeele Zeilen hat die alle mit \n Enden?
    --- Dann nutze Doch folgendes:

    std::string strTextWoAlleZahlenLiegen;
     std::vector<std::string> vecVieeeeeleZahlen;
      //...
     unsigned int zaehler=0;
     if(std::string::npos==zaehler=strTextWoAlleZahlenLiegen.find('\n'))
     { 
        std::cout<<"ERROR";
       }
    else
      vecVieeeeeleZahlen.push_back(strTextWoAlleZahlenLiegen.substr(zaehler));
    

    nun haste dann einen vector voller string's die jeweils eine Zahl beinhalten.

    Was musste nun tun?
    Die Funktion

    std::string::find
    

    kennst Du ja mitlerweile, suche also nach einem j
    (mit der annahme das das Format kontinuierlich gleich ist)
    und schon weisst Du welche Zeile eine Imaginäre und welche Zeile nur einen Realteil enthält.

    Nun Kannst Du mit dieser Fallunterscheidung die jeweilige Zeile entsprechend Parsen...

    sei gegrüßt



  • SeppJ hat völlig recht. Das kann man einfach nicht so weiter laufen lassen. Javun hat ja auch schon fest estellt, dass Ihr Euch hier im Kreis dreht.

    Hallo Javun,
    ich werde versuchen es Dir zu erklären. Schritt für Schritt und in mehreren Lektionen. Erstmal was grundsätzliches - versuche Dein Problem immer(!) in mehrere Teilprobleme zu zerlegen. Wenn Du also eine komplizierte Datei einlesen willst, so versuche zunächst die einzelnen Elemente - z.B. hier eine komplexe Zahl - für sich zu lesen. Lesen aus einer Datei heißt lesen aus einem std::ifstream und da dies ein std::istream ist, ist das Lesen wie von std::cin. Das macht keinen Unterschied für da Einlesen an sich.
    Auch das kann man noch in kleinere Teile zerlegen. In Deinem Fall in vier: den Realteil, den Operator+/-, das 'j' und den Imaginärteil.

    Ich fange ganz einfach an, mit Dingen, die Du schon kennen solltest, wie ich hier gesehen habe:

    // --  Lektion 1
    #include <iostream>
    #include <complex>
    
    int main()
    {
        using namespace std;
        cout << "eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 " << endl;
        double real, imag;
        char plus, imagChar;
        while( cin >> real >> plus >> imagChar >> imag )
            cout << "gelesen: " << complex< double >( real, plus == '+'? imag: -imag ) << endl;
        return 0;
    }
    

    einfach die vier genannten Dinge als double bzw. char einlesen und eine komplexe Zahl daraus machen. Ein Test ergibt:

    eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017
    -9.351E-017-j1.539e-017
    gelesen: (-9.351e-017,-1.539e-017)
    0+j0
    gelesen: (0,0)
    0**2
    gelesen: (0,-2)
    

    natürlich ist dem Programm das Zeichen vor dem Realteil egal und beim Operator interessiert nur ob da ein '+' kommt oder was anderes. Das ist noch suboptimal. Aber bevor ich darauf eingehe, kapsele ich diese Funktionalität und zwar mit dem Ziel, so etwas hinschreiben zu können:

    complex< double > z;
        while( cin >> /*mal sehn*/( z ) )
            cout << "gelesen: " << z << endl;
    

    weil später, da wo das benutzt wird, will ich mich nicht mehr mit dem detailliertem Format herumschlagen, sondern einfach nur eine komplexe Zahle einlesen, so wie ich ein double lese.

    (gleich geht's weiter)



  • Vielen Dank für die ausführliche Erklärung 🙂

    Soweit hatte ich das ja bereits, danach ist mir aufgefallen, dass sowohl reele als auch komplexe Zahlen vorkommen können. Nun geht es um diese Fallunterscheidung...

    Ich hab es probiert, aber anscheind war ich da auf dem falschen Weg. Die Möglichkeit erstmal alle Zeilen als string auszulesen und dann zu verarbeiten ist mir bewusst und so sah es auch in der 1. Version des Programmes aus. Nur wurde mir dann von so vielen Usern geraten das auslesen und verarbeiten direkt zu machen, dass ich das nun so umgesetzt habe...

    Problem ist nur das ich an dieser Stelle jetzt nicht weiterkomme...



  • Hallo Javun,

    wie bereits angekündigt, suche ich eine Möglichkeit das Einlesen einer komplexen Zahl ähnlich aufzurufen, wie das z.B. das Lesen eines double. Der Trick besteht nun darin, dass man sich eine kleine Hilfsstruktur baut, die in in Ihrem Konstruktor eine komplexe Zahl aufnimmt - genauer deren Referenz - und das Einlesen implementiert.

    Angenommen da ist eine struct ComplexReader und ich möchte ein Objekt von ComplexReader einlesbar machen, so schreibe ich für ComplexReader einen sogenannten Streaming-Operator.

    struct ComplexReader {
        friend std::istream& operator>>( std::istream in, ComplexReader& cr ) {
            // .. hier kann ich lesen was ich will
            return in;
        }
    };
    

    Dann bin ich in der Lage so was hinzu schreiben:

    ComplexReader cr1;
        if( cin >> cr1 ) {
            // cr1 wurde ohne Fehler gelesen
    

    Jetzt kann man so einem Objekt vor dem lesen natürlich noch eine Referenz mitgeben. Das ganze nochmal

    struct ComplexReader {
        ComplexReader( std::complex<double>& z )
            : m_target( z )
        {}
        friend std::istream& operator>>( std::istream in, ComplexReader& cr ) {
            // .. hier kann ich lesen und das Ergebnis nach cr.m_target schreiben
            return in;
        }
    private:
        std::complex<double>& m_target;
    };
    

    Dann kann man schreiben:

    complex<double> z;
        ComplexReader cr1( z );
        if( cin >> cr1 ) {
            // cr1 wurde ohne Fehler gelesen; d.h. 'z' enthält jetzt den gelesen Wert
    

    Das schreibt sich natürlich besser und übersichtlicher in einer Zeile:

    complex<double> z;
        if( cin >> ComplexReader( z ); ) {
            // 'z' enthält jetzt den gelesen Wert
    

    und - Oh Schreck das übersetzt nicht. Zumindest nicht mit einem Standard konformen Compiler. Ich will gar nicht erklären warum - das führt uns vom Thema weg.
    Die Lösung besteht darin, den Parameter cr im Streaming-Operator const zu machen oder cr per Value zu übergeben. Da das Objekt cr beim Einlesen gar nicht verändert wird, ist dies ohne weiteres möglich.

    Gleichzeitig mache ich aus ComplexReader noch ein Template, da std::complex ja auch ein Template ist. Lass Dich davon nicht abschrecken, denke einfach, das T wäre ein double oder float, dann bleibt alles wie gewohnt.

    // --   Lektion 2
    #include <iostream>
    #include <complex>
    
    template< typename T >
    struct ComplexReader
    {
        typedef T value_type;
        ComplexReader( std::complex< value_type >& target )
            : m_target( target )
        {}
        friend std::istream& operator>>( std::istream& in, ComplexReader cr ) // Bem.: Übergabe Parameter 'cr' per Value
        {
            double real, imag;
            char plus, imagChar;
            if( in >> real >> plus >> imagChar >> imag )
                cr.m_target = std::complex< double >( real, plus == '+'? imag: -imag );
            return in;
        }
    private:
        std::complex< value_type >& m_target;
    };
    
    int main()
    {
        using namespace std;
        cout << "eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 " << endl;
        complex< double > z;
        while( cin >> ComplexReader< double >( z ) )
            cout << "gelesen: " << z << endl;
        return 0;
    }
    

    an der Funktionalität hat sich gegenüber Lektion 1 nichts geändert, aber das Einlesen der komplexen Zahl geschieht nur noch in dem Streaming-Operator von ComplexReader. Und das ist die Baustelle für die nächsten Lektionen.

    Bevor es weiter geht, beseitige ich noch eine Unschönheit: ComplexReader< double >( z ) ist ein redundanter Ausdruck. Es steht double davor und z ist (und muss) ein complex<double> (sein). Das kann der Compliler für uns erledigen; mit einer sogenannten Factory-Funktion.

    template< typename T >
    ComplexReader< T > read_complex( std::complex< T >& z )
    {
        return ComplexReader< T >( z );
    }
    

    und das Einlesen wird zu:

    while( cin >> read_complex( z ) )
    

    .. und in der nächsten Lektion geht es wirklich um das Lesen. 😉



  • Javun schrieb:

    Soweit hatte ich das ja bereits, danach ist mir aufgefallen, dass sowohl reele als auch komplexe Zahlen vorkommen können. Nun geht es um diese Fallunterscheidung...

    Geduld 😉



  • Lies immer bis zum nächsten Plus alles in einen String und nutze dann eine weitere Funktion/Methode für die Fallunterscheidung.

    Die könnte dann z.B. so aussehen(Pseudocode):

    MyFunction(string complex_number)
    {
    REAL a = 0 ,b = 0;
    std::string::iterator it = complex_number.begin();
    IT LAUFEN LASSEN BIS j auftritt(am ende hört er automatisch auf)
    Substring auslesen und umwandeln
    if(j aufgetreten)
    read zahlen und wandele sie um

    a = wert des ersten substrings
    b = wert des zweiten. Wenn kein real oder imaginärteil vorhanden ist, haben a und b ja schon die gewünschten werte.



  • Hallo Javun,

    die letzte Lektion hat uns beim Lesen nicht weiter gebracht. Aber es war notwendig, um das Problem wirklich zu isolieren. Manchmal ist das sogar wichtiger als eine guter Algorithmus.
    Im diesem Schritt widme ich mich nur dem Operator. Die Lösung ist für die Praxis vielleicht übertrieben, aber ich möchte Dir zeigen, dass man wirklich jedes Problem für sich lösen kann und dann die einzelnen Lösungen gar nicht mehr so schwer zu finden sind. Frei nach dem Motto: "Teile und herrsche"

    Da hier eine simpler char nicht mehr ausreicht, baue ich wieder eine Struktur - hier PlusMinus . Da diese nur innerhalb von ComplexReader benötigt wird, mache ich sie lokal.

    template< typename T >
    struct ComplexReader
    {
        struct PlusMinus
        {
            friend std::istream& operator>>( std::istream& in, PlusMinus& pm )
            {
                return in >> pm.m_op;
            }
            bool positive() const { return m_op == '+'; }
        private:
            char m_op;
        };
    

    und das Einlesen ändere ich zu

    PlusMinus plus;
            char imagChar;
            if( in >> real >> plus >> imagChar >> imag )
                cr.m_target = std::complex< double >( real, plus.positive()? imag: -imag );
    

    wieder ist die Funktionalität gleich geblieben, aber ich habe das Einlesen des Operators isoliert. Als Operatoren sind nur die Zeichen '+' und '-' erlaubt. Und genau das kann ich hier jetzt überprüfen. Falls das nicht passt, so setze ich das Fehlerflag im std::istream.

    Alles zusammen sieht dann so aus:

    // --   Lektion 3
    #include <iostream>
    #include <complex>
    
    template< typename T >
    struct ComplexReader
    {
        struct PlusMinus
        {
            friend std::istream& operator>>( std::istream& in, PlusMinus& pm )
            {
                if( in >> pm.m_op )
                {
                    if( pm.m_op != '+' && pm.m_op != '-' )
                        in.setstate( std::ios_base::failbit );
                }
                return in;
            }
            bool positive() const { return m_op == '+'; }
        private:
            char m_op;
        };
    
        typedef T value_type;
        ComplexReader( std::complex< value_type >& target )
            : m_target( target )
        {}
        friend std::istream& operator>>( std::istream& in, ComplexReader cr ) // Bem.: Übergabe Parameter 'cr' per Value
        {
            double real, imag;
            PlusMinus plus;
            char imagChar;
            if( in >> real >> plus >> imagChar >> imag )
                cr.m_target = std::complex< double >( real, plus.positive()? imag: -imag );
            return in;
        }
    private:
        std::complex< value_type >& m_target;
    };
    template< typename T >
    ComplexReader< T > read_complex( std::complex< T >& z )
    {
        return ComplexReader< T >( z );
    }
    
    int main()
    {
        using namespace std;
        cout << "eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 " << endl;
        complex< double > z;
        while( cin >> read_complex( z ) )
            cout << "gelesen: " << z << endl;
        return 0;
    }
    

    und ein kleiner Test zeigt

    eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017
    -9.3124E-002+j1.5E+017
    gelesen: (-0.093124,1.5e+017)
    9-X5  // das j ist ihm immer noch egal
    gelesen: (9,-5)
    9?j5
    -> Lesefehler kein + und kein -
    

    Du meinst vielleicht, dass man das auch direkt im Streaming-Operator von ComplexReader machen kann, aber der Unterschied ist, dass PlusMinus den Stream auf Fehler setzt, wenn das Format nicht passt, und anschließend nichts mehr gelesen wird. Das hat bei der Analyse des Fehlers und für ein mögliches wieder aufsetzen große Vorteile.

    In der nächsten Lektion kommt das 'j dran.



  • Warum müsst ihr das denn alles kompliziert machen?! Ich bin Dir wirklich dankbar, dass Du Dir die Mühe machst, aber da ist einfach so viel neues bei, da sitz ich ja noch Wochen dran, wenn ich mir jede Funktion und sowas wie struct, friend usw. beibringen muss.

    Ich wollte doch einfach nur Wissen, wo MEIN Fehler liegt, d.h. MEINEN Quellcode korrigieren. Aber seitdem ich hier im Forum bin hab ich meinen Quellcode schon 5 Mal komplett neu geschrieben und ganz ehrlich besser ist er dadurch nicht geworden. Die Fehler sind fast die gleichen, nur mit dem unterschied das ich jetzt jede 10 Zeile nicht mehr verstehe und von daher das Programm nicht anpassen kann.

    Also ich bitte ich um einen Tipp, Hilfestellung zu meinen Quellcode.

    Was muss in der for()-Schleife stehen und warum? Das wäre wirklich super, wenn mir das jmd. sagen könnte...

    Ich habe schon eine Lösung wie ich es machen würde, aber das wird wahrscheinlich 99,999999% der Personen hier im Forum nicht gefallen. Weil es die "Idioten"-Lösung ist. Aber anscheinend scheint das die einzig machbare zu sein, denn ich dreh mich hier die ganze Zeit im Kreis und komm der Lösung kein Stück näher.



  • Javun schrieb:

    Warum müsst ihr das denn alles kompliziert machen?! Ich bin Dir wirklich dankbar, dass Du Dir die Mühe machst, aber da ist einfach so viel neues bei, da sitz ich ja noch Wochen dran, wenn ich mir jede Funktion und sowas wie struct, friend usw. beibringen muss.

    Schade - und ich hatte mir wirklich Mühe gegeben, es eben nicht kompliziert zu beschreiben - eben Schritt für Schritt. Und wenn es so viel wird - aus Deiner Sicht kompliziert, dann liegt es daran, dass das Problem eben kompliziert ist. Dir ist sicher aufgefallen, dass hier niemand eine schnelle und kurze und funktionsfähige Lösung hin geklatscht hat.

    .. aber wenn schon struct für Dich etwas neues ist, dann solltest Du von solchen Aufgaben, wie Du sie uns hier beschreibst die Finger lassen. Das ist dann noch eine (oder zwei) Nummer zu groß für Dich.

    Javun schrieb:

    Ich wollte doch einfach nur Wissen, wo MEIN Fehler liegt, d.h. MEINEN Quellcode korrigieren.

    Du meinst diesen Code hier:

    Javun schrieb:

    double real, imag;
        char plus, j, var;
        int a = 0;
        int b = 0;
        for(int p=0; p<7770; p++){
                    f>>real;                 // double Zahl wird erfolgreich gelesen
                    f.seekg((ios::cur)+1);   // der Zeiger wird +1 genommen
                    var = f.peek();          // es wird das nächste Element gelesen
       
                    if(var == 'j'){          // es wird kontrolliert ob das Element ein j war
                      f.unget();             // nun soll zurückgesprungen werden an das Ende der ausgelesenen double Zahl
                      f>>plus>>j>>imag;      // nun lesen wir den Rest der Zeile aus, beim nächsten Durchlauf soll nun auch die nächste Zahl/Zeile gelesen werden, aber cur zeigt IMMER auf die gleiche Stelle
                      if(plus == '-'){
                              imag *= -1;
                      }
                    }
                    else{
                       imag = 0;
                       f.unget();
                    }
                   complexNumbers[p]=complex<double>(real,imag);
        }
    

    Ok .. kommt gleich



  • Werner Salomon schrieb:

    Schade - und ich hatte mir wirklich Mühe gegeben, es eben nicht kompliziert zu beschreiben - eben Schritt für Schritt.

    Also ich für meinen Teil kann bei deinen Lektionen viel lernen (vor allem Richtung abstrakter Denkweise) und ich denke andere auch (inklusive meines Profs :D). Also falls du noch Lust hast die weiteren Lektionen zu posten, würde ich mich zumindest sehr freuen. 🙂



  • template <typename Iter>
    std::complex<double> parse_complex(Iter begin, Iter end)
    {
    	using namespace boost::spirit::qi;
    	using boost::phoenix::ref;
    
    	double real, imag = 0;
    	char sign;
    
    	if(!phrase_parse(begin, end, double_[ref(real) = _1] >> -((char_('+') | char_('-'))[ref(sign) = _1] >> 'j' >> double_[ref(imag) = _1]), ascii::space) || begin != end)
    		throw parse_error("parse error here: " + std::string(begin, end));
    
    	return std::complex<double>(real, sign == '-' ? -imag : imag);
    }
    

    Um eine Lösungsmöglichkeit zu posten.



  • Ich habe die Stellen mit ### markiert. Der Aufruf in Zeile 7 (oben) ist schon syntaktisch falsch. Die Korrektur findest Du in Zeile 2 (unten). Das ungetc in Zeile 6 geht immer schief, weil es nichts zu unget'en gibt. Du hast ja vorher seekg aufgerufen.

    f>>real;                 // double Zahl wird erfolgreich gelesen
            f.seekg( 1, ios::cur );   // 1 ist der Offset, ab ios::cur-rent-Position // ###
            var = f.peek();          // es wird das nächste Element gelesen
    
            if(var == 'j'){          // es wird kontrolliert ob das Element ein j war
                f.unget();             // nun soll zurückgesprungen werden an das Ende der ausgelesenen double Zahl // ### ist hier immer falsch, weil vorker ein seekg aufgerufen wurde
                f>>plus>>j>>imag;      // nun lesen wir den Rest der Zeile aus, beim nächsten Durchlauf soll nun auch die nächste Zahl/Zeile gelesen werden, aber cur zeigt IMMER auf die gleiche Stelle
    

    Nur zur Info. Eine Datei ist kein lineares Stück Speicher, in dem man mit einem Zeiger herum wandern kann. Und der Einsatz von seekg und unget erfordert einige Kenntnis von den Internas eines Streams - und das soll einfach sein?

    Ich versuche mal was:

    double real, imag;
        char plus, j, var;
        int a = 0;
        int b = 0;
        for(int p=0; p<7770; p++){
            f>>real;
            f.ignore( 1 ); // überliest ein Zeichen, aber es wird im Streambuf berücksichtigt.
            var = f.peek();
    
            if(var == 'j'){
                f.unget();             // Du kannst Dich nicht darauf verlassen, dass dies immer geht !
                f>>plus>>j>>imag;
                if(plus == '-'){
                    imag *= -1;
                }
            }
            else{
                imag = 0;
                f.unget();
            }
            if( f.fail() ) // auf Fehler prüfen
                break;
            complexNumbers[p]=complex<double>(real,imag);
        }
    

    obiges funktioniert in dem Umfeld, wo Du es einsetzt; so weit ich es beurteilen kann. Mit EOF kommt die Konstruktion nicht klar. Ist aber wohl auch nicht notwendig, da in Deiner Datei hinter den komplexen Zahlen immer noch was kommt.

    Probier's mal aus.
    Gruß
    Werner



  • Gugelmoser schrieb:

    Werner Salomon schrieb:

    Schade - und ich hatte mir wirklich Mühe gegeben, es eben nicht kompliziert zu beschreiben - eben Schritt für Schritt.

    Also ich für meinen Teil kann bei deinen Lektionen viel lernen (vor allem Richtung abstrakter Denkweise) und ich denke andere auch (inklusive meines Profs :D). Also falls du noch Lust hast die weiteren Lektionen zu posten, würde ich mich zumindest sehr freuen. 🙂

    Danke 😉
    dann mache ich weiter - zwei Lektionen habe ich noch. Und dann muss man mal sehen.

    Hallo an alle, die es interessiert,

    Ich muss noch mal betonen, dass diese Art der Lösung vorrangig dafür gedacht ist, zu zeigen wie es geht. In der Praxis würde man sicher einiges zusammenfassen. Allerdings würde ich diese Art der Lösung immer der bevorzugen, die ich in meinem letzten Beitrag an Javun gepostet habe. Bei der (anscheinend komplizierten) Lösung bin ich mir eher sicher, dass sie funktioniert, und wenn nicht, lassen sich Probleme schneller identifizieren, weil ja alles einzeln testbar ist.
    Aber nun zum 'j'.

    Im Grunde ist das die einfachste Lektion - es geht letztlich nur darum, dass nächste Zeichen zu Lesen und wenn's kein 'j' ist, den Stream auf Fehler zu setzen. Für mich wäre es am einfachsten auf Char<> zu verweisen und den Streaming-Operator des ComplexReaders wie folgt zu ändern:

    friend std::istream& operator>>( std::istream& in, ComplexReader cr ) // Bem.: Übergabe Parameter 'cr' per Value
        {
            double real, imag;
            PlusMinus plus;
            if( in >> real >> plus >> Char<'j'> >> imag )
                cr.m_target = std::complex< double >( real, plus.positive()? imag: -imag );
            return in;
        }
    

    jetzt gibt es immer einen Fehler, falls das 'j' in der komplexen Zahl fehlt. Damit wird die Eingabesicherheit erhöht. Wenn Formatfehler vorliegen merkt man es ggf. früher.

    Natürlich kann man das auch als Teil des ComplexReader implementieren - nur der Vollständigkeit halber:

    // --   Lektion 4
    #include <iostream>
    #include <complex>
    
    template< typename T >
    struct ComplexReader
    {
        struct PlusMinus
        {
            friend std::istream& operator>>( std::istream& in, PlusMinus& pm )
            {
                if( in >> pm.m_op )
                {
                    if( pm.m_op != '+' && pm.m_op != '-' )
                        in.setstate( std::ios_base::failbit );
                }
                return in;
            }
            bool positive() const { return m_op == '+'; }
        private:
            char m_op;
        };
    
        friend std::istream& imagChar( std::istream& in )
        {
            char c;
            if( in >> c && c != 'j' ) // <== hier steht das 'j'
                in.setstate( std::ios_base::failbit );
            return in;
        }
    
        typedef T value_type;
        ComplexReader( std::complex< value_type >& target )
            : m_target( target )
        {}
        friend std::istream& operator>>( std::istream& in, ComplexReader cr ) // Bem.: Übergabe Parameter 'cr' per Value
        {
            double real, imag;
            PlusMinus plus;
            if( in >> real >> plus >> imagChar >> imag )
                cr.m_target = std::complex< double >( real, plus.positive()? imag: -imag );
            return in;
        }
    private:
        std::complex< value_type >& m_target;
    };
    template< typename T >
    ComplexReader< T > read_complex( std::complex< T >& z )
    {
        return ComplexReader< T >( z );
    }
    
    int main()
    {
        using namespace std;
        cout << "eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 " << endl;
        complex< double > z;
        while( cin >> read_complex( z ) )
            cout << "gelesen: " << z << endl;
        return 0;
    }
    

    ein Test zeigt

    eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017
    3+j9
    gelesen: (3,9)
    -9.1234E-009-j4.009E-1
    gelesen: (-9.1234e-009,-0.4009)
    3+i9
    -> Lesefehler
    

    Das Programm besteht jetzt auf dem 'j' und das ist auch gut so, denn vorher wäre eine Eingabe wie -5+12.1 als (-5,2.1) ohne Erkennen eines Fehlers akzeptiert worden!

    In Lektion 5 geht es um den optionalen Imaginärteil.



  • Danke erstmal für die Hilfe. Leider muss ich sagen, dass Dein Code auch nicht funktioniert.

    Er bricht genau an diesem Übergang

    -2.32123546894e-007-j3.26888151276e-009
      -1.99295822081e-007-j2.80658484281e-009
      -1.74571227184e-007-j2.45840055798e-009
      -1.55279183547e-007-j2.18672021518e-009
      -1.39806488024e-007-j1.96882586959e-009
      -1.27121132199e-007-j1.79018425527e-009
      +1.60790664118e-007
      +1.81016196142e-007
      +2.07106698463e-007
      +2.42045708361e-007
    

    ab, d.h. f.fail tritt ein...

    Wenn ich das

    if( f.fail() ) // auf Fehler prüfen
              break;
    

    rausnehme, dann sind ab der oben genannten Stelle alle folgenden complex Zahlen

    (-2.32123546894e-007,0)
    

    Ich finde das schon sehr ungewöhnlich...

    Nachtrag: Es muss am f.unget() liegen! Wenn ich dies rausnehme, dann stimmen viele Ergebnisse wieder, wiederrum werden andere falsch. Welche andere Möglichkeit gibt es?



  • .. auf jeden Fall die letzte für heute,

    Was ich bis jetzt einfach verschwiegen habe, ist die Tatsache, dass zwischen den einzelnen Teile der Komplexen Zahl beliebig viele Whitespace Character (Space, Tab, LF) stehen dürfen. Also würde von dem hier vorgestellten ComplexReader folgendes einwandfrei gelesen:

    eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017
    12.5
    
    -
    
    j
    0.002
    gelesen: (12.5,-0.002)
    

    Um zu definieren wie man einen optionalen Imaginärteil erkennt, muss das Format genau definiert sein. Oben wurde der Vorschlag gemacht, auf das j los zu gehen. Das hat aber zum einen den Nachteil, das das 'j' nicht das erste Zeichen eines Imaginärteils ist, weil davor steht noch der Operator, und zum anderen kann man doch erwarten, dass ein Imaginärteil auch allein stehen kann. Wenn der Realteil allein vorkommt, warum dann nicht auch der Imaginärteil?
    Beispiel:

    -1.23-j5.67E008
    25.4
    -j4711.0
    

    Die jetzige Implementierung würde zwei komplexe Zahlen lesen. Eine Implementierung, die ein Zeilenende als Ende einer einer komplexen Zahl verwendet, liest zwei Werte und stellt dann einen Formatfehler fest, und eine Implementierung, die zusätzlich alleinstehende Imaginärteile akzeptiert, liest drei Werte ohne Fehler ein. Das will also gut überlegt sein.

    Ich postuliere einfach, dass eine komplexe Zahl hinter dem Realteil mit jedem Zeichen oder EOF endet, was nicht '+' oder '-' ist, oder wenn ein Imaginärteil existiert eben hinter diesem endet. Alleinstehende Imaginärteile seien nicht zulässig, also ein Realteil muss immer vorhanden sein. Weiter darf eine komplexe Zahl durch keine White Space Character unterbrochen sein.
    Das Unterbinden des Überspringens von White Space Character geschieht mit std::noskipws. Da ich für spätere Leseroutinen den Zustand des Streams nicht veränderrn möchte und ich auch nicht weiß, ob das skipws-Flag gesetzt ist oder nicht, merke man sich einfach den aktuellen Zustand und stelle ihn am Ende des Streaming-Operators wieder her. Eine elegante Lösung dazu bietet boost.io.ios_state an.

    friend std::istream& operator>>( std::istream& in, ComplexReader cr ) // Bem.: Übergabe Parameter 'cr' per Value
        {
            const std::ios_base::fmtflags oldFlags = in.flags(); // merken wg. std::noskipws
            double real, imag;
            PlusMinus plus;
            if( in >> real >> std::noskipws >> plus >> imagChar >> imag )
                cr.m_target = std::complex< double >( real, plus.positive()? imag: -imag );
            in.flags( oldFlags ); // alten Zustand des Streams wieder herstellen
            return in;
        }
    

    Wichtig: Das Starten des Einlesens des Realteils überlassen wir hier noch dem Aufrufer von ComplexReader. Natürlich dürfen vor dem Realteil White Space Character stehen und erst danach verhindert man das Überspringen derselben.
    Ein Test:

    eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017
    +1.234-j5.67E008
    gelesen: (1.234,-5.67e+008)
    +1.234- j5.67E008
    -> Lesefehler, da ein Space hinter den Operator steht
    

    Zur Unterbringung der Funktionalität des optionalen Lesens des Imaginärteils gibt es im Prinzip zwei Möglichkeiten. Zum einen kann man das völlig unabhängig von dem was schon implementiert ist, einbringen. Also in der Art

    if( in >> real >> std::noskipws )
            {
                if( kommt_noch_was( in ) ) // <== kommt noch der Imaginärteil?
                {
                    if( in >> plus >> imagChar >> imag )
                        cr.m_target = std::complex< double >( real, plus.positive()? imag: -imag );
                }
                else
                    cr.m_target = std::complex< double >( real, 0.0 );
            }
    

    Vorteil: alles was bisher existiert, wird nicht angefasst, und funktioniert sicher wie vorher. Der Nachteil ist, dass 'kommt_noch_was()' wissen muss, was der Operator tut, nämlich '+' oder '-' lesen. Damit schafft man hier eine Redundanz zwischen der Implementierung des Operators und der Funktion 'kommt_noch_was()' und auch mit der Streaming-Operator des ComplexReaders, da es von der Reihenfolge der Teile abhängt, ob auf +/- oder vielleicht doch auf 'j' geprüft wird.

    Es wäre leichter gewesen, wenn man hier die Negativprüfung postuliert hätte. Z.B. eine komplexe Zahl endet mit einem White Space oder EOF. Dann könnte 'kommt_noch_was()' eben dies abfragen. Das ist in der Praxis aber unschön, man stelle sich vor, die komplexe Zahl ist Teil einer Matrix und unmittelbar hinter ihr stünde ein ')' oder ein ';'. Das würde einen Lesefehler ergeben, der nicht erwünscht ist.

    Die zweite Möglichkeit besteht darin, das optionale Lesen mit in den Operator PlusMinus zu integrieren. Falls in der Lesefunktion von PlusMinus kein '+' oder '-' gefunden wird, so stelle man das Zeichen zurück und merke sich dies.

    // --   Lektion 5
    #include <iostream>
    #include <complex>
    
    template< typename T >
    struct ComplexReader
    {
        struct PlusMinus
        {
            friend std::istream& operator>>( std::istream& in, PlusMinus& pm )
            {
                if( in >> pm.m_op )
                {
                    pm.m_valid = (pm.m_op == '+' || pm.m_op == '-');
                    if( !pm.m_valid ) // falls kein Operator gelesen wurde, Zeichen zurückstellen
                        in.putback( pm.m_op );
                }
                return in;
            }
            bool positive() const { return m_op == '+'; }
            bool ok() const { return m_valid; }
        private:
            char m_op;
            bool m_valid; // true, falls m_op=='+' oder m_op=='-'
        };
    
        friend std::istream& imagChar( std::istream& in )
        {
            char c;
            if( in >> c && c != 'j' ) // <== hier steht das 'j'
                in.setstate( std::ios_base::failbit );
            return in;
        }
    
        typedef T value_type;
        ComplexReader( std::complex< value_type >& target )
            : m_target( target )
        {}
        friend std::istream& operator>>( std::istream& in, ComplexReader cr ) // Bem.: Übergabe Parameter 'cr' per Value
        {
            const std::ios_base::fmtflags oldFlags = in.flags(); // merken wg. std::noskipws
            double real, imag;
            PlusMinus plus;
            if( in >> real >> std::noskipws >> plus )
            {
                if( plus.ok() ) // <== kommt noch der Imaginärteil?
                {
                    if( in >> imagChar >> imag )
                        cr.m_target = std::complex< double >( real, plus.positive()? imag: -imag );
                }
                else
                    cr.m_target = std::complex< double >( real, 0.0 );
            }
            in.flags( oldFlags ); // alten Zustand des Streams wieder herstellen
            return in;
        }
    private:
        std::complex< value_type >& m_target;
    };
    template< typename T >
    ComplexReader< T > read_complex( std::complex< T >& z )
    {
        return ComplexReader< T >( z );
    }
    
    int main()
    {
        using namespace std;
        cout << "eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017 " << endl;
        complex< double > z;
        while( cin >> read_complex( z ) )
            cout << "gelesen: " << z << endl;
        return 0;
    }
    

    Ein abschließender Test:

    eine imaginaere Zahl bitte: z.B.: -9.351e-017-j1.539e-017
    -1.234-j5.6E007
    gelesen: (-1.234,-5.6e+007)
    -1.234
    gelesen: (-1.234,0)
    56.1 5.001+j4
    gelesen: (56.1,0)
    gelesen: (5.001,4)
    42
    gelesen: (42,0)
    +j7.1
    -> Lesefehler; alleinstehender Imaginärteil ist nicht vorgesehen
    

    und das soll für heute reichen.
    Gruß
    Werner



  • Leider funktioniert die Funktion immer noch nicht. So langsam bin ich wirklich am verzweifeln, da aus einer so einfachen Sache eine so komplizierte Sache gemacht wurde.

    Wie ich persönlich als "Nicht-Programmierer" daran gehen würde ist einfach:
    Zeile einlesen mit getline(f,s) und in einen string kopieren. Nun die Länge des strings kontrollieren und dadurch dann Fallunterscheidung ob die Zahl komplex oder reel ist. Wenn die Zahl reel ist dann zerstückel ich den string so, dass er wieder ne Zahl ergibt und FERTIG!!! Sieht nicht schön aus und wird auch wohl kein Mensch auf den ersten Blick verstehen, aber es ist einfach, simpel und funktioniert. Und das schaff ich als "Nicht-Programmierer" mit den einfachsten Funktionen. Ich dachte Ihr könnt mir mit eurem Wissen vllt. paar Tipps geben, wie das Programm kompakter wird und einfacher zu programmieren, aber ich merke nur, dass das Programm nach jedem "Tipp" kaputter wird, da wieder was anderes nicht funktioniert...

    Für jeden der es sich nochmal gerne ansehen möchte, dass ist die Funktion die nicht von mir geschrieben sondern von den verschiedensten Forenmitgliedern "gebastelt" wurde. Falls jmd. noch einen Tipp für mich hat, wäre ich sehr dankbar, aber ich denke wohl kaum das das noch was wird...

    vector<complex<double> > readComplexNumbers(const char* filename,const char* tag, const char* attributeName){
        vector<complex<double> > complexNumbers(30750);
        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, var; 
        int a = 0;
        int b = 0;
        for(int p=0; p<30750; p++){
            f>>real;
            f.ignore( 1 ); // überliest ein Zeichen, aber es wird im Streambuf berücksichtigt.
            var = f.peek();
    
            if(var == 'j'){
                f.unget();             // Du kannst Dich nicht darauf verlassen, dass dies immer geht !
                f>>plus>>j>>imag;
                if(plus == '-'){
                    imag *= -1;
                }
            }
            else{
                imag = 0;
                f.unget();
            }
            if(f.fail()){ // auf Fehler prüfen
                break;}
            complexNumbers[p]=complex<double>(real,imag);
                  cout<<complex<double>(real,imag)<<endl; 
        } 
    
        return complexNumbers; 
    }
    


  • Javun schrieb:

    Wie ich persönlich als "Nicht-Programmierer" daran gehen würde ist einfach:
    Zeile einlesen mit getline(f,s) und in einen string kopieren.

    Es gibt ein getline, das gleich in einen string einliest.

    Javun schrieb:

    Nun die Länge des strings kontrollieren und dadurch dann Fallunterscheidung ob die Zahl komplex oder reel ist.

    Javun schrieb:

    Wenn die Zahl reel ist dann zerstückel ich den string so, dass er wieder ne Zahl ergibt und FERTIG!!! Sieht nicht schön aus und wird auch wohl kein Mensch auf den ersten Blick verstehen, aber es ist einfach, simpel und funktioniert. Und das schaff ich als "Nicht-Programmierer" mit den einfachsten Funktionen.

    Dann den string in einen stringstream geben und jenen dann wie gewohnt auslesen.

    Das halte ich zunächst für eine sehr gute Idee.



  • War doch gar nicht so schwer?!

    Soweit ich es kontrollieren konnte funktioniert die Funktion jetzt. Vllt. findet ja aber noch jmd. einen Fehler?

    Und noch eine Frage am Rande:
    Wenn ich eine double Zahl habe, wie z.B. 1.47086e-09, wie kann ich daraus ein Excel/OpenOffice kompatibles Format bekommen? Also als erstes müsste statt . ein Komma und komischer Weise macht Excel aus e-09 E-04...

    Also aus der Zahl 1.47086e-09 macht Excel 1,47E-4...Auch hier könnte ich natürlich wieder die Zahl als string zerlegen usw., aber vllt. gibt es ja auch eine einfacherere Möglichkeit...

    //nun Werte lesen
        string s;  
        double real, imag;
        char plus, j; 
        for(int p=0; p<21525; p++){
            getline(f, s);
            stringstream ss(s);
            if(s.size() > 22){
                 ss>>real>>plus>>j>>imag;
                 if(plus == '-'){
                    imag *= -1;
                 }              
            }
            else{
                 ss>>real;
                 imag=0;                
            }
            complexNumbers[p]=complex<double>(real,imag);
        }
    

  • Mod

    Die Unterscheidung anhand der magischen Zahl 22 ist total willkürlich und wird dir oder jemand anderem in Zukunft noch Probleme machen. Warum unterscheidest du nicht anhand eines eindeutigen Kriteriums, z.B. ob ein 'j' drin vorkommt oder nicht?



  • SeppJ schrieb:

    Die Unterscheidung anhand der magischen Zahl 22 ist total willkürlich und wird dir oder jemand anderem in Zukunft noch Probleme machen. Warum unterscheidest du nicht anhand eines eindeutigen Kriteriums, z.B. ob ein 'j' drin vorkommt oder nicht?

    Oder ob nach
    ss>>real>>plus>>j>>imag;
    in j ein 'j' steht, anderenfalls plus, j und imag nicht verwendet werden.


Anmelden zum Antworten