Einlesen, stoppen und Einlesen



  • Satan schrieb:

    @Nathan: Was ist mit "1 : 2" (beachte das Leerzeichen)?

    Die Leerzeichen werden hier vom intern erzeugten std::istream::sentry Objekt bei allen formatierten und unformatierten Extraktionen entfernt - solange dass skipws Flag gesetzt ist (was es standardmäßig ist).
    Kurz: Die werden ignoriert.



  • Sone schrieb:

    die C++-Iostreams machen das schon ordentlich

    Jetzt mach bitte noch den Check, dass jede Zeile vom Typ "<Leerzeichen><Zahl><Leerzeichen>:<Leerzeichen><Zahl><Leerzeichen>" sein muss, also insb. "1\n:2" nicht erlaubt wird.

    Sone schrieb:

    Satan schrieb:

    @Nathan: Was ist mit "1 : 2" (beachte das Leerzeichen)?

    Die Leerzeichen werden hier vom intern erzeugten std::istream::sentry Objekt bei allen formatierten und unformatierten Extraktionen entfernt - solange dass skipws Flag gesetzt ist (was es standardmäßig ist).
    Kurz: Die werden ignoriert.

    Nur schade, dass das sentry bei istream.get() auf noskipws gesetzt wird.
    Kurz: Die werden ausgelesen.



  • Sone schrieb:

    Satan schrieb:

    @Nathan: Was ist mit "1 : 2" (beachte das Leerzeichen)?

    Die Leerzeichen werden hier vom intern erzeugten std::istream::sentry Objekt bei allen formatierten und unformatierten Extraktionen entfernt - solange dass skipws Flag gesetzt ist (was es standardmäßig ist).
    Kurz: Die werden ignoriert.

    int main()
    {
    	string test( "1: 2" );
    
    	istringstream file( test );
    
    	unsigned int val1, val2;
    
    	while (file >> val1 && file.get() == ':' && file >> val2)
    	{
    		std::cout << val1 << ' ' << val2 << '\n';
    	}
    }
    


  • @out: Schwer von Begriff? Nimm bitte mal string test( "1 :2" );



  • Sone schrieb:

    if( std::istream::traits_type::to_char_type(is.get()) != C )
    

    Wieso muss bei dir eigentlich alles immer so kompliziert aussehen?

    Satan schrieb:

    @out: Schwer von Begriff? Nimm bitte mal string test( "1 :2" );

    Was damit? Das geht nicht, hab ich doch gesagt?



  • Danke [an alle, insbesondere Nathan].
    Eigentlich habe ich mich verschrieben beim Format:
    statt N: X meine ich N:X, ist aber ganz egal.
    Container etc.. habe ich noch nicht kennengelernt, kommt aber bald.
    Jetzt habe ich mal Verständnisfragen:

    unsigned int val1, val2;
    while (file >> val1 && file.get() == ':' && file >> val2)
    {
        std::cout << val1 << ' ' << val2 << '\n';
    }
    

    Im Schleifenkopf von While kommt nur ne Bedingung,
    ich versteht nicht was die Bedingung prüft:

    while (file >> val1 && file.get() == ':' && file >> val2)
    

    ">>" ist der "rechtsverschiebungsoperator", der den linken operanden um den wert vom rechten operanden nach rechts im RAM verschiebt?
    und

    file.get() == ':'
    

    prüft, ob mein ifstream-Objekt ein : eingelesen hat?



  • @out: Sorry, hab nicht richtig gelesen.

    Btw,

    if( std::istream::traits_type::to_char_type(is.get()) != C )
    

    ist völlig verkehrt. Man müsste C zu einem int_type promoten und dann eq_int_type aufrufen.



  • Satan schrieb:

    @out: Sorry, hab nicht richtig gelesen.

    Ok (sollte für sone sein, damit er es kopieren kann und sieht, dass es nicht geht)

    file >> val1
    

    Nein, mit "rechtschieben" hat das hier nichts zu tun. Das hier ist der "extraction operator". Hierbei wird eine Ganzzahl eingelesen und sofort darauf wird geprüft, ob das einlesen erfolgreich war (2 Fliegen mit einer Klatsche schlagen). Dieser Operator verwirft standardmäßig Whitespaces.

    file.get()
    

    Liest einfach das nächste im Stream stehende Zeichen. (z.B. Leerzeichen oder Doppelpunkt, je nachdem, was eben das nächste Zeichen ist)



  • Satan schrieb:

    @Nathan: Was ist mit "1 : 2" (beachte das Leerzeichen)?

    Das Format war N:X, wie der TE später korrigierte.
    Bei einem Format N:X erwarte ich auch nur Zahl, Zeichen ':', Zahl. Notfalls schiebt man noch ws zwischen.



  • Satan schrieb:

    Nur schade, dass das sentry bei istream.get() auf noskipws gesetzt wird.
    Kurz: Die werden ausgelesen.

    Na jut. Stimmt, das hätte ich wissen müssen - ist schließlich unformatiert. Hatte in Erinnerung dass es eben erst das nächste "signifikante" Zeichen ausliest.

    Ich weiß gar nicht, wieso ich überhaupt Traits benutzt hab'. Hab beim schreiben noch gedacht, ich hab den char-Typ generisch gemacht - hab ich gar nicht... (?)
    Daher etwas einfacher:

    #include <iostream>
    #include <vector>
    #include <utility>
    #include <sstream>
    
    template<char C>
    class Char
    {
        friend std::istream& operator>>( std::istream& is, Char const& c )
        {
            if( is.get() != C )
                is.setstate( std::ios_base::failbit );
    
            return is;
        }
    };
    
    int main()
    {
        std::vector<std::pair<unsigned, unsigned>> mData;
    
        std::istringstream stream("4 : 5\n"
                                  "78:   9");
    
        unsigned a, b;
        while( stream >> a >> std::ws >> Char<':'>() >> b )
            mData.push_back({a, b});
    
        for( auto const& p : mData )
            std::cout << p.first << ':' << p.second << '\n';
    }
    

    Soll das Format tatsächlich so akribisch genau geparst werden, muss man halt nur die Leerzeichen entfernen.
    Das ginge so:

    std::istream& spaces( std::istream& is )
    {
        std::istream::sentry s(is, true);
        if( s )
            while( is.rdbuf()->sgetc() == ' ' )
                is.rdbuf()->sbumpc();
    
        return is;
    }
    
    // ....
        unsigned a, b;
        while( stream >> std::noskipws >> a >> spaces >> Char<':'>() >> spaces >> std::noskipws >>  b )
        {
            mData.push_back({a, b});
            stream >> Char<'\n'>();
        }
    


  • Satan schrieb:

    Btw,

    if( std::istream::traits_type::to_char_type(is.get()) != C )
    

    ist völlig verkehrt. Man müsste C zu einem int_type promoten und dann eq_int_type aufrufen.

    Ne, es ist - wie ich bei einem überraschten zweiten Blick sah - überflüssiger Blödsinn. Man kann natürlich direkt vergleichen. Ich dachte wie gesagt, ich hätte Char vom Zeichentyp unabhängig gemacht, da wären Traits vielleicht notwendig gewesen.



  • Ich habe mich wieder informiert, doch nichts verstädnlich/nützliches gefunden.
    gibt es einen paramter bei getline, oder eine methode bei ifstream, die es mir erlaubt nur duie letzte zeile auszulesen.
    ich habe auch schon die zeilenanzahl ausgelesen.



  • Mapper schrieb:

    gibt es einen paramter bei getline, oder eine methode bei ifstream, die es mir erlaubt nur duie letzte zeile auszulesen.

    Wenn du die Zeilenzahl hast, und bis zur letzten Zeile vorrücken willst, dann nimm istream::ignore .
    Von da wie gewohnt lesen.



  • Ich habe hier mal einen ganz anderen Weg, um die letzte Zeile einer Datei zu lesen:

    int main()
    {
    	string test( "abc\ndef\nghi\nhallo welt!" );
    	istringstream file( test );
    	string last_line;
    
    	file.seekg( 0, std::ios_base::end );
    	file.unget();
    	for( char c=0; file>>std::noskipws>>c && c!='\n'; last_line.push_back(c) )
    	{
    		file.unget();
    		file.unget();
    	}
    	std::reverse( last_line.begin(), last_line.end() );
    	cout << last_line << '\n';
    }
    

    Was haltet ihr davon? Ist das ineffizient? Falls ja, was wäre ein effizienter Weg, die letzte Zeile zu lesen?



  • Ich hätte soetwas verwendet:

    #include <iostream>
    #include <string>
    
    int main()
    {
        std::istream file(std::cin.rdbuf());
    
        std::string last_line;
        while (std::getline(file, last_line))
            ;
        std::cout << last_line;
    }
    

    Aber ich glaube, das ist zu banal. :p
    Außerdem ist outs Lösung effizienter.

    Edit: Das reverse Lesen ist aber schon ein wenig unhandlich. Sone, schreib' mal nen streambuf der rückwärts liest. 😉



  • #include <iostream>
    #include <sstream>
    #include <iterator>
    
    int main()
    {
        std::istringstream file( "abc\nbcd\nhallo welt!" );
    
        file.seekg( 0, std::ios_base::end );
    
        char c;
        do c = file.rdbuf()->sungetc();
        while( c != '\n' && c != std::char_traits<char>::eof() );
    
        std::string last_line;
    
        if( c == '\n' )
            last_line.assign( ++std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>() );
    
        std::cout << last_line << '\n';
    }
    

    🤡

    Edit:

    Nathan schrieb:

    Sone, schreib' mal nen streambuf der rückwärts liest. 😉

    Ohne Witz, ich hab das nicht gelesen! Ich hab das gerade erst entdeckt! 😃



  • Sowas wie getline magste nicht, oder?



  • Nathan schrieb:

    Sowas wie getline magste nicht, oder?

    Ich?
    Doch.



  • Die Zeile ersetzen:

    if( c == '\n' )
            std::getline( file.ignore(), last_line, char(EOF) );
    

    Edit: Zur Konformität nehm' ich mal EOF.



  • Genau das meinte ich.
    Hat das einen besonderen Grund warum du sungetc() verwendest anstatt ungetc des Streams?
    Edit: Und dein file.seekg() kannste durch ignore ersetzen, weniger zu schreiben.


Anmelden zum Antworten