开发者

How to insert spaces in a big number to make it more readable?

开发者 https://www.devze.com 2023-04-01 05:40 出处:网络
I came up with this, since other examples provided on stackoverflow w开发者_如何学Goere in C# string number_fmt(ulong n)

I came up with this, since other examples provided on stackoverflow w开发者_如何学Goere in 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;
}

Do you know any better solution ?


I don't know about "better", but this version uses std::locale, etc.

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


EDIT: Of course, if your final goal is to print the values on an ostream, you can skip storing them in a string altogether.

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


This is already done by the locale.

The default local is "C" which does no formatting. But you can set it to your current language-specific local (as defined by your computer's setting by setting the current local as the first line of 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.

If you want to do something explicitly then you can set a facet in the local for printing numbers.

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


This one is different, but better is subjective. I think it's very succinct and clear what it's doing though:

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

Using it like

cout << number_fmt(43615091387465) << endl;

prints

43,615,091,387,465


Admittedly, if one wanted to have the most possible efficient version and didn't mind specializing it for the case at hand, using a local char buffer can help a lot.

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

In action at ideone:

1 234 567 890

(Key point: for number printing, go backward)


Not very optimal but small

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


If "better" means more efficient, you should:

  1. use reserve on the output string (you know its size...)

  2. avoid the insert in the middle of the string, because you have to copy a big part of the string each time you do that.

I would say something like this (untested):

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

精彩评论

暂无评论...
验证码 换一张
取 消