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.

Krzysztof Szynter
  • 835
  • 4
  • 14
  • 24
  • 1
    @danben: How would tagging it as [homework] change anything about the question or how good answers should be judged? –  Apr 15 '10 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 http://meta.stackexchange.com/questions/10811/how-to-ask-and-answer-homework-questions. – danben Apr 15 '10 at 20:00
  • @danben: See http://meta.stackexchange.com/questions/10811/how-to-ask-and-answer-homework-questions/10825#10825 and http://meta.stackexchange.com/questions/10811/how-to-ask-and-answer-homework-questions/10839#10839 –  Apr 15 '10 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 Apr 15 '10 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." –  Apr 15 '10 at 22:22

4 Answers4

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;
}
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • Though changing how *all* numbers are printed is almost certainly not desired. –  Apr 15 '10 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. – wilhelmtell Apr 15 '10 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? – Johannes Schaub - litb Apr 15 '10 at 21:16
  • Thank You for clear and nice tip with division / modulo 1000. That's what i was looking for. – Krzysztof Szynter Apr 16 '10 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;
}
UncleBens
  • 40,819
  • 6
  • 57
  • 90
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();

    }
Bart
  • 19,692
  • 7
  • 68
  • 77
Afsal
  • 1