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++ 方法是什么?
(奖励将是处理double
s。)
【问题讨论】:
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::locale
与std::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 << 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 <QLocale>
。
【讨论】:
【参考方案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 ++:用逗号格式化数字?的主要内容,如果未能解决你的问题,请参考以下文章
如何在EXCEL中把两列表格里的数字合成一列并且中间用逗号隔开?