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



  • Morgen zusammen,

    kann man diesen Ausdruck if (a.find("Ja") != std::string::npos || a.find("ja") != std::string::npos) vereinfachen? Hier die gesamte Funktion:

    #include <iostream>
    #include <string>
    
    int main()
    {
        std::string a;
        std::getline (std::cin, a);
        
        if (a.find("Ja") != std::string::npos || a.find("ja") != std::string::npos)
            std::cout<<"test1 ";
            
        else
            std::cout<<"test2 ";
       
        return 0;
    }
    

    Gruß,


  • Gesperrt

    Was meinst Du mit vereinfachen?



  • Kürzen. Theoretisch so etwas: if (a.find("Ja" || "ja") != std::string::npos) oder if (a.find("Ja" , "ja") != std::string::npos)


  • 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?


Anmelden zum Antworten