std::basic_string::find Vereinfachen [gelöst]


  • Gesperrt

    Ah, sorry. Habe nur noch kurz Liste der Operatoren nach Priorität gesucht. Nach kurzem Blick sind keine zusätzlichen Klammern nötig, was ich zuerst vermutet habe. Deshalb fragte ich übereilt.



  • @titan99_ Kein Problem! Habe auch erst kurz vor dem Post gemerkt, dass die zusätzlichen Klammern nicht nötig sind 😉



  • Nicht viel kürzer ...

    #include <iostream>
    #include <string>
    #include <regex>
    
    int main()
    {
        std::string s;
        std::getline(std::cin, s);
        if (std::regex_match(s, std::regex{ "Ja|ja" }))
        {
            std::cout << "+ : " << s << "\n";
        }
        else
        {
            std::cout << "- : " << s << "\n";
        }
    }
    

    Das schliesst, wie bei deinem ursprünglichen Code, Eingaben wie "jA" und "JA" aus und Eingaben wie "qwerjayxcv" ein. Ob das wirklich sinnvoll ist, sei mal dahin gestellt. Vielleicht wärst du mit einem einfach Vergleich besser bedient (a == "Ja" || a == "ja") ...



  • @theta Danke! Es wird auf jeden Fall kürzer, wenn mehrere Schlagworte abgefragt werden sollen. Ein einfacher Vergleich reicht aber leider nicht aus, da in der eigentlichen Anwendung komplexere Eingaben abgegriffen werden.

    Edit: Habe es gerade ausprobiert und es funktioniert nur bedingt. Wenn ein längerer String eingegeben wird, beispielsweise "test ja test", greift die Funktion nicht.



  • Wenn du wirklich nur suchen willst, kannst du dir auch schnell eine eigene Funktion basteln, die beliebig viele Parameter nimmt:

    #include <iostream>
    #include <string>
    #include <string_view>
    
    template <typename... Ts>
    bool containsAny(const std::string &s, const Ts &... searchStrings) {
        return ((s.find(searchStrings) != std::string::npos) || ...);
    }
    
    int main() {
        std::string testString;
        getline(std::cin, testString);
        const std::string yetAnotherSearchStr = "X";
        if (containsAny(testString, "Ja", std::string_view("ja"), 'x',
                        std::string("wasnoch"), yetAnotherSearchStr)) {
            std::cout << "gefunden\n";
        } else {
            std::cout << "nee, is nich!\n";
        }
    }
    

    Frage an die Experten: nimmt man hier const T&...? Oder irgendwas anderes wie T&&?



  • @Richard_Wunsch sagte in std::basic_string::find Vereinfachen:

    Wenn ein längerer String eingegeben wird, beispielsweise "test ja test", greift die Funktion nicht.

    Siehe hier unter "Notes": https://en.cppreference.com/w/cpp/regex/regex_match (mit anderen Worten: regex_search versus regex_match)



  • @wob Danke! Eine Funktion ist wahrscheinlich die beste Option. Ich bin noch nicht dazu gekommen, deinen Code auszuprobieren. Mir hat schon der zweite Compiler eine Fehlermeldung bezüglich <string_view> ausgespuckt. Muss ihn wahrscheinlich noch auf C++17 trimmen. Melde mich morgen nochmal, wenn ich mehr Zeit dazu habe. Der Link mit dem dazugehörigen Verweis ist auch sehr nützlich!

    Gruß,



  • Dieser Beitrag wurde gelöscht!


  • @wob Ob Ts&&.. oder const 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:

    1. Warum genau ist es nötig mit einem template zu arbeiten?

    2. Was ist die genaue Definition von ... (Habe keinen Artikel dazu gefunden)

    3. Ist es möglich Bibliotheken einzubinden, die mir ermöglichen ganze Listen von Synoynmen abzufragen.
      -> Also theoretisch statt if (containsAny(testString, "Ja", "ja", "ok", "okay", "jawohl" etc. )) so etwas
      if (containsAny(testString, "JaSynyomListe.txt"))

    4. 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 in bool 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 klar std::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äre search 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 einer tolower () Schleife verknüpfen. Deinen Verweis auf search 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 und regex_match sicher auch dazu!

    Nur sehe ich keinen entscheidenden Unterschied zwischen regex_search und std::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.


Anmelden zum Antworten