std::basic_string::find Vereinfachen [gelöst]
-
@wob Ob
Ts&&..
oderconst Ts&...
ist von der Performance her egal (habs gerade bei 2 Mio Iterationen mit verschiedenen Argumenten gemessen), aber ich würde den ersten Parameter string_view machen, also:bool containsAny(std::string_view s, const Ts &... searchStrings)
Und ich kenne es halt, dass man Args&&... nimmt und dann std::forward<Args>(args)..., aber ich weiß nicht, wie man das hier richtig anwendet oder braucht.
-
@wob Hat alles länger gedauert als gedacht, aber jetzt bin ich endlich dazu gekommen, den Code zu testen. Der Verweis auf eine Funktion, die beliebig viele Parameter nimmt, war genau das, wonach ich gesucht habe! Hätte nicht gedacht, dass
...
tatsächlich interpretiert werden kann. Alles andere (yetAnotherSearchStr
,std::string_view
,std::string ("wasnoch")
) ist zwar auch gut zu wissen, aber vom Prinzip her nur optional.Allerdings ergeben sich mir auch ein paar Fragen zu deinem Code:
-
Warum genau ist es nötig mit einem template zu arbeiten?
-
Was ist die genaue Definition von
...
(Habe keinen Artikel dazu gefunden) -
Ist es möglich Bibliotheken einzubinden, die mir ermöglichen ganze Listen von Synoynmen abzufragen.
-> Also theoretisch stattif (containsAny(testString, "Ja", "ja", "ok", "okay", "jawohl"
etc.))
so etwas
if (containsAny(testString, "JaSynyomListe.txt"))
-
Was mache ich falsch, wenn ich versuche nach gleichem Prinzp mit
regex_match
zu arbeiten? Hier der Code + Fehlermeldung:
#include <iostream> #include <string> #include <regex> template <typename... Ts> bool containsAny(const std::string &s, const Ts &... searchStrings) { return (std::regex_match (searchStrings, std::regex { "Ja | ja"})); } int main() { std::string testString; getline(std::cin, testString); if (containsAny(testString, "Ja")) { std::cout << "gefunden\n"; } else { std::cout << "nee, is nich!\n"; } }
main.cpp: In function ‘bool containsAny(const string&, const Ts& ...)’: main.cpp:8:30: error: parameter packs not expanded with ‘...’: return (std::regex_match (searchStrings, std::regex { "Ja | ja"})); ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ main.cpp:8:30: note: ‘searchStrings’
Schließlich noch eine Frage an @HarteWare
Warum
string_view
als ersten Parameter inbool containsAny(const std::string &s, const Ts &... searchStrings)
Ist doch im Endeffekt redundant, wenn in der eigentlichen Funktion der string sowieso durchsucht wird, oder?Noch einmal Danke für eure Mühe.
Gruß,
-
-
Müsste es statt containsAny nicht besser isOneOf heißen? Dazu ein Vorschlag auf Basis von Foldexpressions (C++17):
#include <iostream> #include <string> template<typename ... Mehr> bool isOneOf(std::string testString, Mehr&&... arg) { return (... || (arg == testString)); } int main() { std::string testString; getline(std::cin, testString); if (isOneOf(testString, "Ja", "ja", "Jawohl")) { std::cout << "gefunden\n"; } else { std::cout << "nee, is nich!\n"; } }```cpp
Die drei Punkte stehen für die noch auszuwertenden Parameter. String_view müsste auch gehen, habe ich nicht probiert. Es muss mit einem Template gearbeitet werden, weil es ohne keine sog. Parameter Packs gibt. Eine Liste von Synonymen abzufragen ist einfach, wenn man die Liste nach und nach auswertet usw.
-
@Schnuffi sagte in std::basic_string::find Vereinfachen:
Müsste es statt containsAny nicht besser isOneOf heißen?
Das hatte der OP oben schon ausgeschlossen (in der Anwort an @theta), er will auch ja in "hbjdgdjanfdi" finden. Warum auch immer. Denn die Suche nach "ja" und "jawohl" ist ja in dem Fall redundant, da "ja" ja schon beides findet...
-
@HarteWare sagte in std::basic_string::find Vereinfachen:
bool containsAny(std::string_view s, const Ts &... searchStrings)
Und ich kenne es halt, dass man Args&&... nimmt und dann std::forward<Args>(args)..., aber ich weiß nicht, wie man das hier richtig anwendet oder braucht.
Args&&
nimmt man wenn man perfect forwarding möchte. Perfect forwarding ist für Sachen wie std::function gut, wo man die Parameter 1:1 so weitergeben will wie man sie erhalten hat. Ist hier aber eher nicht der Fall. Ich denke hier ist es eher so, dass man garantieren möchte dass die Funktion die Argumente niemals modifiziert, egal wie sie übergeben werden.Args&&
drückt das nicht aus,Args const&
dagegen sehr schön.
Und auf den zusätzlichen Template-Bloat, der entsteht wenn man unterschiedlich geconstete Argumente übergibt bzw. mal Rvalues und mal Lvalues, kann man denke ich auch gut verzichten.=>
Args const&
Was
std::string_view
angeht: ja, ganz klarstd::string_view
.
-
Vielen Dank für alle Beiträge und Antworten. Hier die finalen Versionen gemäß euren Vorschlägen:
#include <iostream> #include <string> #include <string_view> template <typename ... T> bool containsAny(const std::string_view &s, T const &... searchStrings) { return ((s.find(searchStrings) != std::string::npos) || ...); } int main() { std::string testString; getline(std::cin, testString); if (containsAny(testString, "Ja", "ja")) { std::cout << "gefunden\n"; } else { std::cout << "nee, is nich!\n"; } }
Mit einfachem Vergleich:
#include <iostream> #include <string> template <typename ... T> bool isOneOf (const std::string s, T const &... searchStrings) { return ((s == searchStrings) || ...); } int main() { std::string testString; getline(std::cin, testString); if (isOneOf(testString, "Ja", "ja", "jawohl", "Jawohl")) { std::cout << "gefunden\n"; } else { std::cout << "nee, is nich!\n"; } }
Gruß,
-
Oder sehr kurz und ich finde elegant nach einer Idee von wob (siehe https://www.c-plusplus.net/forum/post/2565140) :
#include <iostream> #include <string> #include <set> int main() { std::set<std::string> werte {"ja", "Ja"}; std::string testString; getline(std::cin, testString); if (werte.count(testString) == 1) { std::cout << "gefunden\n"; } else { std::cout << "nee, is nich!\n"; } }
-
Es ist nicht geklärt, was bei einem „jA” passieren soll. Auch ”JA” würde „nein” bedeuten.
Es kommt halt immer drauf an, aber wenn es nur um die Groß- oder Kleinschreibung geht, wäresearch
mit einem eigenen Prädikat, das nur die Kleinbuchstaben vergleicht, wohl ausreichend.
-
@Schnuffi Für diesen konkreten Fall trifft das zu. Aber sobald man den werte string ändern möchte, bietet sich eine Funktion wie die obige an.
@yahendrik Doch, geklärt schon. "jA" und "JA" ==
false
nach dem genannten code. Um Groß- und Kleinschreibung zu ignorieren könnte man auch die Eingabe mit einertolower ()
Schleife verknüpfen. Deinen Verweis aufsearch
mit eigenem Prädikat verstehe ich nicht ganz. Kannst du das vielleicht mit einem Beispiel konkretisieren?
-
Meine Vermutung ist ja, dass eingentlich eine Regex
/\b(?:ja|jawohl|sicher|bestimmt)\b/i
gesucht ist. Alternativ wäre es auch sinnvoll, erst einmal den Text (ggf. erst in einzelne Sätze und dann) in einzelne Wörter zu splitten und diese dann zu vergleichen. Es erscheint merkwürdig, Wortteile zu vergleichen.
-
Ich meinte nix Kompliziertes und es kommt ja eigentlich sowieso nicht in Frage.
template<typename charT> struct CICmp { CICmp(const std::locale& loc) : m_loc(loc) { } bool operator()(charT ch1, charT ch2) { return std::tolower(ch1, m_loc)==std::tolower(ch2, m_loc); } private: const std::locale& m_loc; }; template<typename T> size_t ci_find(const T& str1, const T& str2, const std::locale& loc=std::locale()) { typename T::const_iterator it=std::search(str1.begin(), str1.end(), str2.begin(), str2.end(), CICmp<typename T::value_type>(loc)); if (it!=str1.end()) return it-str1.begin(); return -1; } int main() { using namespace std; string str1 = "Guten Tag und Hallo"; string str2 = "hallo"; size_t f1 = ci_find(str1, str2); cout << f1 << '\n'; // liefert wie string::find den Index, also hier 14 wstring wstr1 = L"Guten Tag und Hallo"; wstring wstr2 = L"tag"; size_t f2 = ci_find(wstr1, wstr2); cout << f2 << '\n'; // 6 }
-
@wob Prinzipiell ist alles gesucht, was meinen Anforderungen entspricht. Da gehören
regex_search
undregex_match
sicher auch dazu!Nur sehe ich keinen entscheidenden Unterschied zwischen
regex_search
undstd::basic_string::find
bzw.regex_match
und==
.Der Sinn hinter einem Vergleich auf Wortteile ist, dass man nicht auf verschiedene Worte oder Deklinationen untersuchen muss, sondern nur auf Wortstämme. Natürlich kann es da zu Diskrepanzen kommen, wenn man beispielsweise nach "ja" sucht. Deshalb bietet sich für kürzere Wörter auf jeden Fall ein direkter Vergleich an.
-
@yahendrik sagte in std::basic_string::find Vereinfachen [gelöst]:
nix Kompliziertes
Dein Code übersteigt auf jeden Fall meinen Kenntnisstand ^^ Aber ich glaube ich verstehe, worauf du im Kern hinauswillst.
std::search
scheint gut geeignet zu sein, aber ist doch im Prinzip nichts anderes alsstd::basic_string::find
mit einer range oder?