Newbie: Vernünftiger Umgang mit Strings?



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





  • Artchi schrieb:

    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.

    Also, geht es darum, dass ich a) einer Funktion dann unterschiedliche, sinnvolle Typen übergeben kann und b) je nach Eingabetyp in der Funktion handeln kann? Bspw. könnte eine Funktion sowohl ein Char-Array als auch einen String bekommen?

    Und irgendwas habe ich doch sicher übersehen: an so einer Funktionssignatur "..(WritableRangeT ...) sehe ich doch gar nicht, welche(r) Typ(en) denn nun erlaubt sind?

    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.

    Ja, habe ein Buch von Dirk Louis. Muss ich nochmal lesen, ich glaube da stand etwas in der Zeiger-Ecke, eine Giftküche, an der ich schnell vorbeigelesen hatte. 😃



  • ganz ehrlich, merkt ihr es noch? er will einen string per tolower umwandeln und bekommt templates, boost oder qt vorgesetzt. Irgendwas stimmt da doch ganz gewaltig nicht...



  • Wenn er auf Boost verzichten will, kann er doch die, ein paar Seiten weiter vorne gepostete, einfache und funktionierende Lösung verwenden:

    #include <algorithm>
    #include <string>
    
    std::string test = "THIS IS MY STRING";
    std::transform(test.begin(), test.end(), test.begin(), std::tolower);
    


  • jaja schrieb:

    ganz ehrlich, merkt ihr es noch? er will einen string per tolower umwandeln und bekommt templates, boost oder qt vorgesetzt. Irgendwas stimmt da doch ganz gewaltig nicht...

    C++ ... kompliziertes Denken ... irgendwas war da.



  • jaja schrieb:

    ganz ehrlich, merkt ihr es noch? er will einen string per tolower umwandeln und bekommt templates, boost oder qt vorgesetzt. Irgendwas stimmt da doch ganz gewaltig nicht...

    Vielleicht hast du nichts bemerkt, aber es geht hier in diesem Topic schon laaange nicht mehr um "Wie mache ich Zeichen klein?". Es geht hier mittlerweile um C++-Syntax und -Features, am Beispiel von to_lower. Es könnte aber auch jede andere Funktion aus C++ sein! Er hätte bei jeder anderen C++-Funktion die gleichen Fragen gestellt. Er lernt noch C++, das hat nichts mit Boost oder so zu tun. Wer kein C++ kann, wird nun mal solche Fragen stellen. Das er noch kein C++ kann, kann ja wohl C++ oder Boost nichts für.



  • Für einen Einsteiger mag C++ schwerer sein als andere Sprachen...
    Aber wie oft saß ich schon bei der Arbeit (komplexe Webapplikation in PHP)
    und dachte mir:
    Wie gern hätte ich jetzt Mehrfachvererbung ?
    Was gäbe ich nicht für Templates?
    Wo ist die transparente Laufzeitpolymorphie?
    Wo die strikte Typisierung?

    Und jedesmal wünschte ich mir C++ herbei...

    Die mächtigen Features von von C++ steigern natürlich die Komplexität der Sprache,
    erleichtern aber die Arbeit damit um ein vielfaches, wenn man angefangen hat, sie zu durchschauen.



  • Artchi schrieb:

    jaja schrieb:

    ganz ehrlich, merkt ihr es noch? er will einen string per tolower umwandeln und bekommt templates, boost oder qt vorgesetzt. Irgendwas stimmt da doch ganz gewaltig nicht...

    Vielleicht hast du nichts bemerkt, aber es geht hier in diesem Topic schon laaange nicht mehr um "Wie mache ich Zeichen klein?".

    Ne, um wie mache ich einen String klein.



  • Squeller schrieb:

    Also, geht es darum, dass ich a) einer Funktion dann unterschiedliche, sinnvolle Typen übergeben kann und b) je nach Eingabetyp in der Funktion handeln kann? Bspw. könnte eine Funktion sowohl ein Char-Array als auch einen String bekommen?

    Richtig. Denn ohne Templates, müsste der Bibliotheks-Entwickler alle möglichen Typen beachten und z.B. eine Funktion für Array und eine für Strings bereit stellen... was in einer Copy&Paste-Orgie enden würde. Und wenn du doch Vector benutzen willst, würde die spezielle Funktion fehlen. Also schreibt man ein Template (eine Schablone), und sagt dem User: sag dem Template welcher Typ, und der Compiler macht dann im Hintergrund Copy&Paste mit dem Wunschtyp. 😉

    Squeller schrieb:

    Und irgendwas habe ich doch sicher übersehen: an so einer Funktionssignatur "..(WritableRangeT ...) sehe ich doch gar nicht, welche(r) Typ(en) denn nun erlaubt sind?

    Ja, das kannst du anhand der Dokumentation erfahren. Du kannst aber auch anhand des "Range" einen Hinweis bekommen, das es ein Range (Reihe, Kollektion u.ä.) sein sollte. D.h. potentiell ein Array, ein String, eine List, ein Vector usw. Wenn ein Typ nicht zu dem Template passt, wird der Compiler meckern.

    Das WritableRangeT sagt doch eigentlich schon viel:
    1. Writable: Beschreibbar (also nicht const)
    2. Range: Reihe, Kollektion (also begin bis end)
    3. T: Typ 😉

    Es sollte eigentlich für den nächsten C++-Standard (C++0x) eine drastische Verbesserung geben: Concepts. Eine Spracherweiterung, wo der Template-Entwickler direkt die Bedingungen formulieren kann. Das Feature wird aber aus Zeitgründen für C++0x fallen gelassen. Wird aber in einem nachfolgenden Standard natürlich wieder versucht! Die Idee ist also nicht gestorben.

    Bei solchen Dingen bleibt dir nichts übrig, als die Doku zu lesen. Und halt Erfahrung.

    Ja, habe ein Buch von Dirk Louis. Muss ich nochmal lesen, ich glaube da stand etwas in der Zeiger-Ecke, eine Giftküche, an der ich schnell vorbeigelesen hatte. 😃

    OK, Dirk Louis Bücher sind eher die schlechteren. Es gibt leider viele schlechte C++-Bücher auf dem Markt. Eben weil C++ so komplex ist und nicht jeder ein guter Lehrer sein kann. Hier mal zwei Bücher, die wirklich top sind:

    Der C++ Programmierer von Ulrich Breymann
    C++ Primer von Stanley B. Lippman / Josée Lajoie / Barbara E. Moo


Anmelden zum Antworten