c ++:用逗号格式化数字?

Posted

技术标签:

【中文标题】c ++:用逗号格式化数字?【英文标题】:c++: Format number with commas? 【发布时间】:2011-09-01 21:33:53 【问题描述】:

我想写一个方法,它接受一个整数并返回一个用逗号格式化的整数的std::string

示例声明:

std::string FormatWithCommas(long value);

示例用法:

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"

用逗号将数字格式化为string 的 C++ 方法是什么?

(奖励将是处理doubles。)

【问题讨论】:

How to insert spaces in a big number to make it more readable? 的可能重复项 How do you set the cout locale to insert commas as thousands separators?的可能重复 这些重复语句的不足之处在于,我在使用最明显的搜索词之前搜索了这个问题,但没有找到任何一个问题。我的标题更好,更切题,我更喜欢我的问题的公认答案,而不是任何这些答案。 如果高性能是一个问题,您可以查看我的相关问题:How can I improve formatting number with commas performance? 【参考方案1】:

std::localestd::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();

免责声明:可移植性可能是个问题,您可能应该查看在传递"" 时使用的语言环境

【讨论】:

@Rob Kennedy: ***.com/questions/4406895/… 这个函数没有给我加逗号。我应该设置什么语言环境?我应该要求我的用户设置哪个语言环境?失败。 如果没有使用特定语言环境的示例,答案是不完整的。让它工作需要学习整个语言环境机制。 更具体地说,答案确实否认可移植性可能是一个问题,您可能应该查看传递“”时使用的语言环境。事实证明,这个答案在我的 Mac 上不能开箱即用,但是“查看使用的语言环境”需要进入语言环境的兔子洞。请参阅此问题以获得开箱即用的更好答案:***.com/questions/3479485 在 2021 年用逗号在英国用“gcc 版本 10.3.0 (Ubuntu 10.3.0-1ubuntu1)”吐出数字【参考方案2】:

您可以按照 Jacob 的建议进行操作,并使用 "" 区域设置 imbue - 但这将使用系统默认值,这并不能保证您得到逗号。如果您想强制使用逗号(无论系统默认区域设置如何),您可以通过提供您自己的numpunct facet 来实现。例如:

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

【讨论】:

只是好奇您是否可以长时间编辑您的示例,而不是浮动,因为这就是我要搜索的内容(这就是问题所要问的。) @FellowTraveler 也一样,只需 std::cout &lt;&lt; myLongValue; 为什么即使没有管道std::fixed,这也适用于longs? (没有尝试双打)。 这太棒了!现在不需要弄乱 UTF-8 语言环境了! 迄今为止最好的方法之一【参考方案3】:

我认为以下答案比其他答案更容易:

#include <iostream>
int main() 
   int v = 7654321;
   auto s = std::to_string(v);

   int n = s.length() - 3;
   int end = (v >= 0) ? 0 : 1; // Support for negative numbers
   while (n > end) 
      s.insert(n, ",");
      n -= 3;
   
   std::cout << (s == "7,654,321") << std::endl;
   

这将快速、正确地将逗号插入到您的数字字符串中。

【讨论】:

这不适用于以 0 为前缀的值,例如 010100 @Homer6 负数的问题可以通过对代码的微小调整来解决。如果数字为负数,while 循环标准应为 insertPosition>1 ... for -106 insertPosition 将从 1 开始,并且不会插入逗号。 @Kapil 数字以零为前缀,例如 010100 会起作用:你会得到 insertPosition == 3 开始,你的逗号会在第 3 位和第 4 位数字之间,就是这样。你能进一步解释一下这样一串数字是如何失败的吗? @arljalal 我非常喜欢这段代码。在我看来,唯一的缺陷是如果真的很长的数字很常见,它就是 O(长度平方)。 while 循环运行 O(length) 次,每次传输 O(length) 个数字。适用于逗号分隔块的算法总体上可能是 O(length)。我们大多数人都会格式化 32 位或 64 位数字,所以问题很小。 @cardiffspaceman 您可以在 uint64_t 中存储的最大数字是 18,446,744,073,709,551,615。那是20位数。如果您使用的数字比这更大,并且实际上希望它们可读,那么科学记数法可能是要走的路。 18 位数字很难阅读。 tl; dr:我认为大 O 运行时在这里并不重要。也许如果您正在处理数百万位数的数字,并且您需要精确到一个位置。?【参考方案4】:

这是相当老派的,我在大循环中使用它以避免实例化另一个字符串缓冲区。

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

【讨论】:

我喜欢这样(除了运算符之间没有空格)。尽管在较新的处理器上除以 1,000 可能很快,但您可以在堆栈上分配一个缓冲区并反向生成数字并打印每个字符,每 3 个字符还输出一个逗号...【参考方案5】:

如果您使用的是 Qt,则可以使用以下代码:

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

另外,不要忘记添加#include &lt;QLocale&gt;

【讨论】:

【参考方案6】:

根据上面的答案,我最终得到了以下代码:

#include <iomanip>
#include <locale> 

template<class T>
std::string numberFormatWithCommas(T value)
    struct Numpunct: public std::numpunct<char>
    protected:
        virtual char do_thousands_sep() constreturn ',';
        virtual std::string do_grouping() constreturn "\03";
    ;
    std::stringstream ss;
    ss.imbue(std::locale(), new Numpunct);
    ss << std::setprecision(2) << std::fixed << value;
    return ss.str();

【讨论】:

这会调用未定义的行为(在我的测试中是双重释放或损坏),因为您传递了一个指向未由new 分配的构面的指针。要么在其他答案中使用new,要么在你的构面的构造函数中将基类引用计数设置为1! 感谢您的指出。我只在它工作的 iOS 上测试过它。它不适用于 mac。【参考方案7】:

我找到了解决方案!只需将其复制到您的一个函数中,此函数是用静态函数编写的。

// Convert 100000000 to 100,000,000, put commas on the numbers!

std::string AppManager::convertNumberToString(int number) 
    std::string s = std::to_string(number);
    std::string result = "";
    std::string tempResult = "";
    unsigned long n = s.length() - 3;
    int j = 0;
    for (int i=s.size()-1; i>=0; i--) 
        if (j%3 == 0) 
            result.append(",");
        
        result.append(s, i, 1);
        j++;
    
    
    result = result.substr(1, result.size()-1);
    
    //now revert back
    for (int i=result.size()-1; i>=0; i--) 
        tempResult.append(result, i, 1);
    
    
    return tempResult;

以下是这些代码的结果:

【讨论】:

【参考方案8】:

我见过很多这样做的方法,反转字符串(两次!),使用 setlocale(有时有效,有时无效) 这是一个模板解决方案,然后我添加了明确的专业化。这适用于 char*、wchar*、string 和 wstring。 我不在这里从数字格式转换为字符串格式,我强烈推荐 to_string 和 to_wstring 它们比 'C' 函数(例如 _itoa 等)要快得多......

    template<typename T, typename U>
    T StrFormatNumber(const T Data) 
const size_t Length = Data.length();
assert(Length > 0);
// if( 0 == Length ) I would log this and return 
if (Length < 4)  // nothing to do just return
    return Data;

constexpr size_t buf_size 256 ;
assert(((Length)+(Length / 3)) + 1 < buf_size);
if (((Length)+(Length / 3)) + 1 >= buf_size) 
     throw std::invalid_argument( "Input buffer too large" );

std::array<U, buf_size > temp_buf;
auto p 0 ;
temp_buf[0] = Data[0];
for (auto y 1 ; y < Length; y++) 
    if ((Length - y) % 3 == 0) 
        temp_buf[y + p] = ',';
        p++;
    
    temp_buf[(y + p)] = Data[y];

return temp_buf.data();

    template<typename T = const char*>
    std::string StrFormatNum(const char* Data) 
        return StrFormatNumber<std::string, char>(std::string(Data));
    
    template<typename T= std::string>
    std::string StrFormatNum(const std::string Data) 
         return StrFormatNumber<std::string, char>(Data);
     
    template<typename T = std::wstring>
    std::wstring StrFormatNum( const std::wstring Data) 
        return StrFormatNumber<std::wstring, wchar_t>(Data);
    
    template<typename T = const wchar_t*>
    std::wstring StrFormatNum( const wchar_t* Data) 
        return StrFormatNumber<std::wstring, wchar_t>(std::wstring(Data));
    

    void TestStrFormatNumber() 
        constexpr auto Iterations 180 ;
        for (auto l 0 ; l < Iterations; l++)
        
              // std::string
                std::string mystr "10" ;
                for (int y 0 ; y < Iterations; y++) 

                    mystr += "1";
                    auto p = mystr.length();
                    std::cout << "\r\n  mystr      = " 
                              << std::setw(80) << mystr.c_str()
                              << "\r\n  Length     = "
                              << std::setw(10) << p
                              << "\r\n  modulo % 3 = "
                              << std::setw(10)
                              << p % 3 << "     divided by 3 = "
                              << std::setw(10) << p / 3
                              << "\r\n  Formatted  = " << 
                    StrFormatNum((mystr)).c_str() << "\n";
                
            
              // std::wstring
                std::wstring mystr L"10" ;
                for (int y 0 ; y < Iterations; y++)
                
                     mystr += L"2";
                     auto p = mystr.length();
                     std::wcout << "\r\n  mystr      = "
                                << std::setw(80) << mystr.c_str()
                                << "\r\n  Length     = "
                                << std::setw(10) << p
                                << "\r\n  modulo % 3 = "
                                << std::setw(10) << p % 3
                                << "     divided by 3 = "
                                << std::setw(10) << p / 3
                                << "\r\n  Formatted  = "
                                << StrFormatNum((mystr)).c_str()
                                << "\n";
                        
                    
                       // char*
                        std::string mystr "10" ;
        for (int y 0 ; y < Iterations; y++) 
            mystr += "3";
            auto p = mystr.length();
            std::cout << "\r\n  mystr      = " << std::setw(80) << mystr.c_str()
                << "\r\n    Length     = " << std::setw(10) << p
                << "\r\n  modulo % 3 = " << std::setw(10) << p % 3 << "     divided by 3 = " << std::setw(10) << p / 3
                << "\r\n  Formatted  = " << StrFormatNum((mystr.c_str())).c_str() << "\n";
        
    
      // wchar*
        std::wstring mystr L"10" ;
        for (int y 0 ; y < Iterations; y++) 
            mystr += L"4";
            auto p = mystr.length();
            std::wcout << "\r\n  mystr      = " << std::setw(80) << mystr.c_str()
                << "\r\n  Length     = " << std::setw(10) << p
                << "\r\n  modulo % 3 = " << std::setw(10) << p % 3 << "     divided by 3 = " << std::setw(10) << p / 3
                << "\r\n  Formatted  = " << StrFormatNum((mystr.c_str())).c_str() << "\n";
        
     

我已经测试了多达 1000 个空格(当然还有更大的缓冲区)

【讨论】:

temp_buf[y + p] 算术溢出... 我已经对这段代码进行了数千次测试,我使用的是 Visual c++22,我曾经在 Visual c++ 19 上使用过它,但我从来没有经历过这种情况,我真的把它放在了数千次循环中从一个地方到数百个地方。你是在什么环境下发生这种情况的我很好奇 我也在使用最新的 C++ 和 VS2019,但平台是 x64 位。 我回家再试一次。 好的,谢谢??【参考方案9】:

另辟蹊径:

#include <stdio.h>
#include <string>
#include <stdint.h>
#include <inttypes.h>

std::string GetReadableNum(uint64_t n)

    std::string strRet;
    char szTmp[256] =  0 ;
    int ccWritten = sprintf(szTmp, "%" PRIu64 "", n);
    if (ccWritten > 0)
    
        int nGroup = (ccWritten + 2) / 3;
        int nReminder = ccWritten % 3;
        
        strRet.reserve(ccWritten + (nGroup -1) * 3 + 1);
        const char* p = szTmp;
        for (int i = 0; i < nGroup; i++)
        
            if (nGroup > 1 && i > 0)
                strRet.append(1, ',');

            for (int c = 0; c < (i > 0 || nReminder == 0 ? 3 : nReminder); c++)
                strRet.append(1, *p++);
        
    

    return strRet;


int main(int argc, const char* argv[])

    uint64_t a = 123456789123ULL;

    std::string s = GetReadableNum(a);

    printf("%s\n", s.c_str());

    return 0;

【讨论】:

【参考方案10】:

为了使其更加灵活,您可以使用自定义的千位分隔符和分组字符串来构建构面。 这样你就可以在运行时设置它。

#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() constreturn 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;

【讨论】:

以上是关于c ++:用逗号格式化数字?的主要内容,如果未能解决你的问题,请参考以下文章

用逗号格式化数字PHP [重复]

如何使文本框只接受数字并用逗号格式化数字?

Highcharts:用逗号格式化所有数字?

用逗号格式化数字以在 Python 中分隔千位

JavaScript 用逗号格式化数字

php用逗号格式化数字