getline - string einlesen


  • Mod

    Ethon schrieb:

    Kann ich nicht reproduzieren.

    Versuch mal eine Datei, deren letzte Zeile nicht auf einem Newline endet. Oder probier einen anderen Delimiter. Apropos, sollte dieser als Defaultwert ein Newline haben.



  • Stimmt, man könnte aber im Vorfeld prüfen, ob das letzte Zeichen ein Zeilenumbruch ist, falls nicht, fügt man halt eins ein.

    Oder man prüft einfach nur, ob das Ende der Datei erreicht ist.
    Falls ja, wird der String doch noch ausgegeben bzw. die Zeichen der letzten Zeile dort gespeichert.

    Bin dann off für heute.



  • kann man das eigtl eleganter implementieren? gefällt mir nicht so recht.

    std::istream& getline(std::istream& in, std::string& out, char delim = '\n')
    {
      out.clear();
    
      {
        char to_add;
        while(in.get(to_add))
        {
          if(to_add == delim)
            return in;
    
          out.push_back(to_add);
        };
      }
    
      return in;
    }
    

    bb



  • Du hast denselben Bug wie Ethon. Ich denke, dass du beim ersten Erreichen von eof das Flag wieder zurücksetzen musst.



  • richtig - nachdenken 4tw -.-'
    wollts eigtl noch testen, aber da hat mir dann doch iwie die lust gefehlt 😃

    std::istream& getline(std::istream& in, std::string& out, char delim = '\n')
    {
      out.clear();
    
      if(!in.eof())
      {
        char to_add;
        while(in.get(to_add))
        {
          if(to_add == delim)
            return in;
    
          out.push_back(to_add);
        };
    
        in.setstate(in.rdstat() & ~std::ios::eof);
      }
    
      return in;
    }
    


  • Ethons Code funktioniert, auch wenn man am Ende einer Datei den Zeilenumbruch entfernt.
    Allerdings verstehe ich nicht, was

    for(int c = in.get(); in; c = in.get())
    

    da genau passiert. Mir ist klar, daß der Stream gelesen wird, aber nicht, wie.
    Was bedeutet

    int c=in.get();
    

    ?
    und müßte man nicht schreiben

    in.good()
    

    statt

    in
    

    ?



  • redrew99 schrieb:

    Was bedeutet int c=in.get(); ? [...]

    in.get() gibt ein Zeichen aus dem Stream zurück.

    redrew99 schrieb:

    [...] und müßte man nicht schreiben in.good() statt in ?

    Nein, müsste man nicht. Das Prüfen auf failbit und badbit erledigt der "Convert-to-Pointer"-Operator ( operator void *() ) aus ios .



  • SeppJ schrieb:

    Ist das so eine Art Wettbewerb wie es immer schlechter geht?

    Keine Ahnung. Aber wenn's so ist, dann darf ich wenigstens auch mitmachen 😉



  • redrew99 schrieb:

    Ethons Code funktioniert, auch wenn man am Ende einer Datei den Zeilenumbruch entfernt.

    Das kann ich mir ehrlich gesagt nicht vorstellen. Wird dann auch die letzte Zeile verarbeitet bei folgendem Code?

    string line;
    while(getline(in, line))
        cout << line << endl;
    


  • Nope. Einerseits könnte man argumentieren dass eine Zeile ohne Zeilenende kein gültiges Token ist. Andererseits ist das in der Praxis Mist. 😉



  • Also dieser Code (die Funktion ist von Ethan) funktioniert:

    #include <iostream>
    #include <string>
    #include <fstream>
    
    using namespace std;
    
    std::istream& getline(std::istream& in, std::string& out, char delim)
    {
        out.clear();
        for(int c = in.get(); in; c = in.get())
    
        {
            if(c != delim)
                out += c;
            else
                break;
        }
    
        return in;
    }
    
    int main()
    {
        string s;
        ifstream filestream("test.txt");
        for(;filestream.good();)
        {
          getline(filestream,s,'\n');
          cout<<s<<endl;
        }
        return 0;
    }
    


  • Swordfish schrieb:

    redrew99 schrieb:

    Was bedeutet int c=in.get(); ? [...]

    in.get() gibt ein Zeichen aus dem Stream zurück.

    Das verstehe ich, aber wie funktioniert das, daß ein Zeichen, also in der Regel keine Ziffer, einem Int zugewiesen wird?

    Oder muß man die Funktion so verstehen, daß nicht ein Zeichen zurückgegeben wird, sondern die Position des Zeichens?



  • redrew99 schrieb:

    Swordfish schrieb:

    redrew99 schrieb:

    Was bedeutet int c=in.get(); ? [...]

    in.get() gibt ein Zeichen aus dem Stream zurück.

    Das verstehe ich, aber wie funktioniert das, daß ein Zeichen, also in der Regel keine Ziffer, einem Int zugewiesen wird?

    Schon mal was von ASCII/ANSI-Code gehört? Und dass es überhaupt keinen Unterschied macht, ob man den in einem char oder einem int oder wasweißich speichert?
    char ist insofern für Zeichen im ANSI-Code geeignet, da einerseits eine überladung des operator<< für ihn existiert, die seinen Numerischen Wert (z.B. 65) als ANSI-Code für das entsprechende Zeichen ('A') interpretiert und dieses dann in den Stream schreibt, andererseits, weil seine Größe dafür passt (1 Byte = 256 verschiedene "Codes" oder Zahlen => 256 Zeichen => ANSI).



  • redrew99 schrieb:

    Also dieser Code (die Funktion ist von Ethan) funktioniert:

    Kommt drauf an, was man unter "funktioniert" versteht. Denn das fail-Bit wird gesetzt, obwohl nur das eof-Bit gesetzt werden sollte. Wenn man wie ich bei fail- und bad-Bit Exceptions werfen lässt, fliegt einem das Programm um die Ohren, obwohl alles korrekt abgelaufen ist. Wenn man while(getline(...)) benutzt, fehlt die letzte Zeile, weil operator void*() nicht auf eof prüft.

    Hacker: Das geht auch freundlicher.



  • Tut mir leid, passiert mir immer wieder 😞



  • redrew99 schrieb:

    Swordfish schrieb:

    redrew99 schrieb:

    Was bedeutet int c=in.get(); ? [...]

    in.get() gibt ein Zeichen aus dem Stream zurück.

    Das verstehe ich, aber wie funktioniert das, daß ein Zeichen, also in der Regel keine Ziffer, einem Int zugewiesen wird?

    In Wirklichkeit werden alle Werte sowieso als bits representiert. get() gibt einen int zurück, damit ggf. auch EOF zurückgegeben werden kann.

    redrew99 schrieb:

    Oder muß man die Funktion so verstehen, daß nicht ein Zeichen zurückgegeben wird, sondern die Position des Zeichens?

    Njet.



  • unskilled schrieb:

    wollts eigtl noch testen, aber da hat mir dann doch iwie die lust gefehlt 😃

    dann liefere ich Dir dafür eine Testumgebung. Einfach in dem namespace test die eigene getline-Funktion einfügen, und das Programm übersetzen und ausführen:

    #include <iostream>
    #include <string>
    #include <sstream>
    #include <streambuf>
    #include <cassert>
    #include <ctime>
    
    namespace test
    {
        // hier das getline einfügen
    }
    
    //  .. oder obiges auskommentieren und folgende Zeile aktivieren, als Referenz sozusagen
    //namespace test = std;
    
    class BigBuf : public std::streambuf
    {
        static std::size_t const Len_of_a_line = 56;
    public:
        explicit BigBuf( int kbyte = 100 ) :m_i(0),  m_n(kbyte*1000)
        {
            for( char* p = line; p != line+Len_of_a_line; ++p )
                *p = '.';
            line[Len_of_a_line-1] = '\n';
            setg( line, line, line+Len_of_a_line );
        }
        bool empty() const { return m_i >= m_n; }
    protected:
        int_type underflow()
        {
            if( (m_i += Len_of_a_line) >= m_n )
                return traits_type::eof();
            setg( line, line, line+Len_of_a_line );
            return traits_type::to_int_type( *line );
        }
    private:
        char line[Len_of_a_line];
        std::size_t m_i, m_n;
    };
    
    int do_main()
    {
        using std::istringstream;
        using std::cout;
        using std::endl;
    
        std::string s = "irgend etwas";
        // --   getline testsuite
        {
            istringstream in("a b\nX");
            test::getline( in, s );
            assert( in.good() );
            assert( s == "a b" ); // hier passt redrew99 Version 2
            assert( in.get() == 'X' );
        }
        {
            istringstream in("a b\n");
            test::getline( in, s );
            assert( in.good() );
            assert( s == "a b" );
            assert( in.get() == std::istream::traits_type::eof() );
        }
        {
            istringstream in("a b");
            test::getline( in, s ); // aus dieser Funktion kommt redrew99s Code Version 1 nie wieder heraus
            assert( !in.fail() ); // dieser Test wird weder von Ethon noch von unskilled passiert
            assert( in.eof() );
            assert( s == "a b" );
        }
        {
            istringstream in("x");
            char c; in >> c;
            test::getline( in, s );
            assert( in.fail() );
            assert( in.eof() );
        }
        {
            istringstream in("x");
            in >> s;
            test::getline( in, s );
            assert( in.fail() );
            assert( in.eof() );
        }
        {
            istringstream in("egal");
            in.exceptions( std::ios_base::failbit );
            test::getline( in, s );
            // hier darf keine Exception geworfen werden
        }
        {
            // -- Performance Test (natürlich im Release-Mode auszuführen)
            std::clock_t const start = std::clock();
            {
                BigBuf buf(10000);
                std::istream in(&buf);
                while( test::getline( in, s ) )
                    ;
                if( !buf.empty() )
                    std::cerr << "Lesefehler" << endl;
            }
            std::clock_t const duration = std::clock() - start;
            cout << "Dauer: " << double(duration)/CLOCKS_PER_SEC << "s" << endl;
        }
        cout << "Test Ende " << endl;
        std::cin.get();
        return 0;
    }
    
    int main()
    {
        try
        {
            return do_main();
        }
        catch( const std::exception& ex )
        {
            std::cerr << "Exception: " << ex.what() << std::endl;
        }
        return -1;
    }
    

    Gruß
    Werner



  • Warum startest du in Zeile 73 nicht direkt mit einem leeren String?



  • Michael E. schrieb:

    Denn das fail-Bit wird gesetzt, obwohl nur das eof-Bit gesetzt werden sollte.

    Habe mal im I-net recherchiert. Demnach müssen beide Bits gesetzt werden, da
    eofbit erst nach dem Versuch gesetzt wird, nach dem letzten Zeichen noch ein weiteres zu lesen. Da dieses dann nicht vorhanden ist, wird auch failbit gesetzt.

    Das Setzen des Failbits könnte man evtl. mit

    in.clear(ios::goodbit);
    

    korrigieren?



  • redrew99 schrieb:

    Habe mal im I-net recherchiert. Demnach müssen beide Bits gesetzt werden, da
    eofbit erst nach dem Versuch gesetzt wird, nach dem letzten Zeichen noch ein weiteres zu lesen. Da dieses dann nicht vorhanden ist, wird auch failbit gesetzt.

    C++98 §21.3.7.9.6-9 sagt, dass das Failbit außerhalb des istream::sentry-Hilfsobjekts genau dann gesetzt wird, wenn der Zielstring zu klein ist oder kein Zeichen extrahiert wurde. Dementsprechend stimmt deine Aussage nicht.


Anmelden zum Antworten