Frage zu Übungsaufgabe mit string



  • Hallo an alle,
    kurze Frage ich möchte in einem String jedes 3te Wort ausgegeben bekommen. Also in etwa so
    Eingabe: Tell the truth, Nobody will do Anything.
    Ausgabe: Tell nobody Anything.

    die idee ist es das jedes 3te (' ') das wort danach ausgibt. Aber wie mach ich das?



    1. Du gibst solange Zeichen aus, bis du auf das Stringende oder ein Leerzeichen triffst
    2. Du überspringst so lange Zeichen, bis du
      a) zwei Leerzeichen übersprungen hast => weiter bei 1)
      b) du das Stringende erreicht hast => fertig


  • @KarlSr Du machst ungenaue Angaben.

    Einmal soll jedes dritte Wort ausgegeben werden aber laut deiner Angabe soll Tell als erstes vorkommen.

    Und ich glaube auch nicht, dass du nach jedem dritten Leerzeichen an genau jedes dritte Wort kommst.

    Die Ausgabe sollte für mich "truth" und "do" sein.



  • Woher kommt deine Eingabe? Wenn sie aus einem Eingabestrom kommt, dann trennt >> bereits an Leerräumen. Der Code sieht dann also so in etwa aus:

    std::stringstream sentence("Tell the truth, Nobody will do Anything.");
    std::string word;
    int i=0;
    while (sentence >> word) {  //solange noch ein Wort vorhanden ist, lies es in "wort" ein
        if (i++ % 3 == 0) {  // wenn "jedes dritte Wort" erfüllt ist
            std::cout << word << " ";
        }
    }
    


  • Tja, das mit jedem dritten Wort habe ich halt wortwörtlich genommen.

    Aber weil der @KarlSr sich eh nicht mehr drum schert, hier meine beiden Versionen, einmal mit Streams, dann mit Strings. Vielleicht lerne ich ja noch wenigstens etwas dazu.

    #include <iostream>
    #include <string>
    
    
    void print_nth_word(std::istream& in, std::size_t nth){
        std::string word;
        std::size_t n{};
    
        while(in >> word){
            if(++n % nth == 0)
                std::cout << word << '\n';
        }
    }
    
    
    void print_nth_word(const std::string& s, std::size_t nth){
        std::string word;
        std::size_t n{};
    
       for(auto it = s.begin(); it < s.end(); ++it){
            while(it < s.end() && *it == ' ')
                ++it;
    
            if(it == s.end())
                return;
    
            word.clear();
    
            while(it < s.end() && *it != ' ')
                word.push_back(*it++);
    
            if(++n % nth == 0)
                std::cout << word << '\n';
        }
    }
    

    EDIT: https://ideone.com/PYK3GV

    EDIT2: Was den String-Teil angeht: Noch interessanter finde ich es dann noch ohne die Verwendung eines Wort-Zwischenspeichers:

    void print_nth_word(const std::string& s, std::size_t nth){
        std::size_t n{};
    
       for(auto it = s.begin(); it < s.end(); ++it){
            while(it < s.end() && *it == ' ')
                ++it;
    
            if(it == s.end())
                return;
    
            if(++n % nth == 0){
                while(it < s.end() && *it != ' ')
                    std::cout << *it++;
    
                std::cout << '\n';
            }
    
            else{
                while(it < s.end() && *it != ' ')
                    ++it;
            }
        }
    }
    


  • @KarlSr sagte in Frage zu Übungsaufgabe mit string:

    die idee ist es das jedes 3te (' ') das wort danach ausgibt. Aber wie mach ich das?

    Mehr oder weniger wörtlich genommen:

    #include <stdio.h>
    #include <string>
    
    int main() {
        std::string str("Tell the truth, Nobody will do Anything."); // sic
        int w = 0;
        for (char ch : str) {
            w += (ch == ' ');
            if ((w % 3) == 0)
                putc(ch & 0xFF, stdout);
        }
    }
    

    Das gibt, beginnend mit dem ersten, jedes 3. Wort aus. Allerdings nur so lange die Worte alle mit genau einem Leerzeichen getrennt sind. Andere Zeichen wie Tab funktionieren nicht, und wenn 2 Leerzeichen hintereinander stehen wird das als "leeres Wort" gewertet.

    Wenn du mehrere aufeinanderfolgende Leerzeichen als einen einzigen Wort-Trenner behandeln willst, dann ist die Löstung mit dem stringstream einfacher.



  • Naja wenn man bedenkt, dass da bei den String Versionen wohl irgendwann ein Pufferüberlauf vorkommt der sich vermutlich nicht mehr gerade Teilen lässt.

    Aber wen schert das schon. Außer @KarlSr



  • @Großmaul sagte in Frage zu Übungsaufgabe mit string:

    Naja wenn man bedenkt, dass da bei den String Versionen wohl irgendwann ein Pufferüberlauf vorkommt der sich vermutlich nicht mehr gerade Teilen lässt.

    Hä?


  • Mod

    Oder C++23. https://coliru.stacked-crooked.com/a/9f35f35ba3eefc4b

    string str("Tell the truth, Nobody will do Anything."); // sic
    auto s = views::split(str, ' ') | views::filter([i=0] (auto) mutable {return i++ % 3 == 0;});
    ranges::copy(s, ostream_iterator<string_view>{cout, " "});
    

    Geht auch noch prägnanter mit join_with. Haben wir aber noch nicht.


  • Mod

    Wobei mir gerade auffaellt, dass eben die erwaehnte stringstream Methode doch fast sexier ist:

    istringstream sentence("Tell the truth, Nobody will do Anything.");
    copy_if(istream_iterator<string>(sentence), {}, ostream_iterator<string>(cout, " "), 
            [i=0] (auto) mutable {return i++ % 3 == 0;});
    

    (Wobei man eben auch den Input in Form eines Streams haben sollte)
    Wenn man lustig ist, kann man auch ein eigenes Facet zum ignorieren von Satzzeichen und Zeilenumbruechen in den Stream einfuegen.



  • @hustbaer sagte in Frage zu Übungsaufgabe mit string:

    Hä?

    Naja, damit meine ich halt, dass bei meiner Version der Zähler irgendwann einmal überlauft und ob das wieder genau so teilbar ist wenn das Ding wieder bei der Null ankommt ist mir nicht schlüssig.

    @Columbo bei deinen Examples sieht mans. Ich bin ein verrosteter alter C++-Programmierer.

    EDIT: @hustbaer bei deinem Example seh ich auch wie du irgendwas immer weiter aufaddierst aber den Anker dazu find ich bei dir nicht. Hierzu also ein herzliches Hä zu deinem Hä.

    MfG



  • @Großmaul

    Naja, das ist ein sehr theoretischer Fall. Nimm nen std::uint64_t dann biste um Größenordnungen weiter.



  • @DocShoe Ja, naja, machen wir es halt so, dass es noch auf der Beerdigung läuft ... 😉

    EDIT: Dann würde ich @KarlSr noch freundlich fragen ob das Ding noch laufen soll wenn er tot ist 😂

    Aber ich glaube wir haben ihm hier schon genug Weisheiten auf den Teller drittes Wort.


  • Mod

    @Großmaul sagte in Frage zu Übungsaufgabe mit string:

    Hä?

    Naja, damit meine ich halt, dass bei meiner Version der Zähler irgendwann einmal überlauft und ob das wieder genau so teilbar ist wenn das Ding wieder bei der Null ankommt ist mir nicht schlüssig.

    Das ist auch unmoeglich, weil ja die groesste positive repraesentierbare Zahl im two's complement entweder durch drei teilbar ist, in welchem Fall die uebernaechste additive Gegenzahl ebenfalls durch drei teilbar waere, oder nicht, in welchem Fall drei Zahlen in Folge nicht durch drei teilbar sind (weil ja auch 32N3 \nmid -2^N).

    Aber trotzdem etwas laecherlich so etwas zu bemaengeln, wenn man beruecksichtigt, dass bei einem String der 2^31 Worte umfasst, wir schon fast die 3.9 Milliarden Worte in der englischen Wikipedia sprengen. An welchem Punkt andere Nuancen wie korrekte Behandlung von Leerraum etc. sowieso zu behandeln waeren.



  • @Columbo sagte in Frage zu Übungsaufgabe mit string:

    @Großmaul sagte in Frage zu Übungsaufgabe mit string:

    Hä?

    Naja, damit meine ich halt, dass bei meiner Version der Zähler irgendwann einmal überlauft und ob das wieder genau so teilbar ist wenn das Ding wieder bei der Null ankommt ist mir nicht schlüssig.

    Das ist auch unmoeglich, weil ja die groesste positive repraesentierbare Zahl im two's complement entweder durch drei teilbar ist, in welchem Fall die uebernaechste additive Gegenzahl ebenfalls durch drei teilbar waere, oder nicht, in welchem Fall drei Zahlen in Folge nicht durch drei teilbar sind (weil ja auch 32N3 \nmid -2^N).

    Aber trotzdem etwas laecherlich so etwas zu bemaengeln, wenn man beruecksichtigt, dass bei einem String der 2^31 Worte umfasst, wir schon fast die 3.9 Milliarden Worte in der englischen Wikipedia sprengen. An welchem Punkt andere Nuancen wie korrekte Behandlung von Leerraum etc. sowieso zu behandeln waeren.

    Nein das meinte ich nicht, ich finde das eine sehr attraktive Information das mit einem std::size_t sogar so machen zu können. 👍



  • @Großmaul sagte in Frage zu Übungsaufgabe mit string:

    @hustbaer sagte in Frage zu Übungsaufgabe mit string:

    Hä?

    Naja, damit meine ich halt, dass bei meiner Version der Zähler irgendwann einmal überlauft

    Das ist aber kein Pufferüberlauf. Lässt sich auch leicht vermeiden indem man - so wie du das gemacht hast - size_t statt wie bei mir int verwendet. Weil size_t immer gross genug ist um jedes mögliche Array vollständig zu indizieren - und damit auch gross genug für jeden möglichen std::string.

    Mit anderen Quellen (Files, generell Streams) sähe die Sache anders aus. Aber auch das liesse sich leicht fixen, man kann ja den Zähler nachdem man bis N gezählt hat einfach wieder auf 0 setzen. Oder man verwendet einfach long long als Zähler. Das kann dann zwar theoretisch genau so überlaufen, aber praktisch halt nicht.

    und ob das wieder genau so teilbar ist wenn das Ding wieder bei der Null ankommt ist mir nicht schlüssig.

    Wenn N nicht gerade eine 2er Potenz ist, dann tritt dabei sichr ein Fehler auf. Aber halt kein Pufferüberlauf.

    EDIT: @hustbaer bei deinem Example seh ich auch wie du irgendwas immer weiter aufaddierst aber den Anker dazu find ich bei dir nicht. Hierzu also ein herzliches Hä zu deinem Hä.

    Ich hab keine Ahnung was du mit Anker meinst. w zählt einfach wie viele Leerzeichen bisher (aktuelles Zeichen inklusive) bisher gefunden wurden.



  • Generell würde ich sowas auch mit Streams machen.



  • Ich nicht. Meiner Erfahrung nach ist es in vielen Fällen schwieriger C++ Streams dazu zu überreden genau das zu machen was man haben will, als den Code einfach ohne Streams selbst zu schreiben.



  • @hustbaer sagte in Frage zu Übungsaufgabe mit string:

    Ich nicht. Meiner Erfahrung nach ist es in vielen Fällen schwieriger C++ Streams dazu zu überreden genau das zu machen was man haben will, als den Code einfach ohne Streams selbst zu schreiben.

    Ach findest du.

    Naja, im Sinne eines echten Wortes auszulesen und N Spaces zu skippen finde ich sind Streams wie geschaffen dafür was "jedes dritte Wort" angeht. Man kann sich aber natürlich auch mit Strings den Arm ausrenken, so wie ich es gemacht habe. Und natürlich nachbessern.



  • Ich bins.


Anmelden zum Antworten