Einlesen, stoppen und Einlesen



  • 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.



  • Nathan schrieb:

    Edit: Und dein file.seekg() kannste durch ignore ersetzen, weniger zu schreiben.

    Wirklich. Ohne Witz. Hab ich gerade gemacht, da sehe ich deine Antwort.

    ungetc kannte ich nicht. 👍

    Edit: Was für ein ungetc? 😕



  • Klar kannte ich das noch nicht -
    das gibt es ja auch gar nicht.



  • Sone schrieb:

    char(EOF)

    Also echt, du hast ja gar keine Ahnung ...

    Es gibt einen Grund, dass getchar() int und nicht char zurückgibt.
    Es gibt einen Grund, dass es einen char_type und einen int_type gibt.
    Es gibt einen Grund, dass EOF kein gültiger unsigned char ist.
    Versuche mal, dahinter zu kommen.



  • Sone schrieb:

    Klar kannte ich das noch nicht -
    das gibt es ja auch gar nicht.

    Sorry, meinte unget() von std::basic_istream.



  • Satan: Soll das ein Scherz sein?
    Ich kann nicht einfach EOF schreiben, das geht nicht. Da gibt es Fehler bei der Deduktion des Arguments. Und die template-parameter-list explizit hinzuschreiben würde den gleichen Cast erzwingen.
    Soll ich jetzt
    std::char_traits<char>::to_char_type( std::char_traits<char>::eof() )
    schreiben, wenn ich einfach EOF als char haben möchte?

    Nathan schrieb:

    Sone schrieb:

    Klar kannte ich das noch nicht -
    das gibt es ja auch gar nicht.

    Sorry, meinte unget() von std::basic_istream.

    Aber dann ist die Version nicht so elegant wie durch einen Methodenaufruf.
    file.unget().peek() vs file.rdbuf()->sungetc() .

    Edit: to_char_type natürlich.


Anmelden zum Antworten