wordcount mit string



  • Hallo alle zusammen !
    Hoffe hierbei kann mir jemand behilflich sein!
    Aufgabe:
    Der Funktion wordcount wird ein String als Argument u¨bergeben. Die Funktion liefert
    die Zahl der im String enthaltenen Wörter zurück (Wörter sind durch Leerzeichen getrennt).
    Erstellen Sie den Prototypen (Funktionskopf) und schreiben Sie die Funktion.
    Habe versucht diese Aufgabe zu lösen jedoch ohne Erfolge.
    So weit bin ich gekommen:

    #include<iostream>
    #include<cstdlib>
    #include<string>
    using namespace std;
    void wordcount(string satz);
    
    void main()
    {
    	string satz;
    	cout<<"Geben Sie einen Satz ein: ";
    	cin>>satz;
    	wordcount(satz);
    	system("PAUSE");
    }
    
    void wordcount(string satz)
    {
    	int words=0;
    	int L=satz.length();
    	for(int i=0;i<L;i++)
    	{
    		if(satz.at(i)==' ')
    		{
    			words=words+1;
    		}
    	}
    	cout<<"Sie haben "<<words<<" Woerter eingegeben"<<endl;
    }
    

    Denke das einzigste Problem Hier sind die Schleifen der Funktion. Hoffe jemand von euch kann mir hierbei helfen.
    Danke schon einmal an Alle.



  • Ah und es müsste

    int words=1;
    

    heißen da es ja immer ein Leerzeichen weniger als Worte sind.



  • Gegeben Dein Algorithmus ist richtig:
    Ein großes Problem ist, dass der operator>>() nur bis zum ersten Leerzeichen liest.

    Du schreibst entweder einen string in den Source

    std::string satz = "Furble Wurble war hier.";
    

    Oder Du liest mit der Funktion getline() ein:

    std::string satz;
    std::getline(std::cin, satz);
    

  • Mod

    Ich nehme mal an, "ohne Erfolg" bezieht sich darauf, dass du immer 1 erhältst, oder? Das liegt nicht an deiner Funktion (die zählt zwar falsch, aber sie zählt), sondern wie du die Eingabe durchführst. Lies mal aufmerksam die Referenz durch:
    http://www.cplusplus.com/reference/string/string/operator>>/

    Zu deiner Zählfunktion an sich: Teste sie auch in folgenden Fällen:
    -Keine Wörter
    -Genau ein Wort
    -Zwei oder mehr Leerzeichen zwischen Wörtern
    -Leerzeichen am Anfang und/oder am Ende



  • Vielen Dank für die schnelle Antwort 🙂



  • @SeppJ
    Wenn ich aber die funktion und alles so beibehalte und dafür den string gleich definiere. Bekomme ich die richtige Wortanzahl heraus.
    Das sagt mir doch dass ansonsten alles in ordnung ist oder ?



  • void wordcount(string satz)
    {
        int words=0;
        int L=satz.length();
        for(int i=0;i<L;i++)
        {
            if(satz.at(i)==' ')
            {
                words=words+1;
            }
        }
        cout<<"Sie haben "<<words<<" Woerter eingegeben"<<endl;
    }
    

    --->

    #include <algorithm>
    
    void wordcount(string satz)
    {
        int words = count(satz.begin(), satz.end(), ' ');
        cout<<"Sie haben "<<words<<" Woerter eingegeben"<<endl;
    }
    

    🙂


  • Mod

    Phil123 schrieb:

    @SeppJ
    Wenn ich aber die funktion und alles so beibehalte und dafür den string gleich definiere. Bekomme ich die richtige Wortanzahl heraus.
    Das sagt mir doch dass ansonsten alles in ordnung ist oder ?

    Wieso bekomme ich dann gänzlich falsche Ergebnisse?

    @Ethon: Ähnlicher Fehler wie der TE

    Siehe:
    https://ideone.com/XJk11I
    Viele von den Beispielen sind nicht einmal besonders gemein. Das heißt natürlich nicht, dass man die gemeinen Fälle nicht auch richtig behandeln sollte. Das Testen der Extremfälle ist in der Programmierung sehr oft eine gute Methode, um Fehler zu finden.



  • schöne Gegenüberstellung. Macht mal wieder deutlich, dass man sich vorher oft nicht ausreichend Gedanken über mögliche Probleme macht.

    Also vorher den String trimmen wie z.B. hier
    http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
    beschrieben?

    hab nicht gesehen, dass es schon etwas adäquates in std:: gäbe. Eigentlich erstaunlich, gerade mit LTRIM(RTRIM(RTRIM(string)) konnte man als BASIC'ler schon immer "angeben" 🙂



  • Ich wollte ihm nur std::count zeigen und habe seinen Code darauf angepasst - nicht mehr. Optimal ist es natürlich Wortanfänge zu zählen, also:

    Ist der Charakter alphabetisch?
    ---> Ja
    Ist das Zeichen davor ein Whitespace oder außerhalb des Input Strings?
    ---> Ja => Wortzähler erhöhen.
    ---> Nein => Nichts tun.
    ---> Nein => Nichts tun.

    Was man natürlich noch verbessern könnte um sinnlose Tests zu vermeiden.


  • Mod

    Quisslich schrieb:

    Also vorher den String trimmen wie z.B. hier
    http://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
    beschrieben?

    Nein, Trimmen geht am Problem vorbei. Das eigentliche Problem ist, dass aus der Definition des Wortes "Wörter sind durch Leerzeichen getrennt" die falschen Schlüsse gezogen wurden, wie man Wörter erkennt. Zählen der Leerzeichen ist der falsche Weg, denn die Anzahl der trennenden Zeichen hat nicht direkt damit zu tun, wie viele getrennte Wörter es gibt. Der Versuch nun den String zu trimmen und tausend Fallunterscheidungen einzubauen, behebt bloß die Symptome des Problems.

    Der bessere Weg wäre, die Trennungen, nicht die Trennzeichen zu zählen. Damit haben wir ebenfalls einen sehr einfachen Algorithmus, der aber richtig zählt:
    https://ideone.com/eL8Mrb



  • Ein Vorschlag:

    #include <cctype>
    #include <iostream>
    #include <string>
    #include <boost/range/adaptors.hpp>
    #include <boost/range/algorithm.hpp>
    
    int word_count(std::string const& s)
    {
       namespace ba = boost::adaptors;
       return boost::count(
         s | ba::transformed(static_cast<int(*)(int)>(std::isspace))
           | ba::uniqued,
         0
       );
    }
    
    int main()
    {
        std::string test = "Hallo  Welt,\n"
                  "\tdu     grausame     Welt,\n"
                  "\thast Tabs und auch Zeilen\n"
                  "\tsowie      Steuerzeichen.";
        std::cout << word_count(test) << '\n';
        return 0;
    }
    

    getestet:

    12
    


  • SeppJ schrieb:

    Der bessere Weg wäre, die Trennungen, nicht die Trennzeichen zu zählen. Damit haben wir ebenfalls einen sehr einfachen Algorithmus, der aber richtig zählt:
    https://ideone.com/eL8Mrb

    Igitt, eine for-Schleife.

    int wordcount_Unreg(string satz)
    {
      struct info { int w; bool l; } init{0,true};
      return std::accumulate(satz.begin(), satz.end(), init,
          [](info i,char c){bool b=isspace(c); return info{i.w+(!b&&i.l),b};}).w;
    }
    


  • Solange nur Leerzeichen auftauchen, ist das schön und gut, aber...

    #include<algorithm>
    #include<cstdlib>
    #include<iostream>
    #include<iterator>
    #include<sstream>
    #include<string>
    
    using namespace std;
    
    int wordcount_TE(string satz)
    {
        int words=1;
        int L=satz.length();
        for(int i=0;i<L;i++)
        {
            if(satz.at(i)==' ')
            {
                words=words+1;
            }
        }
        return words;
    }
    
    int wordcount_Ethon(string satz)
    {
       int words = count(satz.begin(), satz.end(), ' ');
       return words;
    }
    
    int wordcount_SeppJ(string satz)
    {
        int words=0;
        bool last_was_space = true;
        for(char c : satz)
        {
            if(c != ' ')
            {
                if (last_was_space)
                   words += 1;
                 last_was_space = false;
            }
            else
              last_was_space = true;
        }
        return words;
    }
    
    int wordcount_seldon(string satz) {
      istringstream in(satz);
      return distance(istream_iterator<string>(in), istream_iterator<string>());
    }
    
    void vergleich(string satz)
    {
        cout << "Satz ist: \"" << satz << "\"\n"
             << " Phil123 zählt darin "       << wordcount_TE(satz)
             << " Wörter, Ethon zählt "       << wordcount_Ethon(satz)
             << " Wörter, SeppJ zählt "       << wordcount_SeppJ(satz)
             << " Wörter, seldon zählt "      << wordcount_seldon(satz)
             << '\n';
    }
    
    int main()
    {
        vergleich("Hallo Welt");
        vergleich("");
        vergleich(" ");
        vergleich("  ");
        vergleich("Hallo  Welt");
        vergleich("Hallo");
        vergleich(" Hallo");
        vergleich("Hallo ");
        vergleich(" Hallo ");
        vergleich("Hallo  Welt,\n"
                  "\tdu     grausame     Welt,\n"
                  "\thast Tabs und auch Zeilen\n"
                  "\tsowie      Steuerzeichen.");
    }
    
    Satz ist: "Hallo  Welt,
    	du     grausame     Welt,
    	hast Tabs und auch Zeilen
    	sowie      Steuerzeichen."
     Phil123 zählt darin 23 Wörter, Ethon zählt 22 Wörter, SeppJ zählt 9 Wörter, seldon zählt 12
    

    Richtig spannend wird das allerdings erst, wenn auch Satzzeichen, Bindestriche und derlei berücksichtigt werden sollen.


  • Mod

    Die Aufgabenstellung war eindeutig nur mit Leerzeichen. Aber alle Lösungen, außer Ethons, sind flexibel was die Definition der Trennung angeht und können leicht auf ein isspace umgestellt werden.

    Flexibilität ist übrigens auch bei dir eine Schwäche. Die Lösung des TE (nach Korrektur), meine, krümelkackers (nach Korrektur) und die des Unregs sind auch leicht anpassbar, Satzzeichen nicht als Wörter zu zählen. Bei dir müsste das auch gehen, aber dazu wären wohl tiefgehende Fummeleien in der Locale nötig, die hier im Forum vielleicht ein oder zwei Personen beherrschen. Umgekehrt kann deine Lösung sich gar nicht an die eigentliche Definition (nur Leerzeichen) halten, außer wieder durch fortgeschrittene Locale-Manipulation.



  • Da ist was wahres dran. Gut, ich nehme alles zurück.



  • SeppJ schrieb:

    krümelkackers (nach Korrektur)

    Tja, boost::adaptors::transformed (Boost 1.53) mag doch tatsächlich keime Lambdas, da sie nicht sowas wie einen result_type -typedef haben. 😞


  • Mod

    krümelkacker schrieb:

    SeppJ schrieb:

    krümelkackers (nach Korrektur)

    Tja, boost::adaptors::transformed (Boost 1.53) mag doch tatsächlich keime Lambdas, da sie nicht sowas wie einen result_type -typedef haben. 😞

    Da ist übrigens noch ein anderer Fehler drin. Aber den editierst du einfach raus, dann erzähle ich auch niemandem, was dir da peinliches passiert ist 😉 .

    edit: Oh, ich sehe, das hattest du ohnehin schon geändert. Dann hat's sich erledigt.



  • SeppJ schrieb:

    Bei dir müsste das auch gehen, aber dazu wären wohl tiefgehende Fummeleien in der Locale nötig, die hier im Forum vielleicht ein oder zwei Personen beherrschen.

    Das geht zwar (copy/paste von http://en.cppreference.com/w/cpp/locale/ctype_char beherrschen bis auf ein, zwei Personen alle hier im Forum), ist aber nicht mal nötig.

    struct mystr {};
    std::istream& operator>>(std::istream& in, mystr&)
    { std::istreambuf_iterator<char> it(in), end;
      std::find_if(std::find_if(it,end,[](char c){return  isspace(c);}),
                   end,                [](char c){return !isspace(c);});
      return in;
    }
    int wordcount_fummler(string satz) {
      istringstream in(satz);
      return distance(istream_iterator<mystr>(in), istream_iterator<mystr>());
    }
    


  • Edit: Yikes! Da hat schon jemand auf eine praktisch gleiche Lösung verlinkt!
    :duck und weg:


Log in to reply