Datei zeichenweise auslesen - newline Problem.



  • SeppJ schrieb:

    Nein, das ist vom Standard so vorgesehen.

    Mag sein, die Funktion läßt sich zumindest nicht wirklich verwenden, selbst dann nicht, wenn man negative Zahlen extrahieren lassen möchte.

    #include <iostream>
    #include <locale>
    
    using namespace std;
    
    int main()
    {
      locale loc;
      do
        if (isdigit(cin.peek(), loc))
          {
            unsigned wert;
            cin >> wert;
            cout << wert << '\n';
          }
      while (cin.ignore(1));
    }
    

    SeppJ schrieb:

    Ist das jetzt besser?

    Sry, aber der Code funktioniert leider nicht.
    Aber macht nichts, wir haben 2 funktionierende Versionen.



  • redrew99 schrieb:

    Gugelmoser schrieb:

    cctype ist in Ordnung, nicht in Ordnung wäre ctype.h

    Ok. Hatte das schon oft gesehen hier, daß von der Verwendung von C-Bibliotheken abgeraten wird, sofern es irgend möglich ist. Aber egal.

    Das ist richtig, nur hast du es falsch interpretiert: Header, die auf .h enden, sind für C. Für C++ gibt es eine Neuauflage für solche Header, die nicht auf .h enden, sondern mit c vorne anfangen. D.h. cctype ist richtig, ctype.h ist falsch.


  • Mod

    redrew99 schrieb:

    Sry, aber der Code funktioniert leider nicht.
    Aber macht nichts, wir haben 2 funktionierende Versionen.

    Sehr mitteilsam oder gar selbstständig bist du nicht gerade, oder?

    if (isdigit(static_cast<char>(cin.peek()), loc))
    

    Und jetzt verstehst du auch vielleicht so langsam, wieso man nicht aus Prinzipientreue gegen schöne gute Standardfunktionen ist, bloß weil es sie in C auch gibt.



  • SeppJ schrieb:

    Sehr mitteilsam oder gar selbstständig bist du nicht gerade, oder?

    Berechtigte Frage. Schauen wir doch mal.
    Nach Deinem Post

    SeppJ schrieb:

    Im Moment sieht das nach einer unendlich umständlichen Art und Weise aus, (bis auf den Fehler) alle Zahlen aus einer Datei auszugeben.

    (man beachte das "unendlich") habe ich mich hingesetzt und eine funktionierende Version gebastelt.

    Sry, aber wer Kritik in der Form wie "unendlich umständlich" äußert, steht in der moralischen Pflicht, es besser zu machen. Interessanterweise gestaltete sich das
    "besser machen" doch schwieriger als erwartet.

    Davon ab, vielen Dank für das Coaching und die Geduld, an Dich und natürlich an
    Gugelmoser. 👍



  • Hallo redrew99,

    wenn ich das richtig verstanden habe, so geht es doch darum, alles zu überlesen, was keine Zahl ist und anschließend die Zahl zu lesen. Unter der Voraussetzung, dass keine isolierten '-' (und '+'?) Zeichen in der Datei stehen, sollten auch Vorzeichen erlaubt sein, und das ganze sollte von außen schaltbar sein.
    Hier mal ein Manipulator, der direkt an der streambuf-Schnittstelle die Zeichen abgreift. In Summe ist das natürlich mehr Code, aber in der Anwendung wird's dann wieder schnuckelig einfach.

    #include <fstream>
    #include <iostream>
    #include <locale>
    
    template< typename E, typename Traits >
    std::basic_istream< E, Traits >& skip_until_number( std::basic_istream< E, Traits >& in )
    {
        std::basic_istream< E, Traits >::sentry ok( in );
        if( ok )
        {
            std::ios_base::iostate state = std::ios_base::goodbit;
            try
            {
                const std::ctype< E >& ctype_ = std::use_facet< std::ctype< E > >( in.getloc() );
                for( 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 );
                    if( ctype_.is( std::ctype_base::digit, c ) || (in.flags() & std::ios_base::showbase && (c == E('+') || c == E('-'))) )
                        break;
                }
            }
            catch(...)
            {
                state |= std::ios_base::badbit;
                if( in.exceptions() & std::ios_base::badbit )
                    throw;
            }
            in.setstate( state );
        }
        return in;
    }
    
    int main()
    {
        using namespace std;
        ifstream filestream("test.txt");
        filestream >> showbase; // mit showbase ist Vorzeichen erlaubt, ohne nicht
        for( int wert; filestream >> skip_until_number >> wert; )
            cout << wert << endl;
        cin.get();
        return 0;
    }
    

    Das 'showbase'-Flag habe ich zweckentfremdet, um mit/ohne Vorzeichen schaltbar zu machen.

    Gruß
    Werner



  • Du hast echt nen... gewöhnungsbedürftigen Stil, Werner. 😉



  • Werner Salomon schrieb:

    Hallo redrew99,
    wenn ich das richtig verstanden habe, so geht es doch darum, alles zu überlesen, was keine Zahl ist und anschließend die Zahl zu lesen. Unter der Voraussetzung, dass keine isolierten '-' (und '+'?) Zeichen in der Datei stehen, sollten auch Vorzeichen erlaubt sein, und das ganze sollte von außen schaltbar sein.

    Ja, genau. Wenn man schon so ein Programm schreibt, gehört meiner Ansicht nach ein "Schalter" für die Vorzeichen unbedingt mit rein, schließlich weiß man ja nie, ob man negative Zahlen braucht/nicht braucht.Vorstellbare Anwendungen für beides gibt es genug, z.B. Kontenstände(ja) oder Geburtsjahre(nein) auslesen.

    Was ich überhaupt nicht verstehe ist, warum die Standardfunktion so gehalten ist, daß bei

    #include <iostream>
    
    using namespace std;
    
    int main()
    {
      int wert;
      while (!(cin >> wert).eof())
        if (cin)
          cout << wert << '\n';
        else
          {
            cin.clear();
            cin.ignore(1);
          }
    }
    

    einer Eingabe von z.B. "+-798" "798", aber bei einer Eingabe von "++-798" "-798" ausgegeben wird. Imho ergibt das keinen Sinn.

    Werner Salomon schrieb:

    Hier mal ein Manipulator, der direkt an der streambuf-Schnittstelle die Zeichen abgreift. In Summe ist das natürlich mehr Code, aber in der Anwendung wird's dann wieder schnuckelig einfach.[cpp]

    Autsch. Ganz schöner Brocken.
    Davon verstehe ich so gut wie nichts(wegen der Templates), dass muss ich mir erstmal in Ruhe anschauen.
    Danke für den Code. 🙂
    Bin dann erstmal off, muss morgen früh raus.



  • redrew99 schrieb:

    Was ich überhaupt nicht verstehe ist, warum die Standardfunktion so gehalten ist, daß bei

    #include <iostream>
    
    using namespace std;
    
    int main()
    {
      int wert;
      while (!(cin >> wert).eof())
        if (cin)
          cout << wert << '\n';
        else
          {
            cin.clear();
            cin.ignore(1);
          }
    }
    

    einer Eingabe von z.B. "+-798" "798", aber bei einer Eingabe von "++-798" "-798" ausgegeben wird.

    Ich denke, es wird alles klarer, wenn du das Ganze mal so umschreibst und dann +-798 eingibst:

    int main()
    {
    	int wert;
    	while( !(cin >> wert).eof() )
    	{
    		if( !cin ) // !cin entspricht cin.fail()
    		{
            	cin.clear();
            	cin.ignore(1);
            }
            cout << "Inhalt der Variablen = " << wert << '\n';
    	}
    
    	/*
    		Schritt 1:
    			Du willst einen int einlesen.
    
    		Schritt 2:
    			Du gibst +-798 ein.
    
    		Schritt 3:
    			Das erste Zeichen der Sequence ist ein +. Alles OK, eine Zahl darf ein Vorzeichen haben.
    
    		Schritt 4:
    			Das zweite Zeichen der Sequence ist ein -. What? Eine Zahl kann immer nur 1 Vorzeichen haben. D.h. das - ist
    			ein nicht-numerisches Zeichen. Nun wird der Einlesevorgang abgebrochen, cin geht in einen Fehlerzustand über,
    			und die restlichen Zeichen (-798) bleiben im Puffer stecken. Es wurde also nur das + gelesen, und einfach nur + wird als +0 gehandhabt.
    			Die Variable hat also nun den Wert 0.
    
    		Schritt 5:
    			!cin ergibt nun true. Mit clear wird cin in seinen gültigen Zustand zurückgeführt und dann wird ein
    			im Puffer steckendes Zeichen (-) ignoriert. (ignorieren = das Zeichen lesen und verwerfen)
    
    		Schritt 6:
    			Die Variable mit dem Inhalt 0 wird ausgegeben.
    
    		Schritt 7:
    			Du willst einen int einelsen.
    
    		Schritt 8:
    			Im Puffer befinden sich noch die Zeichen 798. Alles OK, das ist eine positive Ganzzahl. Die Sequence wird gelesen und in der Variable abgespeichert.
    			Ein nicht-numerisches Zeichen gab es nicht, cin befindet sich also in keinem Fehlerzustand.
    
    		Schritt 9:
    			!cin ergibt false, da sich cin in keinem Fehlerzustand befindet.
    
    		Schritt 10:
    			Die Variable mit dem Inhalt 798 wird ausgegeben.
    
    		Nun geht es wieder bei Schritt 1 los.
    	*/
    
        return 0;
    }
    

    Ich hoffe, meine Erklärung hat dir geholfen.



  • Gugelmoser schrieb:

    Ich hoffe, meine Erklärung hat dir geholfen.

    Ja, hat sie, herzlichen Dank:-)
    Die fehlerhafte Ausgabe ist jetzt nachvollziehbar.

    Schritt 3:
    			Das erste Zeichen der Sequence ist ein +. Alles OK, eine Zahl darf ein Vorzeichen haben.
    
    		Schritt 4:
    			Das zweite Zeichen der Sequence ist ein -. What? Eine Zahl kann immer nur 1 Vorzeichen haben. D.h. das - ist
    			ein nicht-numerisches Zeichen. Nun wird der Einlesevorgang abgebrochen, cin geht in einen Fehlerzustand über,
    			und die restlichen Zeichen (-798) bleiben im Puffer stecken. Es wurde also nur das + gelesen, und einfach nur + wird als +0 gehandhabt.
    

    Mit der Programmabfolge ist imho klar, daß die benutzten Standardfunktionen nicht wirklich dafür geeignet sind, Zahlen aus einem Stream zu filtern, denn wer kann denn schon garantieren, daß in einer Datei vor einer negativen Zahl nicht zufällig ein "+" steht. Und das natürlich nur, sofern negative Zahlen überhaupt vorkommen dürfen.


  • Mod

    redrew99 schrieb:

    Mit der Programmabfolge ist imho klar, daß die benutzten Standardfunktionen nicht wirklich dafür geeignet sind, Zahlen aus einem Stream zu filtern, denn wer kann denn schon garantieren, daß in einer Datei vor einer negativen Zahl nicht zufällig ein "+" steht. Und das natürlich nur, sofern negative Zahlen überhaupt vorkommen dürfen.

    Wobei dich nichts daran hindert, dein eigenes num_get-facet zu schreiben und zu verwenden.



  • Man könnte neg. Zahlen auch einfach mit -1 multiplizieren. Allerdings finde ich auch, wenn z.B. in einer Datei "Geburtsjahre" neg. Zahlen vorkommen, dann sollte man eher an der Datei ansetzen, anstatt am Programm.



  • Werner Salomon schrieb:

    Hier mal ein Manipulator, der direkt an der streambuf-Schnittstelle die Zeichen abgreift. In Summe ist das natürlich mehr Code, aber in der Anwendung wird's dann wieder schnuckelig einfach.Werner

    Wenn ich das richtig sehe, müßte in Zeile 8 und 15 noch ein "typename" gesetzt werden, sonst kompiliert der Code nicht.

    typename std::basic_istream< E, Traits >::sentry ok( in);
    ...
    for( typename Traits::int_type m = in.rdbuf()->sgetc(); ; m = in.rdbuf()->snextc() )
    ...
    


  • Und da soll mir noch ein einziges mal jemand widersprechen, MSVC wäre so korrekt mit Templates. -.-



  • Werner Salomon schrieb:

    Unter der Voraussetzung, dass keine isolierten '-' (und '+'?) Zeichen in der Datei stehen, Gruß Werner

    Sry, aber das ist nicht optimal, denn ein "§$!56 zp-,7789" sollte korrekt ausgelesen werden können. Aktuelle Ausgabe wäre "56".
    Das Problem kann man aber relativ einfach lösen, indem man das Zeichen nach einem "+/-" abfragt und je nachdem, ob Zahl oder nicht,dann halt weiterverfährt.



  • redrew99 schrieb:

    Wenn ich das richtig sehe, müßte in Zeile 8 und 15 noch ein "typename" gesetzt werden, sonst kompiliert der Code nicht.

    typename std::basic_istream< E, Traits >::sentry ok( in);
    ...
    for( typename Traits::int_type m = in.rdbuf()->sgetc(); ; m = in.rdbuf()->snextc() )
    ...
    

    Hallo redrew99,

    Natürlich habe ich den Code vorher mal durch den Compiler geschickt, aber Du hast Recht, das Visual Studio, welches ich verwende, ist da recht großzügig.

    Gruß
    Werner



  • Möchte ich nochmal festhalten: MSVC ist, was Templates angeht kaputt. Werner gibt mir Recht.

    ICH HABE RECHT!



  • 314159265358979 schrieb:

    Möchte ich nochmal festhalten: MSVC ist, was Templates angeht kaputt.

    Ja, Visual C++ hat einige Bugs im Bereich Templates. "Ist kaputt" ist aber vielleicht etwas übertrieben, obwohl das natürlich Ansichtssache ist.

    Werner gibt mir Recht.

    ICH HABE RECHT!

    Ganz ruhig, Mami kommt gleich.



  • Mami ist tot. <.<


  • Mod

    Hier mal ein Entwurf mit einem eigenen num_get-facet:

    #include <iostream>
    #include <locale>
    
    class my_num_get : public std::num_get<char>
    {
    private:
        template <typename T>
        iter_type do_do_get(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, T& v) const
        {
            bool negative = false;
            for ( ; in != end; ++in )
            {
                auto c = *in;
                if ( c == '+' )
                    negative = false;
                else if ( c == '-' )
                    negative = true;
                else
                    break;
            }
            auto res = std::num_get<char>::do_get( in, end, str, err, v );
            if ( negative )
                v = -v;
            return res;
        }
    
        virtual iter_type do_get(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, long& v) const override { return do_do_get( in, end, str, err, v ); }
        virtual iter_type do_get(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, long long& v) const override { return do_do_get( in, end, str, err, v ); }
        virtual iter_type do_get(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, unsigned short& v) const override { return do_do_get( in, end, str, err, v ); }
        virtual iter_type do_get(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, unsigned& v) const override { return do_do_get( in, end, str, err, v ); }
        virtual iter_type do_get(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, unsigned long& v) const override { return do_do_get( in, end, str, err, v ); }
        virtual iter_type do_get(iter_type in, iter_type end, std::ios_base& str, std::ios_base::iostate& err, unsigned long long& v) const override { return do_do_get( in, end, str, err, v ); }
    };
    
    int main()
    {
        using namespace std;
        cin.imbue(locale(locale(""), new my_num_get()));;
        long long wert;
        for (;;)
        {
            cin >> wert;
            if (cin.eof())
                break;
            if (cin)
                cout << wert << '\n';
            else
                {
                    cin.clear();
                    cin.ignore(1);
                }
        }
    }
    


  • Mit Makros wärs besser.


Anmelden zum Antworten