Newbie: Vernünftiger Umgang mit Strings?



  • Hi,

    wie steige ich am besten in das Thema Strings ein? Ich finde Strings in c++ wirklich merkwürdig gelöst, wenn ich mal mit Delphi vergleiche. Was ich in Delphi out of the box an Stringfunktionen hatte, muss ich mir in c++ in Bibliotheken zusammensammeln und überschreite da oft auch noch Grenzen von Datentypen. 😞 Der Einstieg ist echt hart, erste Ergebnisse lassen ganz schön auf sich warten...

    Ich will Strings der Länge <300 in Variablen schreiben. Erst dachte ich, es sei eine gute Idee, die string-Klasse zu Hilfe zu nehmen, diese scheint mir intuitiver zu sein. Jetzt will ich aber ganz natürliche Operationen darauf machen, wie zum Beispiel tolower(). Das bekomme ich aber nicht auf meine String-Variable angewandt. Wie wende ich es an?

    #include <string>
    int main(void)
    {string test = "Max";}
    

    Also tolower(test) ist schon mal nicht erlaubt. (Ja, ich hatte das included). Ist "Variable" überhaupt der richtige Begriff? Oder sagt man hier "test" ist ein Objekt der Klasse "string". Dann folgt doch für mich daraus, ich kann nur Methoden des Objekts aufrufen? Also in meinem Fall brauche ich eine Lowercase-Funktion, ich würde dann, falls es das geben würde, test.tolower aufrufen? Bin ich für OOP zu alt? 😃 Ich glaube mein Problem ist, ich komme total mit Klassen, Funktionen, Konstruktoren, Methoden durcheinander.

    Was würdet Ihr im konkreten Fall (Variable und eine Lowercase-Funktion) raten?

    Danke, ich hoffe ich nerve nicht zu sehr.



  • mit c_str() kannst du dir ein char* ausgeben lassen
    http://www.cplusplus.com/reference/string/string/c_str/

    test.c_str()
    


  • Die Lösung für deine Probleme heißt Boost.
    http://www.boost.org/doc/libs/1_40_0/doc/html/string_algo.html



  • €dit: Verpeilt



  • Die String-Klassen sind auf jeden Fall sehr nützlich und erstmal der richtige Schritt. Du wirst aber nicht darum herum kommen, dich mit der Standard-Bibliothek von C++ auseinander setzen zu müssen, um zu wissen, wo du was findest und anwendest. D.h. du solltest dir folgendes anschauen:

    - Die Memberfunctions (Methoden) von basic_string: http://www.cplusplus.com/reference/string/string/
    - Die Algorithmen http://www.cplusplus.com/reference/algorithm/ lassen sich auch auf String-Objekte anwenden, da ein String auch nur ein Container sit.
    - Die Stringstreams, sind zum Formatieren und Umwandeln (z.B. von Zahl zu Zeichenkette) sehr nützlich.

    Leider ist die Std-Lib von C++ wirklich nicht der König in Sachen String-Manipulation. Wenn du viel mit Strings arbeitest, solltest du dir http://www.boost.org/doc/libs/1_40_0/doc/html/string_algo.html anschauen. Die haben dort sehr viel, auch was für dich: http://www.boost.org/doc/libs/1_40_0/doc/html/string_algo/usage.html#id1685354

    Überhaupt gehört Boost auf jeden Rechner eines C++-Proggers.



  • sunny31 schrieb:

    #include <cctype>
    #include <iostream>
    int main()
    {
        std::string test = "MAX";
        std::cout << test << std::endl; // Ausgabe: MAX
        test = std::tolower(test);
        std::cout << test << std::endl; // Ausgabe: max
        return 0;
    }
    

    Also das kann ich mir beim besten Willen nicht vorstellen...
    Soweit ich weiß erwartet std::tolower() einen int als Argument...



  • sunny31 schrieb:

    test = std::tolower(test);
    

    Ich hoffe, dass mein Beispiel funktioniert. Habe momentan kein Compiler bei mir rumliegen um das zu testen...

    Genau das war mein Problem. Das tolower() da oben scheint nicht erlaubt zu sein...

    "error: cannot convert `std::string' to `int' for argument `1' to `int tolower(int)'"



  • Ja du hast recht...

    richtig wäre es so:

    #include <iostream>
    #include <string>
    #include <cctype>
    
    int main()
    {
        std::string test = "MAX";
        for ( unsigned int i=0; i < test.size(); i++ )
               test[i]=std::tolower(test[i]);
        std::cout << test << std::endl;   //max
    }
    

    Wobei, es sicher bessere Lösungen dafür gibt...



  • versuchs mal so

    string test="Test String";
    for (int i=0;i<test.length();i++) test[i]=tolower(test[i]);
    


  • sunny31 schrieb:

    funktioniert die ToLower Funktion so:

    #include <cctype>
    #include <iostream>
    int main()
    {
        std::string test = "MAX";
        std::cout << test << std::endl; // Ausgabe: MAX
        test = std::tolower(test);
        std::cout << test << std::endl; // Ausgabe: max
        return 0;
    }
    

    Ich hoffe, dass mein Beispiel funktioniert. Habe momentan kein Compiler bei mir rumliegen um das zu testen...

    Habe es auch nicht ausprobiert, aber 1. ist es eine C-Funktion (nichts C++-artiges) und 2. konvertiert es nur einen einzelnen Charachter und keine Zeichenkette.
    Es ist sowieso blödsinnig jemanden mit C-Krüppelfunktionen anzukommen, wenn er C++ machen will. Weil man hier eigentlich eine for-Schleife bauen müsste:

    #include <cctype>
    #include <iostream>
    int main()
    {
        std::string test = "MAX";
        std::cout << test << std::endl; // Ausgabe: MAX
        for(size_t i = 0; i<test.size(); i++)
              test[i] = std::tolower(test[i]);
        std::cout << test << std::endl; // Ausgabe: max
        return 0;
    }
    

    Aber sowas findet man fertig in der Boost-String-Algo-Lib.



  • So, wenn man kein Boost nutzen will:

    #include <cctype>
    
    void to_lower(string &str)
    {
        for(size_t i = 0; i<str.size(); ++i)
              str[i] = std::tolower(str[i]);
    }
    

    Aber Boost ist hier eindeutig lohnenswert, da es nicht nur mit std::string zusammen arbeitet und auch die Locale beachtet!



  • Dafür kann man aber die Funktion 'std::transform' benutzen, jedoch gibt es dabei auch ein paar Fallstricke (je nach Compiler) zu beachten, s. http://www.c-plusplus.net/forum/viewtopic-var-t-is-210176-and-highlight-is-transform+tolower.html



  • Danke allerseits... Zwischenfazit bzgl. Lowercase/Uppercase: Rein auf STL-Ebene macht man es wohl tatsächlich selbst, also eine Schleife über ein Char-Array...
    Zwischenfazit2: Boost muß her. Ich will nämlich langsam Ergebnisse sehen. Und damit den Ansporn aufbauen, mich dann auch Standard-näher schlau zu machen.



  • Ich habe mal eine Nachfrage. Das ist aus boosts "to_lower" (boost::algorithm::to_lower). Ich habe oft Probleme, die Funktionenbeschreibungen zu verstehen. Ich weiß dann nicht, welche Argumente von welchem Typ verlangt werden und wie die Rückgabe aussieht...

    [http://www.boost.org/doc/libs/1_40_0/doc/html/boost/algorithm/to_lower.html]

    void to_lower(WritableRangeT & Input, 
                    const std::locale & Loc = std::locale());
    

    "void to_lower". OK, void sagt mir AFAIK "keine Rückgabe". Die Funktion ändert den String direkt. In der Klammer sollte m.E. das Argument stehen. Nur was heißt das darin? Kann mir das jemand erklären? Ich sehe ein Komma, würde also 2 Argumente vermuten. Aber warum 2?



  • Man sollte auch nicht unbedingt tolower aus <ctype> benutzen, sondern besser tolower aus <locale> .
    Irgendwie so:

    #include <iostream>
    #include <string>
    #include <algorithm>
    #include <locale>
    
    template<typename charT>
    class ToLower
    {
    public:
        ToLower(char const * locale = "")
            : loc(locale)
        { }
        charT operator()(charT c){ return std::tolower(c, loc); }
    private:
        std::locale loc;
    };
    
    int main()
    {
        std::string hallo = "haLLo wELt";
        std::transform(hallo.begin(), hallo.end(), hallo.begin(), ToLower<std::string::value_type>());
        std::cout << hallo << '\n';
    }
    


  • #include <algorithm>
    #include <string>

    std::string test = "THIS IS MY STRING";
    std::transform(test.begin(), test.end(), test.begin(), std::tolower);

    So gehts wohl am besten und am schönsten.



  • Squeller schrieb:

    "void to_lower". OK, void sagt mir AFAIK "keine Rückgabe". Die Funktion ändert den String direkt.

    Ja. Wenn du eine Kopie haben willst, schau dir to_lower_copy() an.

    Squeller schrieb:

    In der Klammer sollte m.E. das Argument stehen. Nur was heißt das darin? Kann mir das jemand erklären? Ich sehe ein Komma, würde also 2 Argumente vermuten. Aber warum 2?

    Das erste für den zu bearbeitenden String und das zweite für das Locale. Das Locale bestimmt zum Beispiel, welche speziellen Zeichen berücksichtigt werden, also 'ä' in der deutschen und 'é' in der französischen Sprache.



  • Squeller schrieb:

    [http://www.boost.org/doc/libs/1_40_0/doc/html/boost/algorithm/to_lower.html]

    void to_lower(WritableRangeT & Input, 
                    const std::locale & Loc = std::locale());
    

    "void to_lower". OK, void sagt mir AFAIK "keine Rückgabe". Die Funktion ändert den String direkt. In der Klammer sollte m.E. das Argument stehen. Nur was heißt das darin? Kann mir das jemand erklären? Ich sehe ein Komma, würde also 2 Argumente vermuten. Aber warum 2?

    Unter Description stehen ja die Bedeutungen der einzelnen Parameter:

    Input A range
    Loc a locale used for conversion

    Wobei du den zweiten Wert nicht explizit angeben mußt, weil der zweite Parameter einen Default-Wert vorgibt:

    void to_lower(WritableRangeT & Input, const std::locale & Loc = std::locale());

    Nur wenn dir der Default-Wert std::locale() nicht zusagt, gibst du beim Aufruf einen anderen an. In den meisten Fällen dürfte aber std::locale() i.O. sein, da sich dieses nach den Betriebssystem-Einstellungen richtet. Was die Locale ist, weißt Du? Das hat was mit den Zeichencodes für verschiedene Länder und Sprachen zu tun, siehe hier:

    http://www.cplusplus.com/reference/std/locale/



  • Vielen Dank für die Antwort.

    Nein, was eine locale ist, habe ich auch nur erahnt... Wie ist das "&" zu verstehen? Hat das was mit Standardwerten zu tun? Mein Problem ist wohl die Komplexität des Ausdrucks. Wenn da jetzt stünde "void ziehbvonaab(int a, int b)" dann ginge es ja...

    Ebenso weiß ich noch nicht, wie "WritableRangeT & Input" zu verstehen ist, was ist hier der Datentyp?



  • Ja, für einen C++-Neuling sind in der Funktionssignatur tatsächlich viele Features drin. C++ ist komplex (nicht kompliziert!), und man muß viel lernen. Aber das ist alles erlernbar und keine Magie! 😉 Wichtig ist sich die gesamte Funktions-Signatur anzuschauen:

    template<typename WritableRangeT> // Funktion to_lower erwartet einen genauen Typnamen!!!
    void to_lower(WritableRangeT & Input,
                                          const std::locale & Loc = std::locale());
    

    Du mußt als erstes lernen was Templates sind, um WritableRangeT zu verstehen. Bei Templates handelt es sich "nur" um Codeschablonen, die erst zur Compilezeit typisiert werden. Grob gesagt kann WritableRangeT jeder Typ sein, mit dem das Template "kompatibel" ist. Range deutet darauf hin, das es ein Array, String, Vector etc. sein kann. Der vollständige Aufruf müsste so lauten:

    string s("HaLLo!");
    to_lower<string>(s);     // mit <string> typisiere ich WritableRangeT bzw. das Template
    

    So weiß die Funktion, das s ein String-Objekt ist. Aber da bei solchen einfachen Funktionen der Compiler aus dem übergebenen Wert "s" den Typ string selber feststellen kann, muß man das nicht angeben.

    string s("HaLLo!");
    to_lower(s);     // <string> setzt der Compiler selbst ein... implizite Typisierung
    

    Der Vorteil von Templates ist, das man Code für verschiedenen Typen benutzen kann. Und man spart sich Copy&Paste in seinem Code. D.h. to_lower kann auch mit Arrays, Vector usw. arbeiten.

    Das & steht für By-Reference, also das die Funktion mit dem "original" Objekt arbeitet bzw. dieses manipulieren wird. Weitere Infos gibt es unter http://www.kharchi.eu/wiki/doku.php?id=cpp:std:reference.

    Hast du ein vernünftiges C++-Buch, in dem solche Dinge ausführlich und vor allem verständlich erklärt werden? Weil für C++ kommt man mit Try & Error nicht schnell vor ran.


Log in to reply