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




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



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;
}

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.


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)?"":",");
    }
}

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();
}

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


-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;
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.