wordcount mit string
-
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; }
-
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(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.
-
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/eL8MrbIgitt, 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.
-
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 einenresult_type
-typedef haben.
-
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 einenresult_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:
-
Uihh! - ein kleiner Programmier-Contest; da darf mein Beitrag nicht fehlen.
std::inner_product
hat noch keiner:#include <functional> // std::plus<> #include <iostream> #include <numeric> // std::inner_product #include <string> #include <cctype> // std::isalnum, etc. namespace werner { int wordcount( const std::string& satz ) { return std::inner_product( begin(satz), end(satz)-1, begin(satz)+1, !satz.empty() && std::isalnum( satz.front() )? 1: 0, std::plus< int >(), []( char prev, char next )->int { return std::isspace( prev ) && std::isalnum( next )? 1: 0; } ); } } int main() { using namespace std; for( string satz; cout << "\nGeben Sie einen Satz ein: ", getline( cin, satz ) && satz != "x"; ) cout << "Sie haben " << werner::wordcount( satz ) << " Woerter eingegeben" << endl; }
Beispiel:
Geben Sie einen Satz ein: Karl-Heinz treibt's um 12:00 bunt - oder ? Sie haben 6 Woerter eingegeben Geben Sie einen Satz ein: Hallo Welt, Du grausame Welt, hast Tabs und auch Zeilen sowie Steuerzeichen Sie haben 12 Woerter eingegeben
Erkennt "Karl-Heinz" als ein Wort; "-" als kein Wort; leider auch "-oder" als kein Wort ... das würde mehr erfordern, als zwei hinter einander stehende Zeichen zu interpretieren.
Gruß
Werner
-
Phil123 schrieb:
Aufgabe:
Der Funktion wordcount wird ein String als Argument u¨bergeben. ...Derartige Aufgabenstellungen schränken doch bereits die Möglichkeiten der Lösung ein. Wieso eigentlich String? Irgendwie muss der Satz doch auch in den Rechner kommen, also per Input - in C++ auch als
std::istream
bekannt: zunächst mal dasmain()
#include <iostream> #include <locale> // std::ctype<> struct WordCounter { WordCounter( int& cnt ) : cnt_( cnt ) {} friend std::istream& operator>>( std::istream& in, WordCounter wc ); private: int& cnt_; }; int main() { using namespace std; for( int words; cout << "\nGeben Sie einen Satz ein: ", cin >> WordCounter( words ); ) cout << "Sie haben " << words << " Wort(e) eingegeben" << endl; }
fehlt noch die Implementierung des Manipulators
WordCounter
:std::istream& operator>>( std::istream& in, WordCounter wc ) { std::istream::sentry ok( in ); if( ok ) { std::ios_base::iostate state = std::ios_base::goodbit; try { enum Mode { Word, Space } mode = Space; wc.cnt_ = 0; typedef std::istream::traits_type Tr; const std::ctype< char >& ct = std::use_facet< std::ctype< char > >( in.getloc() ); for( char c = 0; c != '\n'; ) // bis EOL; irgendwo muss Schluss sein { const Tr::int_type m = in.rdbuf()->sbumpc(); if( Tr::eq_int_type( m, Tr::eof() ) ) // EOF { state |= std::ios_base::eofbit; break; } c = Tr::to_char_type( m ); if( ct.is( std::ctype_base::alnum, c ) ) // Wort { if( mode == Space ) // Wechsel von Space zu Word -> Wortanfang ++wc.cnt_; mode = Word; } else if( ct.is( std::ctype_base::space, c ) ) // Leerzeichen mode = Space; } } catch( ... ) { state |= std::ios_base::badbit; if( in.exceptions() | std::ios_base::badbit ) throw; } in.setstate( state ); } return in; }
ein wenig länglich, dafür werden keine Umwege über einen String genommen (siehe auch hier).
Eine hübsche Aufgabe, und eine nette Übung zur Darstellung von Möglichkeiten zur Programmierung
Gruß
Werner
-
Werner Salomon schrieb:
std::inner_product
hat noch keiner:Ich werfe als Herausforderung std::set_symmetric_difference in den Raum.