8

I've got a simple class Currency with overloaded operator<<. I don't know how can i separate the number with spaces every 3 digits, so it looks like: "1 234 567 ISK".

#include <cstdlib>
#include <iostream>

using namespace std;

class Currency
{
    int val;
    char curr[4];

    public:
    Currency(int _val, const char * _curr)
    {
        val = _val;
        strcpy(curr, _curr);
    }

    friend ostream & operator<< (ostream & out, const Currency & c);
};

ostream & operator<< (ostream & out, const Currency & c)
{
    out << c.val<< " " << c.curr;
    return out;
}

int main(int argc, char *argv[])
{
    Currency c(2354123, "ISK");
    cout << c;
}

What interests me, is somehow the easiest solution for this particular situation.

5
  • 1
    @danben: How would tagging it as [homework] change anything about the question or how good answers should be judged?
    – Roger Pate
    Commented Apr 15, 2010 at 19:48
  • 2
    @Roger Pate: Tagging a question as homework lets the SO community know that they should be giving guidance and helping the poster come to the solution on his own rather than just writing the solution for him. See meta.stackexchange.com/questions/10811/….
    – danben
    Commented Apr 15, 2010 at 20:00
  • @danben: See meta.stackexchange.com/questions/10811/… and meta.stackexchange.com/questions/10811/…
    – Roger Pate
    Commented Apr 15, 2010 at 20:11
  • @Roger Pate - note that those links are to answers that got a relatively low score. Anyone can add any answer to any question, but given that SO policies are governed by the community, you really want to give the most weight to the highest-voted answer. Note also that what Joel was doing in that top post was outlining the policy that best reconciled the majority of opinions, and presumably it worked as it is the highest-voted answer.
    – danben
    Commented Apr 15, 2010 at 20:51
  • @danben: Joel also said "The other viewpoints don't have to out-vote this one to have an impact. .. If you take the group-think I accidentally created into account they are very close to succeeding."
    – Roger Pate
    Commented Apr 15, 2010 at 22:22

4 Answers 4

15

This can be done with facets

struct myseps : numpunct<char> { 
   /* use space as separator */
   char do_thousands_sep() const { return ' '; } 

   /* digits are grouped by 3 digits each */
   string do_grouping() const { return "\3"; }
};

int main() {
  std::cout.imbue(std::locale(std::locale(), new myseps));
  std::cout << 10000; // 10 000
}

Alternatively, you may code your own loop

void printGrouped(ostream &out, int n) {
  if(n < 0) {
    out << "-";
    return printGrouped(out, -n);
  }

  if(n < 1000) {
    out << n;
  } else {
    printGrouped(out, n / 1000);
    out << " " << setw(3) << setfill('0') << (n % 1000);
  }
}

ostream & operator<< (ostream & out, const Currency & c) {
    printGrouped(out, c.val);
    out << " " << c.curr;
    return out;
}
4
  • Though changing how all numbers are printed is almost certainly not desired.
    – Roger Pate
    Commented Apr 15, 2010 at 20:03
  • locale("") is implementation-defined. on OS X 10.5 it won't work (throws a runtime_exception with what() "locale::facet::_S_create_c_locale name not valid"). You can use the current global locale with the default constructor instead. Commented Apr 15, 2010 at 20:51
  • 2
    @wilhelmtell i used locale("") on purpose because according to stroustrup's locale appendix in TC++PL, it uses the "user's preferred locale" and the C++ Standard says that valid arguments constitute "C" and "" (i suspect "" is supposed to use some environment variables?). Changing to default constructor for now. Someone has a clue what happens on OSX that it doesn't work? Commented Apr 15, 2010 at 21:16
  • Thank You for clear and nice tip with division / modulo 1000. That's what i was looking for. Commented Apr 16, 2010 at 11:12
7

One possibility might be to use locales for this.

#include <locale>
#include <string>
#include <cstddef>

class SpaceSeparator: public std::numpunct<char>
{
public:
    SpaceSeparator(std::size_t refs): std::numpunct<char>(refs) {}
protected:
    char do_thousands_sep() const { return ' '; }
    std::string do_grouping() const { return "\03"; }
};

//...    
ostream & operator<< (ostream & out, const Currency & c)
{
    SpaceSeparator facet(1); //1 - don't delete when done
    std::locale prev = out.imbue(std::locale(std::locale(), &facet));
    out << c.val<< " " << c.curr;
    out.imbue(prev);  //restore previous locale
    return out;
}
1
  • 1
    However, std::moneypunct would be a better facet to use.
    – Roger Pate
    Commented Apr 16, 2010 at 0:25
2
struct Currency {
  static char const sep = ' ';
  static int const group_size = 3;

  Currency(int val, std::string unit)
  : val(val), unit(unit)
  {}

  friend std::ostream& operator<<(std::ostream& out, Currency const& v) {
    // currently ignores stream width and fill
    std::ostringstream ss;
    bool const neg = v.val < 0;
    int const val = (neg ? -v.val : v.val);
    if (neg) out << '-';
    ss << val;
    std::string const s = ss.str();
    std::string::size_type n = s.size() % v.group_size;
    if (n) out << s.substr(0, n);
    for (; n < s.size(); n += v.group_size) {
      out << sep << s.substr(n, v.group_size);
    }
    out << ' ' << v.unit;
    return out;
  }

private:
  int val;
  std::string unit;
};

Could make sep and group_size non-static members, if you want to customize each object with comma, etc. (If so, make them private and initialize in the ctor, probably with default parameter values.) You could also use a traits class controlling output formatting.

Locales also support currency formatting through the moneypunct facet.

-1
#include <iostream>

#include <sstream>

#include <cstdlib>

#define GROUP_SEP ','

#define GROUP_SIZE 3

using namespace std;

string  output_formatted_string(long long num);


int main() { 

    string temp;

    cout << "Enter a large number:  ";

    getline(cin, temp);

    long long num = atoll(temp.c_str());

    string output = output_formatted_string(num);


    cout << output << endl;

    return 0;

    }

    string output_formatted_string(long long num)

    { 

    stringstream temp, out;

    temp << num;

    string s = temp.str();


    int n = s.size() % GROUP_SIZE;

    int i = 0;

    if(n>0 && s.size() > GROUP_SIZE)

      {

        out << s.substr(i, n) << GROUP_SEP;

        i += n;

      }


    n = s.size() / GROUP_SIZE - 1;

    while(n-- > 0)
         {

        out << s.substr(i, GROUP_SIZE) << GROUP_SEP;

        i += GROUP_SIZE;         
}

    out << s.substr(i);

    return out.str();

    }

Not the answer you're looking for? Browse other questions tagged or ask your own question.