【问题标题】:c++: Format number with commas?c ++:用逗号格式化数字?
【发布时间】:2011-11-08 18:17:00
【问题描述】:

我想写一个方法,它接受一个整数并返回一个用逗号格式化的整数的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。)

【问题讨论】:

标签: c++ comma number-formatting


【解决方案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();
}

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

【讨论】:

  • 这个函数没有给我加逗号。我应该设置什么语言环境?我应该要求我的用户设置哪个语言环境?失败。
  • 如果没有使用特定语言环境的示例,答案是不完整的。让它工作需要学习整个语言环境机制。
  • 更具体地说,答案确实否认可移植性可能是一个问题,您可能应该查看传递“”时使用的语言环境。事实证明,这个答案在我的 Mac 上不能开箱即用,但是“查看使用的语言环境”需要进入语言环境的兔子洞。请参阅此问题以获得开箱即用的更好答案:stackoverflow.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() {
   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;
}   

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

【讨论】:

  • 这不适用于以 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() 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();
    }
    

    【讨论】:

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

    另辟蹊径:

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

    【讨论】:

      【解决方案8】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-05-31
        • 2020-12-18
        • 2017-12-09
        • 1970-01-01
        • 1970-01-01
        • 2010-10-16
        • 1970-01-01
        相关资源
        最近更新 更多