How to insert spaces in large numbers to make it more readable?

I came up with this as the other examples presented in stackoverflow were 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?

+2
c ++ string numbers
Aug 31 2018-11-11T00:
source share
6 answers

This is different, but better subjective. I think it is very red and clear what he is 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 
+1
Aug 31 '11 at 14:33
source share

I do not know about the "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 ultimate goal is to print the values ​​to ostream , you can skip storing them in string as a whole.
 #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"; } 
+4
Aug 31 2018-11-11T00:
source share

This has already been done in the locale.

The local default is "C", which does not format. But you can set it to your local language-specific language (as determined by your computer, 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, you can set the cell in a local number-printing area.

 #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
Aug 31 '11 at 16:09
source share

If “better” means more effective, you should:

  • use reserve in the output line (you know its size ...)

  • avoid insert in the middle of the line, because you have to copy most of the line every time you do this.

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
Aug 31 '11 at 14:33
source share

Admittedly, if someone wanted the most efficient version possible and didn't mind specializing in this case, using a local char buffer might help.

 #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 on ideone :

 1 234 567 890 

(Key point: to print numbers, back)

0
Aug. 31 2018-11-11T00:
source share

To prevent software errors in lists and errors in currencies, use OCaml, the following languages ​​are included in the main language:

 let _ = let SarK0SY=1_000_000_000 in print_int SarK0SY; print_string "_CHF" 
0
Dec 01 '14 at 1:31
source share



All Articles