FAQ - Umwandlung nach std::string und zurück



  • Es gibt viele Methoden, um zwischen std::string und anderen Typen umzuwandeln.
    Dieser Artikel zeigt, wie man es richtig macht, welche Probleme manche Methoden haben und welche man nur in Ausnahmefällen verwenden sollte.

    Umwandlungen über boost::lexical_cast

    Am einfachsten ist es, lexical_cast aus boost zu verwenden. Boost ist eine riesige Bibliothek an Funktionen und Klassen, die einem das Leben etwas leichter machen, jeder C++ Programmierer sollte sie haben. Boost kann kostenlos unter www.boost.org heruntergeladen werden.

    Eingebaute Typen wie int können wir damit ohne weiteren Aufwand umwandeln:

    #include <boost/lexical_cast.hpp>
    #include <string>
    
    int main()
    {
        int als_int = 42;
    
        std::string als_string = boost::lexical_cast<std::string>(als_int); // "42"
        int wieder_als_int = boost::lexical_cast<int>(als_string); // 42
    }
    

    Sollte eine Umwandlung nicht gelingen, versucht man z.B. aus "C++" eine Zahl zu machen, wirft lexical_cast eine bad_lexical_cast Exception.

    Mit lexical_cast können wir auch eigene Typen umwandeln:

    #include <boost/lexical_cast.hpp>
    #include <ostream>
    #include <istream>
    #include <iomanip>
    #include <string>
    
    struct person
    {
    	std::string name;
    	unsigned alter;
    
    	// Wenn wir einen std::string in einen eigenen Typen casten wollen, brauchen wir einen Standardkonstruktor.
    	person()
    	{}
    
    	person(std::string name, unsigned alter)
    		: name(name)
    		, alter(alter)
    	{}
    };
    
    // Das casten nach string geschieht ganz einfach über den operator <<, den wir ganz normal schreiben.
    std::ostream& operator << (std::ostream& os, const person& p)
    {
    	// Wir trennen Name und Alter durch ein Leerzeichen, damit das Einlesen etwas leichter wird.
    	return os << p.name << ' ' << p.alter;
    }
    
    // Beim Einlesen müssen wir prüfen, ob Leerzeichen ausgelassen werden oder nicht.
    std::istream& operator >> (std::istream& is, person& p)
    {
    	// Wir holen uns die Formatflags, in denen steht, ob Leerzeichen ausgelassen werden.
    	std::ios_base::fmtflags flags = is.flags();
    
    	// Wenn Leerzeichen nicht ausgelassen werden, aktivieren wir das.
    	if(!(flags & std::ios::skipws))
    		is >> std::skipws;
    
    	// Jetzt können wir ganz normal einlesen.
    	is >> p.name >> p.alter;
    
    	// Zu sauberer Programmierung gehört auch, dass wir den alten Status wiederherstellen.
    	is.flags(flags);
    
    	return is;
    }
    
    int main()
    {
    	person als_person("Marc++us", 40); // Unser Foren-Administrator
    
    	std::string als_string = boost::lexical_cast<std::string>(als_person); // Umwandlung nach std::string
    	person wieder_als_person = boost::lexical_cast<person>(als_string); // Umwandlung zurück in eine Person
    }
    

    Umwandlungen über std::stringstream
    Mit std::stringstream können wir auch umwandeln, um genau zu sein macht boost::lexical_cast genau das - nur etwas komfortabler - für uns.

    Hier kommt noch was, ich fahr jetzt nach Hause. Feedback wäre schon mal super 😃


  • Mod

    Zu laaaaaaaannnnng! Das liest doch keiner durch, wenn er sofort von 50+ Zeilen Code erschlagen wird. Wozu das ganze Personenbeispiel? Als Leser dieses FAQ-Eintrages will ich nicht Grundlagen von C++ wiederholen, sondern einfach eine kurze Funktion, die mir aus einem String eine Zahl macht oder umgekehrt.



  • Kann man ja ans Ende des Artikels schieben 🙄


  • Administrator

    Lies die FAQ!
    http://www.c-plusplus.net/forum/39488

    lol, perfekt. Jemand verfasst einen Artikel für die FAQ, welche er selbst gar nicht liest :p
    Man könnte sich höchstens mal überlegen, dem FAQ Artikel einen Zusatz mit boost::lexical_cast zu geben. Ist meiner Meinung nach fragwürdig, schliesslich ist Boost immer noch "nur" ein Framework.

    Grüssli



  • Viel wichtiger als boost::lexical_cast sind die direkten Stringumwandlungsfunktionen der C++0x-Standardbibliothek, also std::to_string() , std::stoi() und Co. Siehe hier

    Davon abgesehen ist dein Code viel zu komplex. Operatorüberladung und Stream-Flags gehören nicht in einen Artikel über String-Umwandlung.



  • Ich kenne den Artikel. Wie schon im anderen Thread erwähnt finde ich ihn bestenfalls mangelhaft.



  • Wollte ich noch hinzufügen, Nexus.


  • Administrator

    Sorry, mein Beitrag vorher war etwas zu scharf und nicht gut genug. War noch am bearbeiten, da habe ich versehentlich auf Absenden geklickt. Und kurz darauf wurde er versehentlich von einem verwirrten Mod (;)) in die FAQ verschoben, wodurch ich nicht mehr editieren konnte 😃

    Naja, also:
    Wie gesagt, wir haben schon eine FAQ Artikel für std::string nach Zahl und zurück. Da sieht man auch gut, wie so ein FAQ Artikel aussehen muss. Kurz und einfach.

    Zusätzlich haben wir noch mindestens zwei Artikel, wo das Überladen von den Stream-Operatoren vorkommt:
    Überladen von Operatoren in C++ (Teil 1)
    Ein- und Ausgabe in C++ - IO-Streams

    Ich würde da wirklich etwas massiv kurzes Schreiben und auf den FAQ-Artikel sowie die beiden Magazin-Artikel verweisen.

    Grüssli



  • Falls man den FAQ-Artikel verbessern will, schlage ich vor, std::strstream (deprecated) sowie die C-Möglichkeiten nicht mehr zu erwähnen. Allenfalls könnte man letztere in der C-FAQ auflisten.

    Also nur noch:

    • C++98: std::stringstream
    • C++0x: std::to_string() & Co.
    • Boost: boost::lexical_cast

    Aber liest überhaupt jemals jemand die FAQ? Vor allem gibt es etwa 50 andere unwichtige Threads in diesem Bereich, zwischen denen sowas nur untergeht. Dann gibt es noch den Thread "die meistgestellten Fragen". Klar muss man irgendwo anfangen, aber wenn man die FAQ verbessert, müsste man am besten ein paar einzelne gute Beiträge behalten und den Rest komplett löschen. Vor allem hat eine Forendiskussion nichts in einem FAQ-Thread zu suchen.



  • person(std::string name, unsigned alter)
    : name(name)
    , alter(alter)
    {}

    *stirnklatsch*
    So benennt ein selbsternannter Profi-C++ler also seine member und parameter.



  • Das war genau mein Vorschlag bezüglich des Inhalts @ Nexus 🙂



  • no_comment schrieb:

    person(std::string name, unsigned alter)
    : name(name)
    , alter(alter)
    {}

    *stirnklatsch*
    So benennt ein selbsternannter Profi-C++ler also seine member und parameter.

    Und was gibts daran auszusetzen? Weiters habe ich mich nirgens als C++ Profi bezeichnet.



  • Der Ansatz sieht schonmal recht brauchbar aus, allerdings läuft zumindest das Beispiel mit den selbstdefinierten IO-Operatoren über's Ziel hinaus. Selbst wenn du mit dem alten FAQ-Beitrag inhaltlich nicht zufrieden bist, könntest du zumindest die grundlegende Struktur übernehmen.



  • Ich schreib sonst mal einen Entwurf.



  • Hier wäre der Entwurf. Ich habe die Konvertierungen von und zu Strings jeweils zusammengefasst, damit die Ähnlichkeit deutlicher wird. Weiterführende Themen wie Fehlerbehandlung oder zusätzliche Parameter von stoi() etc. habe ich der Einfachheit halber weggelassen. Ich wäre froh um Rückmeldungen und allfällige Verbesserungen.

    In C++ gibt es verschiedenste Möglichkeiten, Konvertierungen zwischen Zeichenketten (z.B. std::string ) und arithmetischen Typen (z.B. int , double ) vorzunehmen. Im Folgenden sollen die wichtigsten davon erklärt werden.

    1. Standard-Konvertierungsfunktionen

    Standardbibliotheken, die den C++11-Standard unterstützen, bieten diverse überladene Funktionen im Header <string> an, welche eine direkte Umwandlung zwischen Strings und eingebauten Typen unterstützen. Die Konvertierung von arithmetischen Typen zu Strings erfolgt mit den folgenden überladenen Funktionen:

    • to_string() zur Konvertierung in std::string
    • to_wstring() zur Konvertierung in std::wstring

    In umgekehrter Richtung existieren folgende Funktionen, die jeweils für std::string - und std::wstring -Parameter überladen sind:

    • stoi() für int
    • stol() für long
    • stoll() für long long
    • stoul() für unsigned long
    • stoull() für unsigned long long
    • stof() für float
    • stod() für double
    • stold() für long double

    Die Anwendung sieht folgendermassen aus:

    #include <string>
    
    int main()
    {
        // Zahl nach String
        double      a = 4.72;
        std::string x = std::to_string(a);
    
        // String nach Zahl
        std::string y = "5.67";
        double      b = std::stod(y);
    }
    

    2. Stringstreams

    Eine weitere Möglichkeit zur Umwandlung bietet die Klasse std::stringstream . Falls nur Konvertierungen in eine Richtung erforderlich sind, kann std::ostringstream für Output- und std::istringstream für Input-Streams verwendet werden. Für Wide-Strings ( std::wstring ) existieren entsprechende Klassen mit vorangestelltem "w", z.B. std::wostringstream .

    Streams zahlen sich gegenüber den direkten Umwandlungsfunktionen vor allem dann aus, wenn mehrere Werte verkettet werden oder spezielle Stream-Funktionalität (wie Formatflags) erforderlich ist. Die Handhabung ist ähnlich zu std::cout und std::cin , zum Setzen und Auslesen des Strings können die überladenen Methoden str() verwendet werden.

    #include <sstream>
    #include <string>
    
    int main()
    {
        double a = 4.72;
        int    b = 88;
    
        // Output-Stream: Zahlen zu String zusammenfügen
        std::ostringstream oss;
        oss << a << ' ' << b;
        std::string x = oss.str();
    
        // Input-Stream: Zahlen aus String extrahieren
        std::istringstream iss;
        iss.str("5.67 12"); // oder direkt Konstruktor verwenden
        iss >> a >> b;
    }
    

    3. Boost.LexicalCast

    Boost ist eine Sammlung von C++-Bibliotheken und enthält unter anderem Funktionalität zur Umwandlung zwischen arithmetischen Datentypen und Zeichenketten. Dazu dient das Funktionstemplate boost::lexical_cast<T> , wobei T den Zieltypen der Konvertierung bezeichnet. Die Syntax ist analog zu den C++-Cast-Operatoren wie z.B. static_cast<T> .

    Gegenüber der ersten Variante besteht hier der Vorteil, dass keine C++11-Unterstützung notwendig ist. Im Weiteren ist die Syntax für alle Umwandlungen einheitlich, was generische Programmierung vereinfacht.

    #include <boost/lexical_cast.hpp>
    #include <string>
    
    int main()
    {
        // Zahl nach String
        double      a = 4.72;
        std::string x = boost::lexical_cast<std::string>(a);
    
        // String nach Zahl
        std::string y = "5.67";
        double      b = boost::lexical_cast<double>(y);
    }
    


  • Finde ich gut, allerdings sollte zumindest bei boost::lexical_cast auf die Fehlerbehandlung kurz eingegangen werden und bei Streams auf die Referenz verwiesen werden, meiner Meinung nach.



  • 314159265358979 schrieb:

    Finde ich gut, allerdings sollte zumindest bei boost::lexical_cast auf die Fehlerbehandlung kurz eingegangen werden und bei Streams auf die Referenz verwiesen werden, meiner Meinung nach.

    Ich schlage vor, Du kopierst und änderst (machst debei die Änderungen kenntlich). Der nächste kopiert und ändert weiter. Und wenn alle Protagonisten damit zufrieden sind, wird's wohl so gut sein, daß es (unter Streichung aller Änderungsvermerke) in die FAQ gehört.



  • Meinst du das erst oder zynisch? 😃



  • Dann kann man auch gleich ein Mercurial-Repository aufsetzen. 😃



  • 314159265358979 schrieb:

    Meinst du das erst oder zynisch? 😃

    Ich glaube an das Gute.
    Ich glaube daran, daß der Artikel schon fast fertig ist und binnen Tagen gegen eine nahezu perfekte Fassung konvergieren wird.



  • volkard schrieb:

    Ich glaube daran, daß der Artikel schon fast fertig ist und binnen Tagen gegen eine nahezu perfekte Fassung konvergieren wird.

    ist der Konvergenzradius denn groß genug? Und ist sicher, dass der Prozess eine ständig steigende Nutzenfunktion zu Folge hat? Gibt es vielleicht mehrere gute Lösungen mit suboptimalen Transienten dazwischen?


Anmelden zum Antworten