Operator >> überladen



  • Und wie kann ich dann den Operator >> so überladen, dass er bis zum ende der Zeile liest oder geht das gar nicht?
    Danke schon mal.



  • ugoessner schrieb:

    Und wie kann ich dann den Operator >> so überladen, dass er bis zum ende der Zeile liest oder geht das gar nicht?

    Nimm - einfach - meine - Funktion!
    Außerdem kannst du nicht einfach bereits überladene Operatoren neu definieren. DAnn könnte man einen std::string -Wrapper basteln und dann für den den Shift-Operator überladen, aber das ist mehr als nur unnötig.



  • int main()
    {
    	string test;
    	cout << "Eingabe: ";
    	getline(cin,test); // so wirds gemacht.
    	cout << "Ausgabe: " << test << endl;
    }
    

    Wenn du eine komplette Zeile einlesen willst, darfst du kein operator>> verwenden.



  • ugoessner schrieb:

    Und wie kann ich dann den Operator >> so überladen, dass er bis zum ende der Zeile liest oder geht das gar nicht?
    Danke schon mal.

    struct Line
    {
        std::string value;
    
        operator std::string const&()
        {
            return value;
        }
    };
    
    std::istream& operator>>(std::istream& in, Line& out)
    {
        return std::getline(in, out.value);
    }
    
    std::ostream& operator<<(std::ostream& out, Line const& in)
    {
        return out << in.value;
    }
    
    int main()
    {
        Line line;
        std::cin >> line;
        std::cout << line;
    }
    


  • Ethon schrieb:

    ..
    

    Ho! Klaust mir meine Idee, was? 😃



  • Hacker schrieb:

    Ethon schrieb:

    ..
    

    Ho! Klaust mir meine Idee, was? 😃

    Ehrlich gesagt hab ich deinen Post nicht gelesen, hatte nur das Problem auch schon einmal. 😉



  • Ethon schrieb:

    ...

    struct Line
    {
        std::string value;
    
        operator std::string const&()
        {
            return value;
        }
    }; 
    
    int main()
    {
    	Line line;
    	const std::string& ref = (std::string)line; // copy  :confused: 
    }
    

    Wieso bekomme ich hier keine Referenz auf line.value ?



  • const std::string& ref = (std::string const&)line; // nix copy  :bulb: 
    }
    

    Gugelmoser schrieb:

    Wieso bekomme ich hier keine Referenz auf line.value ?

    Bekommst du ja, aber du konstruierst daraus direkt wieder einen String.



  • Gugelmoser schrieb:

    const std::string& ref = (std::string)line; // copy  :confused:
    

    Wieso bekomme ich hier keine Referenz auf line.value ?

    🙄 Das wird ja auch so gemacht.

    const std::string& ref = line;
    

    Danke für den Hinweis Bashar, da bin ich gerade total neben mir gestanden.



  • man kann das getline auch in einen selbstgestrickten Manipulator stecken, dann spart man sich das casten von Line nach std::string. Die Idee ist, dass die Anwendung später so aussieht:

    string ln;
        if( cin >> line( ln ) )
        {  // Zeile nach string 'ln' eingelsen
    

    das ganze sieht in einer einfachen Fassung so aus:

    #include <iostream>
    #include <string>
    
    struct line_reader
    {
        explicit line_reader( std::string& txt ) : m_txt( txt ) {}
        std::string& m_txt;
    private:
        line_reader& operator=( const line_reader ); // unterdrückt ggf. Warnungen des Compilers
    };
    std::istream& operator>>( std::istream& in, line_reader lr )
    {
        return getline( in, lr.m_txt );
    }
    line_reader line( std::string& txt )
    {
        return line_reader( txt );
    }
    
    int main()
    {
        using namespace std;
        for( string ln; cout << ">", cin >> line( ln ) && ln != "q"; )
        {
            cout << "line: [" << ln << "]" << endl;
        }
        return 0;
    }
    

    und für die Profis gibt es noch die Template-Version mit schaltbarer trimm-Funktionalität für alle std::basic_istream's und mit user-defined Abbruchbedingungen:

    #include <iostream>
    #include <string>
    
    template< typename E, typename Traits, typename Pred >
    struct line_reader
    {
        explicit line_reader( std::basic_string< E, Traits >& txt, Pred delim ) : m_txt( txt ), m_pred( delim ) {}
        std::basic_string< E, Traits >& m_txt;
        Pred m_pred;
    private:
        line_reader& operator=( const line_reader ); // unterdrückt ggf. Warnungen des Compilers
    };
    
    namespace lr_detail
    {
        template< typename E >
        bool is_delimiter( E delim, E c )
        {
            return delim == c;
        }
        template< typename E, typename Pred >
        bool is_delimiter( Pred delim, E c )
        {
            return delim( c );
        }
    }
    
    // --   factory functions
    template< typename E, typename Traits, typename Pred >
    line_reader< E, Traits, Pred > line( std::basic_string< E, Traits >& txt, Pred pred )
    {
        return line_reader< E, Traits, Pred >( txt, pred );
    }
    template< typename E, typename Traits >
    line_reader< E, Traits, E > line( std::basic_string< E, Traits >& txt, E delim = '\n' )
    {
        return line_reader< E, Traits, E >( txt, delim );
    }
    
    // --   extractor
    template< typename E, typename Traits, typename Pred >
    std::basic_istream< E, Traits >& operator>>( std::basic_istream< E, Traits >& in, line_reader< E, Traits, Pred > lr )
    {
        std::ios_base::iostate state = std::ios_base::goodbit;
        bool changed = false;
        typename std::basic_istream< E, Traits >::sentry ok( in, true ); // noskipws = true
        if( ok )
        {
            lr.m_txt.erase();
            typename std::basic_string< E, Traits >::size_type last_nospace_idx = 0;
            try
            {
                const typename std::ctype< E >& ctype_ = std::use_facet< typename std::ctype< E > >( in.getloc() );
                for( typename Traits::int_type m = in.rdbuf()->sgetc(); ; m = in.rdbuf()->snextc() )
                {
                    if( Traits::eq_int_type( m, Traits::eof() ) )
                    {
                        state |= std::ios_base::eofbit;
                        break;
                    }
                    const E c = Traits::to_char_type( m );
                    changed = true;
                    if( lr_detail::is_delimiter( lr.m_pred, c ) )
                    {
                        in.rdbuf()->sbumpc(); // consume the delimiter char
                        break;
                    }
                    if( ctype_.is( std::ctype_base::space, c ) && (in.flags() & std::ios_base::skipws) )
                    {
                        if( last_nospace_idx > 0 )
                            lr.m_txt += c;
                    }
                    else
                    {
                        lr.m_txt += c;
                        last_nospace_idx = lr.m_txt.size();
                    }
                }
                if( in.flags() & std::ios_base::skipws )
                    lr.m_txt.erase( last_nospace_idx ); // trim text, if skipws is set
            }
            catch( ... )
            {
                state |= std::ios_base::badbit;
                if( in.exceptions() & std::ios_base::badbit )
                    throw; // re-throw, if badbit is set
            }
        }
        if( !changed )
            state |= std::ios_base::failbit;
        in.setstate( state );
        return in;
    }
    
    struct eol
    {
        bool operator()( char c ) const
        {
            return c == ';' || c == '\n';
        }
    };
    
    int main()
    {
        using namespace std;
        for( string ln; cout << ">", cin >> line( ln /*, eol()*/ ) && ln != "q"; )
        {
            if( ln == "skipws" ) cin >> skipws;
            else if( ln == "noskipws" ) cin >> noskipws;
            else cout << "line: [" << ln << "]" << endl;
        }
        return 0;
    }
    

    .. probiert's mal aus.

    Gruß
    Werner

    PS.: nicht vergessen: getline ist doof



  • Werner Salomon schrieb:

    PS.: nicht vergessen: getline ist doof

    Sorry, aber ich sehe hier keine Begründung. Warum ist getline doof?

    Nehmen wir ein kleines Beispiel. Ich habe eine Konfigurationsdatei, die von meinem Programm eingelesen werden soll. Damit der Benutzer die Datei lesen und bearbeiten kann, werden die Daten zeilenweise in der Form "key = value" gespeichert. Einzelne Zeilen werden mit getline eingelesen und danach geparst.

    Oder ist das eine Aufgabe, die zu deinen Ausnahmen gehört? Was definierst du überhaupt als Anwendungsfall für getline, und was als Ausnahme? Und was macht dein getline besser als std::getline?



  • 314159265358979 schrieb:

    Nehmen wir ein kleines Beispiel. Ich habe eine Konfigurationsdatei, die von meinem Programm eingelesen werden soll. Damit der Benutzer die Datei lesen und bearbeiten kann, werden die Daten zeilenweise in der Form "key = value" gespeichert. Einzelne Zeilen werden mit getline eingelesen und danach geparst.

    Also das finde ich jetzt doof. Nimm eine XML-Datei, lies alles auf einmal ein und parse es dann mit einem XML-Parser. 🤡

    New, im Ernst. Ich versteh auch nicht, was doof an getline ist. Macht was ich will, ist portabel und prüfbar.



  • 314159265358979 schrieb:

    Und was macht dein getline besser als std::getline?

    besser nichts - das ist genauso doof 😉
    Es kann etwas mehr, aber das ist alles.

    314159265358979 schrieb:

    Warum ist getline doof?

    Nehmen wir ein kleines Beispiel. Ich habe eine Konfigurationsdatei, die von meinem Programm eingelesen werden soll. Damit der Benutzer die Datei lesen und bearbeiten kann, werden die Daten zeilenweise in der Form "key = value" gespeichert. Einzelne Zeilen werden mit getline eingelesen und danach geparst.

    wenn Du dies tust, so ignorierst Du den Parser, den Dir C++ zur Verfügung stellt - nämlich den std::istream - liest an ihm vorbei und bastelst Dir anschließend einen eigenen Parser. Dieses Vorgehen ist doch doof, oder?

    Es gibt hier im Forum zu häuf Beispiele dafür. Die Ursache ist i.A. die Unkenntnis über die Fähigkeiten von istream.

    Gruß
    Werner



  • Werner Salomon schrieb:

    314159265358979 schrieb:

    Warum ist getline doof?

    Nehmen wir ein kleines Beispiel. Ich habe eine Konfigurationsdatei, die von meinem Programm eingelesen werden soll. Damit der Benutzer die Datei lesen und bearbeiten kann, werden die Daten zeilenweise in der Form "key = value" gespeichert. Einzelne Zeilen werden mit getline eingelesen und danach geparst.

    wenn Du dies tust, so ignorierst Du den Parser, den Dir C++ zur Verfügung stellt - nämlich den std::istream - liest an ihm vorbei und bastelst Dir anschließend einen eigenen Parser. Dieses Vorgehen ist doch doof, oder?

    Es gibt hier im Forum zu häuf Beispiele dafür. Die Ursache ist i.A. die Unkenntnis über die Fähigkeiten von istream.

    Gruß
    Werner

    Ich persönlich würde darauf ein getline mit '=' als demin und anschließend ein getline mit '\n' als delim loslassen. Dann hat man mit minimalem Aufwand Key und Value in nem String. Oder gibts da eine bessere Vorgehensweise?


  • Mod

    Ethon_ schrieb:

    Ich persönlich würde darauf ein getline mit '=' als demin und anschließend ein getline mit '\n' als delim loslassen. Dann hat man mit minimalem Aufwand Key und Value in nem String. Oder gibts da eine bessere Vorgehensweise?

    Das wäre doof, da man dann den ganzen Whitespace mitnimmt. Besser stream >> key >> gleich >> value;



  • SeppJ schrieb:

    Ethon_ schrieb:

    Ich persönlich würde darauf ein getline mit '=' als demin und anschließend ein getline mit '\n' als delim loslassen. Dann hat man mit minimalem Aufwand Key und Value in nem String. Oder gibts da eine bessere Vorgehensweise?

    Das wäre doof, da man dann den ganzen Whitespace mitnimmt. Besser stream >> key >> gleich >> value;

    Was aber erfordert dass sowohl Key als auch Value keine Whitespace enthalten. Da doch lieber einfach danach trimmen.

    Obwohl meine Lieblingslösung ja wäre, die ganze Datei in einen vector zu laden, beim Parsen Null-Terminatoren einzufügen und einfach nur Pointer auf die Strings herumzureichen. Dann ist es nur eine Allocation.



  • Werner Salomon schrieb:

    wenn Du dies tust, so ignorierst Du den Parser, den Dir C++ zur Verfügung stellt - nämlich den std::istream - liest an ihm vorbei und bastelst Dir anschließend einen eigenen Parser. Dieses Vorgehen ist doch doof, oder?

    Der istream würde folgende (fehlerhafte) Datei als korrekt erkennen und einlesen.

    foo =
    bar
    

    Um das zu vermeiden lese ich zeilenweise und parse selbst. Es sei denn, man kann dem istream irgendwie sagen, \r und \n nicht zu überlesen, aber das wäre mir neu.


  • Mod

    314159265358979 schrieb:

    Um das zu vermeiden lese ich zeilenweise und parse selbst. Es sei denn, man kann dem istream irgendwie sagen, \r und \n nicht zu überlesen, aber das wäre mir neu.

    Nun, das geht schon. Nötig dafür ist bloß ein bisschen obskures Wissen um Facets und Locales.

    Aber ich würde an sich die obige Datei nicht unbedingt als falsch ansehen. Zumindest wäre es ein doofes Format, dass sich einerseits um Paare key = wert dreht, andererseits jedoch strikt zeilenbasiert ist.

    Falls man dieses Format jedoch vorgegeben hat, dann ist das mit dem getline durchaus eine gute Möglichkeit, wenn man sich nicht gut mit den Streams auskennt (und Werner ist einer der wenigen mir bekannten Personen, die sich gut genug auskennen, für die oben angedeutete Methode)



  • Gut, dieses obskure Wissen habe ich (leider) nicht. Ihr habt nicht zufällig eine Buchempfehlung?

    Das Format hab ich mir gestern spontan ausgedacht. Was ist daran doof? Ich bezweifle mal, dass ein User, der meinem Programm so eine Datei vorlegt, das so wollte. Meiner Meinung nach wäre es schlimm, so eine Datei zu schlucken, da so Resultate auftreten könnten, die der User ganz und gar nicht beabsichtigt hätte.


  • Mod

    Buchempfehlungen gab es mal von Werner (die Bücher selber habe ich nicht und daher auch nicht das Wissen):
    Standard C++ IOStreams and Locales | ISBN: 0201183951C++ IOStreams Handbook | ISBN: 0201596415
    Wie du siehst, die sind nicht gut zu bekommen, sonst hätte ich sie selber 😞 . Und sie sind uralt. 😞
    (Ich hoffe das waren die richtigen, die Buchempfehlung ist aus dem Gedächtnis zitiert)

    An dem Format stört mich, dass man einerseits Formatierungszeichen (das '=') hat, die schon die logische Struktur eindeutig machen, andererseits aber auch der Whitespace eine Bedeutung hat. Whitespace sehe ich in erster Linie als Formatierung für einen Menschen zum besseren Lesen an. Außer in Sprachen wie Python, aber da gibt es wiederum keine Sonderzeichen und es ist der Whitespace allein, der die logische Struktur ergibt. Ich persönlich hasse Formate, bei denen das gemischt ist (z.B. FORTRAN im alten Stil)


Anmelden zum Antworten