c ++: Nummer mit Kommas formatieren?

73

Ich möchte eine Methode schreiben, die eine Ganzzahl verwendet und eine std::stringdieser mit Kommas formatierten Ganzzahlen zurückgibt.

Beispieldeklaration:

std::string FormatWithCommas(long value);

Anwendungsbeispiel:

std::string result = FormatWithCommas(7800);
std::string result2 = FormatWithCommas(5100100);
std::string result3 = FormatWithCommas(201234567890);
// result = "7,800"
// result2 = "5,100,100"
// result3 = "201,234,567,890"

Wie formatiert C ++ eine Zahl als stringKomma?

(Bonus wäre, auch mit doubles umzugehen.)

Nutzer
quelle
1
Was an diesen doppelten Aussagen lahm ist, ist, dass ich nach dieser Frage gesucht habe, bevor ich die offensichtlichsten Suchbegriffe verwendet habe, und keine dieser Fragen gefunden habe. Mein Titel ist besser und auf den Punkt gebracht und ich mag die akzeptierte Antwort auf meine Frage besser als die Antworten auf diese.
Benutzer
Wenn hohe Leistung ein Problem ist, können Sie meine verwandte Frage sehen: Wie kann ich die Formatierungsnummer mit der Komma-Leistung verbessern?
Benutzer

Antworten:

57

Verwenden Sie std::localemitstd::stringstream

#include <iomanip>
#include <locale>

template<class T>
std::string FormatWithCommas(T value)
{
    std::stringstream ss;
    ss.imbue(std::locale(""));
    ss << std::fixed << value;
    return ss.str();
}

Haftungsausschluss: Portabilität kann ein Problem sein, und Sie sollten sich wahrscheinlich ansehen, welches Gebietsschema verwendet wird, wenn ""es übergeben wird

Jakob
quelle
3
Diese Funktion hat für mich keine Kommas gesetzt. Welches Gebietsschema soll ich einstellen? Welches Gebietsschema soll ich von meinen Benutzern festlegen lassen? Scheitern.
Jonny
Die Antwort ist ohne Beispiel für die Verwendung eines bestimmten Gebietsschemas nicht vollständig. Um es zum Laufen zu bringen, muss man die gesamte Gebietsschema-Maschinerie lernen.
Reb.Cabin
Insbesondere wurde in der Antwort abgelehnt, dass Portabilität ein Problem sein könnte, und Sie sollten sich wahrscheinlich ansehen, welches Gebietsschema verwendet wird, wenn "" übergeben wird. Es stellt sich heraus, dass diese Antwort auf meinem Mac nicht sofort funktioniert, aber "um zu sehen, welches Gebietsschema verwendet wird", muss das Gebietsschema-Kaninchenbau heruntergefahren werden. Sehen Sie diese Frage für eine bessere Antwort, die sofort
funktioniert
53

Sie können tun, was Jacob vorgeschlagen hat, und zwar imbuemit dem ""Gebietsschema - dies verwendet jedoch die Systemvorgabe, die nicht garantiert, dass Sie das Komma erhalten. Wenn Sie das Komma erzwingen möchten (unabhängig von den Standardeinstellungen des Systems), können Sie dies tun, indem Sie Ihre eigene numpunctFacette angeben. Zum Beispiel:

#include <locale>
#include <iostream>
#include <iomanip>

class comma_numpunct : public std::numpunct<char>
{
  protected:
    virtual char do_thousands_sep() const
    {
        return ',';
    }

    virtual std::string do_grouping() const
    {
        return "\03";
    }
};

int main()
{
    // this creates a new locale based on the current application default
    // (which is either the one given on startup, but can be overriden with
    // std::locale::global) - then extends it with an extra facet that 
    // controls numeric output.
    std::locale comma_locale(std::locale(), new comma_numpunct());

    // tell cout to use our new locale.
    std::cout.imbue(comma_locale);

    std::cout << std::setprecision(2) << std::fixed << 1000000.1234;
}
Knoten
quelle
Nur neugierig, ob Sie Ihr Beispiel lange bearbeiten können, anstatt zu schweben, da ich danach gesucht habe (und das ist, was die Frage stellt)
Fellow Traveller
1
@FellowTraveler es ist das gleiche, einfach tun std::cout << myLongValue;.
Knoten
Warum funktioniert das für die Longs auch ohne Verrohrung std :: fixed? (Habe das Doppel nicht ausprobiert).
Ajeh
Das ist toll! Sie müssen sich jetzt nicht mehr mit UTF-8-Gebietsschemas herumschlagen!
ForceBru
Bei weitem eine der besten Möglichkeiten, dies zu tun
Vivick
36

Ich halte die folgende Antwort für einfacher als die anderen:

#include <iostream>
int main() {
   auto s = std::to_string(7654321);
   int n = s.length() - 3;
   while (n > 0) {
      s.insert(n, ",");
      n -= 3;
   }
   std::cout << (s == "7,654,321") << std::endl;
}   

Dadurch werden schnell und korrekt Kommas in Ihre Ziffernfolge eingefügt.

Carljalal
quelle
Dies funktioniert nicht mit Werten, denen Nullen wie 010100
PapaDiHatti
@ Homer6 Das Problem mit negativen Zahlen könnte durch eine geringfügige Anpassung des Codes behoben werden. Wenn die Zahl negativ ist, sollte das while-Schleifenkriterium insertPosition> 1 ... sein. Für -106 würde insertPosition bei 1 beginnen und kein Komma eingefügt.
Cardiff Space Man
@ Kapil-Zahlen mit vorangestellten Nullen wie 010100 würden funktionieren: Sie würden insertPosition == 3 zum Starten erhalten, Ihr Komma würde zwischen der 3. und 4. Stelle stehen und das war's. Können Sie weiter erklären, wie eine solche Ziffernfolge fehlschlagen würde?
Cardiff Space Man
@arljalal Ich mag den Code sehr. Meiner Meinung nach ist es nur ein Fehler, wenn wirklich lange Zahlen üblich sind, ist es O (Länge im Quadrat). Die while-Schleife wird O (Länge) Mal ausgeführt und überträgt jedes Mal O (Länge) Ziffern. Ein Algorithmus, der mit durch Kommas getrennten Blöcken arbeitet, könnte insgesamt O (Länge) sein. Die meisten von uns werden 32-Bit- oder 64-Bit-Zahlen formatieren, sodass das Problem geringfügig ist.
Cardiff Space Man
2

Dies ist eine ziemlich alte Schule. Ich verwende sie in großen Schleifen, um zu vermeiden, dass ein weiterer Zeichenfolgenpuffer instanziiert wird.

void tocout(long a)
{
    long c = 1;

    if(a<0) {a*=-1;cout<<"-";}
    while((c*=1000)<a);
    while(c>1)
    {
       int t = (a%c)/(c/1000);
       cout << (((c>a)||(t>99))?"":((t>9)?"0":"00")) << t;
       cout << (((c/=1000)==1)?"":",");
    }
}
Tom Serink
quelle
Ich mag das (außerhalb des Mangels an Leerzeichen zwischen den Betreibern). Obwohl die Division durch 1.000 auf neueren Prozessoren wahrscheinlich schnell ist, können Sie einen Puffer auf dem Stapel zuweisen und die Zahl in umgekehrter Reihenfolge generieren und jedes Zeichen drucken. Alle drei Zeichen geben Sie auch ein Komma aus ...
Alexis Wilke
1

Basierend auf den obigen Antworten kam ich zu folgendem Code:

#include <iomanip>
#include <locale> 

template<class T>
std::string numberFormatWithCommas(T value){
    struct Numpunct: public std::numpunct<char>{
    protected:
        virtual char do_thousands_sep() const{return ',';}
        virtual std::string do_grouping() const{return "\03";}
    };
    std::stringstream ss;
    ss.imbue({std::locale(), new Numpunct});
    ss << std::setprecision(2) << std::fixed << value;
    return ss.str();
}
Radif Sharafullin
quelle
Dies ruft undefiniertes Verhalten hervor ( doppelt frei oder Korruption) in meinem Test), da Sie einen Zeiger auf eine Facette übergeben, die nicht von zugewiesen wurde new. Verwenden Sie entweder newwie in anderen Antworten oder setzen Sie die Basisklasse refcount im Konstruktor Ihrer Facette auf 1!
Cubbi
Vielen Dank für den Hinweis. Ich habe es nur auf iOS getestet, wo es funktioniert hat. Es hat nicht für Mac funktioniert.
Radif Sharafullin
1

Wenn Sie Qt verwenden, können Sie diesen Code verwenden:

const QLocale & cLocale = QLocale::c();
QString resultString = cLocale.toString(number);

Vergessen Sie auch nicht, hinzuzufügen #include <QLocale>.

tro
quelle
-1

Um es flexibler zu machen, können Sie die Facette mit einer benutzerdefinierten Zeichenfolge für Tausende und Gruppen erstellen. Auf diese Weise können Sie es zur Laufzeit einstellen.

#include <locale>
#include <iostream>
#include <iomanip>
#include <string>

class comma_numpunct : public std::numpunct<char>
{
public:
   comma_numpunct(char thousands_sep, const char* grouping)
      :m_thousands_sep(thousands_sep),
       m_grouping(grouping){}
protected:
   char do_thousands_sep() const{return m_thousands_sep;}
   std::string do_grouping() const {return m_grouping;}
private:
   char m_thousands_sep;
   std::string m_grouping;
};

int main()
{

    std::locale comma_locale(std::locale(), new comma_numpunct(',', "\03"));

    std::cout.imbue(comma_locale);
    std::cout << std::setprecision(2) << std::fixed << 1000000.1234;
}
Marcel
quelle