Zahl mit Tausenderpunkten darstellen



  • Hallo Leute,

    ich weiß, dass diese Frage für viele Profis hier ziemlich peinlich sein dürfte. Ich komme eher aus der PHP Gegend und da gibt es format_number.

    Für C++ habe ich nach einigem gesucht, aber komischerweise nichts richtiges gefunden, obwohl das Problem sicherlich schon tausendfach gelöst wurde. Daher schonmal sorry.

    Ich habe Folgendes:
    sprintf(sText, "%i / sec", iCFDRate);

    dieses iCFDRate würde ich gerne mit Tausenderpunkte darstellen, damit bei meiner Ausgabe über
    SendDlgItemMessage(m_hwndTabDataInfo, IDC_STATIC_RATE, WM_SETTEXT, 0, (LPARAM)sText);
    folgendes geschrieben steht:
    250.000

    kann mir hierbei bitte jemand helfen?

    Vielen Dank und Grüße,
    Jonson



  • SendDlgItemMessage( m_hwndTabDataInfo,
                        IDC_STATIC_RATE,
                        WM_SETTEXT,
                        0,
                        (std::stringstream() << std::setprecision(6) << iCFDRate << " / sec").str().c_str() );
    

    Quick and dirty Lösung.



  • Ethon schrieb:

    ...

    Nö. Wo kommen denn da Tausenderpunkte?

    Ich kenne aber leider auch keine gute Lösung.



  • boost::lexical_cast fügt meines Wissens nach standardmäßig Tausenderpunkte hinzu.



  • Jonson86 schrieb:

    Hallo Leute,

    ich weiß, dass diese Frage für viele Profis hier ziemlich peinlich sein dürfte.

    Hallo Jonson,

    das muss Dir gar nicht peinlich sein. Sondern eher den C++-Profis. Ich schätze, dass mehr als die Hälfte derselben nicht auf Anhieb wissen, wie das geht 😉 .

    Jonson86 schrieb:

    Ich habe Folgendes:
    sprintf(sText, "%i / sec", iCFDRate);

    dieses iCFDRate würde ich gerne mit Tausenderpunkte darstellen, damit bei meiner Ausgabe über
    SendDlgItemMessage(m_hwndTabDataInfo, IDC_STATIC_RATE, WM_SETTEXT, 0, (LPARAM)sText);
    folgendes geschrieben steht:
    250.000

    Wenn Du Tausenderpunkte (also '.') und nicht Tausenderkommas (',') verlangst, so handelt es sich um die im deutschen Sprachraum übliche Trennung von Tausenderstellen. Dann reicht es i.A. einen Ausgabestream auf die deutsche Locale umzustellen. Da Du in Deinem Fall das Ergebnis als String benötigst, ist std::ostringstream das Mittel der Wahl.

    Im Prinzip so:

    int iCFDRate = 250000;
        std::ostringstream buf;
        buf.imbue( std::locale("german") ); // Der String "german" funktioniert unter Windows, kann sonst abweichen
        buf << iCFDRate << " / sec";
        SendDlgItemMessage( .., (LPARAM)buf.str().c_str()); // Textinhalt: "250.000 / sec"
    

    Benötigte Includes sind:

    #include <locale> // std::locale
    #include <sstream> // std::ostringstream
    

    Beachte bitte, dass durch die Umstellung auf 'deutsch' auch der Dezimaltrenner von '.' nach ',' wechselt. Solange Du nur int-Variablen ausgibst, spielt das aber keine Rolle.

    Gruß
    Werner



  • Vielleicht etwas Hintergrund dazu:

    Eine Locale ist in C++ ist ein Container von Facetten. Eine Facette übernimmt eine bestimmte Aufgabe, etwa die Formatierung von Zahlen, Geldbeträgen, Uhrzeiten oder sonstigem; sie kann zur Laufzeit aus der Locale herausgeholt und benutzt werden. Zum Beispiel kriegst du den Dezimaltrenner einer Locale durch

    std::locale loc;
    
    ...
    
    // std::numpunct<char> ist die benutzte Facette
    std::use_facet<std::numpunct<char> >(loc).decimal_point();
    

    Wenn die Locale die entsprechende Facette nicht beinhaltet, wird std::bad_cast geworfen.

    Die Umsetzung ist eine etwas haarige Angelegenheit, weil es jedem Benutzer freisteht, völlig neue Facetten mit beliebigem Interface einzuführen, die der Standardbibliothek nicht bekannt sind (dann allerdings auch nicht von ihr benutzt werden). Eine Facette ist technisch ein Objekt einer Kindklasse von std::locale::facet. Die Locale speichert Zeiger auf std::locale::facet, die bei der Verwendung in den passenden Typen zurückgecastet werden (also per Downcast), somit handelt es sich bei ihr um einen polymorphischen Container - das sind die, von deren Verwendung dir jeder fähige Programmierer unter fast allen Umständen abrät. Jede Facetten(basis)klasse hat einen eindeutigen, statischen ID-Member, damit das nicht böse ins Auge gehen kann.

    Wenn einem jetzt bestimmte Aspekte (bzw. Facetten) einer Locale nicht gefallen, kann man von einer der Standardfacetten ableiten, das Verhalten ändern und die eigene Facette in eine Locale stopfen. Diese kann man dann einem Stream zur Benutzung andrehen.

    Im konkreten Fall könnte das beispielsweise so aussehen:

    #include <iostream>
    #include <locale>
    #include <sstream>
    #include <string>
    
    // Hier unsere eigene Facette:
    class de_numpunct : public std::numpunct<char> {
    public:
      // refs ist zur Referenzzählung. Die letzte Locale räumt die Facette auf.
      explicit de_numpunct(std::size_t refs = 0)
        : std::numpunct<char>(refs) { }
    
    protected:
      // Komma als Dezimaltrenner
      virtual char_type do_decimal_point() const { return ','; }
    
      // Punkt als Gruppentrenner
      virtual char_type do_thousands_sep() const { return '.'; }
    
      // Und eine Gruppe soll jeweils drei Ziffern breit sein.
      virtual string_type do_grouping() const { return "\x003"; }
    };
    
    int main() {
      // Die Facette in eine Locale stopfen
      std::locale loc(std::locale(), new de_numpunct());
    
      std::ostringstream fmt;
    
      // ...und die Locale widerum in den Stream.
      fmt.imbue(loc);
    
      // Dieser formatiert dann unserer Facette entsprechend.
      fmt << 123456789;
      std::string s = fmt.str();
      std::cout << s << std::endl;
    }
    

    Diese neue Facette zählt für die Locale als std::numpunct<char>, weil sie ihre ID erbt. So wird sie, wenn der Stream std::numpunct<char> von der Locale anfordert, zurückgegeben und benutzt.

    Alle Standardfacetten kann ich dir in einem Forumspost natürlich nicht beschreiben; du findest eine Referenz zum Beispiel hier. Die Erstellung ganz eigener Facetten (im Gegensatz zur Ableitung von Standardfacetten) ist eher selten wirklich sinnvoll, aber wenn es dich interessiert, kann ich dazu gern auch ein kurzes Beispiel zusammenschustern.

    Mit der Benutzung vorgefertigter Facetten ist das so eine Sache. Grundsätzlich gibt es zwei Probleme:

    1. Man muss herausfinden, welche Facetten es gibt und welche man davon benutzen soll. Ich neige dazu, das irgendwo in der Konfiguration einstellbar zu machen, dann ist es nicht mehr dein Problem und ggf. auch nicht mehr deine Schuld.

    2. Man weiß nicht genau, was dabei am Ende herauskommt. Hinter Systemlocales kann sich eine Menge verbergen; unter Windows kann der Benutzer in der Systemsteuerung beispielsweise viele Schrauben drehen, wie er lustig ist.

    Das bedeutet praktisch: Systemlocales zu benutzen macht dann Sinn, wenn dir nicht wichtig ist, was genau dabei herauskommt, sondern lediglich, dass es mit dem übereinstimmt, was der Benutzer gewohnt ist. Das ist in GUIs fast immer der Fall, aber bei der Ausgabe von maschinenlesbaren Daten (beispielsweise CSV-Export) kann einem so etwas das Genick brechen.



  • Super, vielen Dank für Eure Hilfe und die wirklich tollen Antworten!!!

    Viele Grüße,
    Jonson


Log in to reply