如何在大数字中插入空格以使其更具可读性?

时间:2011-08-31 13:53:20

标签: c++ string numbers

我想出了这个,因为stackoverflow上提供的其他示例都在C#

string number_fmt(ulong n)
{
    // cout << "(" << n << ")" << endl;
    char s[128];
    sprintf(s, "%lu", n);
    string r(s);
    reverse(r.begin(), r.end());
    int space_inserted = 0;
    size_t how_many_spaces = r.length() / 3;

    if(r.length() % 3 != 0)
        how_many_spaces += 1;

    for(int i = 1; i < how_many_spaces; ++i)
    {
        r.insert(3 * i + space_inserted, " ");
        space_inserted += 1;
    }
    reverse(r.begin(), r.end());

    return r;
}

你知道更好的解决方案吗?

6 个答案:

答案 0 :(得分:5)

我不知道“更好”,但此版本使用std::locale等。

#include <iostream>
#include <locale>
#include <sstream>

template<class Char>
class MyFacet : public std::numpunct<Char> {
public:
  std::string do_grouping() const { return "\3"; }
  Char do_thousands_sep() const { return ' '; }
};

std::string number_fmt(unsigned long n)
{
  std::ostringstream oss;
  oss.imbue(std::locale(oss.getloc(), new MyFacet<char>));
  oss << n;
  return oss.str();
}

int main() {
  std::cout << number_fmt(123456789) << "\n";
}

<小时/> 编辑:当然,如果您的最终目标是在ostream上打印值,则可以跳过将它们全部存储在string中。

#include <iostream>
#include <locale>
#include <sstream>
#include <cwchar>

template <class Char>
class MyFacet : public std::numpunct<Char> {
public:
  std::string do_grouping() const { return "\3"; }
  Char do_thousands_sep() const { return ' '; }
};

int main(int ac, char **av) {
  using std::locale;
  using std::cout;

  // Show how it works to start with
  cout << 123456789 << "\n";

  // Switch it to spacey mode
  locale oldLoc =
    cout.imbue(locale(cout.getloc(), new MyFacet<char>));

  // How does it work now?
  cout << 456789123 << "\n";

  // You probably want to clean up after yourself
  cout.imbue(oldLoc);

  // Does it still work?
  cout << 789123456 << "\n";
}

答案 1 :(得分:3)

这已由语言环境完成。

默认本地为“C”,不进行格式化。但您可以将其设置为当前特定语言的本地(通过将当前本地设置为main的第一行来定义您的计算机设置。)

int main()
{
    std::locale::global(std::locale("")); // Set the default local of the machine
                                          // Will be used by all streams.
                                          // The "" will find the machine specific local
                                          // and use that instead of the "C" locale
                                          // Note: The C local should only be used for programmers.

    // Alternatively you can imbue particular stream with the local
    // To achieve a localized effect
    // std::cout.imbue(std::locale(""));

    // Now all you do is print the number.
    std::cout << "123456789\n";  // This will print the number according to your local
}                                // For me US-en this is   123,456,789
                                 // Your may very.

如果您想明确地执行某些操作,则可以在本地设置构面以打印数字。

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


template<typename CharT>
struct Sep : public std::numpunct<CharT>
{
        virtual std::string do_grouping()      const   {return "\003";}
        virtual CharT       do_thousands_sep() const   {return ':';}
};

int main()
{
        std::cout.imbue(std::locale(std::cout.getloc(), new Sep <char>()));

        std::cout << 123456789 << "\n";   // this prints 123:456:789
}

答案 2 :(得分:2)

这个是不同的,但更好是主观的。我认为它非常简洁明了,但它正在做什么:

string number_fmt(unsigned long long n, char sep = ',') {
    stringstream fmt;
    fmt << n;
    string s = fmt.str();
    s.reserve(s.length() + s.length() / 3);

    // loop until the end of the string and use j to keep track of every
    // third loop starting taking into account the leading x digits (this probably
    // can be rewritten in terms of just i, but it seems more clear when you use
    // a seperate variable)
    for (int i = 0, j = 3 - s.length() % 3; i < s.length(); ++i, ++j)
        if (i != 0 && j % 3 == 0)
            s.insert(i++, 1, sep);

    return s;
}

一样使用它
cout << number_fmt(43615091387465) << endl;

打印

43,615,091,387,465

答案 3 :(得分:0)

如果“更好”意味着更高效,您应该:

  1. 在输出字符串上使用reserve(你知道它的大小......)

  2. 避免字符串中间的insert,因为每次执行此操作时都必须复制字符串的大部分内容。

  3. 我会说这样的话(未经测试):

    std::string number_fmt (ulong n)
    {
      std::ostringstream buff;
      buff << n;
      std::string without_spaces = buff.str ();
      std::string with_spaces;
      with_spaces.reserve ((without_spaces.size () * 4) / 3);
      std::size_t nb_inserted = 0;
    
      for (auto it = without_spaces.rbegin (); it != without_spaces.rend (); ++it)
      {
         if (nb_inserted % 3  ==  0  &&  nb_inserted != 0)
         {
             with_spaces.push_back (' ');
         }
         ++ nb_inserted;
    
         with_spaces.push_back (*it);   
      }
      std::reverse (with_spaces.begin (), with_spaces.end ());
      return with_spaces;
    }
    

答案 4 :(得分:0)

不可否认,如果一个人想拥有最有效的版本并且不介意专门针对手头的情况,那么使用本地字符缓冲区可以提供很多帮助。

#include <iostream>
#include <string>

std::string format(unsigned long long i) {
  char buffer[128]; // can be adapted more tightly with std::numeric_limits

  char* p = buffer + 128;
  *(--p) = '\0';

  unsigned char count = 0;
  while (i != 0) {
    *(--p) = '0' + (i % 10);
    i /= 10;

    if (++count == 3) { count = 0; *(--p) = ' '; }
  }

  return p;
}

int main() {
  std::cout << format(1234567890) << '\n';
}

ideone的行动:

1 234 567 890

(关键点:数字打印,向后

答案 5 :(得分:0)

不是很好,但是很小

QString str = QString::number(d);
for (int i = 3; str.size() > i; i += 4)
   str.insert(str.size() - i, ' ');