views:

213

answers:

9

I have an 8 digit integer which I would like to print formatted like this:

XXX-XX-XXX

I would like to use a function that takes an int and returns a string.

What's a good way to do this?

+1  A: 
int your_number = 12345678;

std::cout << (your_number/10000000) % 10 << (your_number/1000000) % 10 << (your_number/100000) %10 << "-" << (your_number/10000) %10 << (your_number/1000) %10 << "-" << (your_number/100) %10 << (your_number/10) %10 << (your_number) %10;

http://www.ideone.com/17eRv

Its not a function, but its a general method for parsing an int number by number.

Ami
+2  A: 

You can use the std::ostringstream class to convert the number to a string. Then you can use the string of digits and print them using whatever formatting you want, as in the following code:

std::ostringstream oss;
oss << std::setfill('0') << std::setw(8) << number;
std::string str = oss.str();
if ( str.length() != 8 ){
    // some form of handling
}else{
    // print digits formatted as desired
}
Michael Aaron Safyan
+1  A: 

How's this?

std::string format(int x)
{
    std::stringstream ss

    ss.fill('0');
    ss.width(3);
    ss << (x / 10000);

    ss.width(1);
    ss << "-";

    ss.width(2);
    ss << (x / 1000) % 100;

    ss.width(1);
    ss << "-";

    ss.width(3);
    ss << x % 1000;

    return ss.str();
}

Edit 1: I see strstream is deprecated and replaced with stringstream.

Edit 2: Fixed issue of missing leading 0's. I know, it's ugly.

andand
A: 

Obviously a char * and not a string, but you get the idea. You'll need to free the output once you're done, and you should probably add error checking, but this should do it:

char * formatter(int i)
{    
  char *buf = malloc(11*sizeof(char));
  sprintf(buf, "%03d-%02d-%03d", i/100000, (i/1000)%100, i%1000);
  return buf;
}
Dusty
why malloc, we're in C++ here
valya
Why manual allocation? If anything, make the buffer automatic and not dynamic (`sizeof(char)` is always 1, make a buffer of 16 just because it's a nice number), `sprintf` into that, then return a `std::string`.
GMan
+7  A: 

Tested this, it works.

The format parameter here is "XXX-XX-XXX", but it only looks at (and skips over) the dashes.

std::string foo(char *format, long num)
{
    std::string s(format);

    if (num > 99999999 || num < 0) return s; // really an error

    for (int nPos = 9; nPos >= 0; --nPos)
    {
        if (s.at(nPos) == '-') continue;

        s.at(nPos) = '0' + (num % 10);
        num = num / 10;
    }

    return s;
}

Usage:

int main()
{
    printf(foo("###-##-###", 12345678).c_str());

    return 0;
}
egrunin
+1 for a more general solution.
andand
+0 for violating YAGNI :-)
paxdiablo
But what does `cout << foo("###-##", 12345) << endl` do? If the function is only going to work with one particular length of format string, you should `assert` that. IMO you should `assert` range conditions rather than failing silently also.
spong
Not only YAGNI, but duplicating something that's already in the standard library. Almost enough to qualify for a -1...
Jerry Coffin
for the sake of abstraction, i would use `if (s.at(nPos) != '#') continue;` instead of `if (s.at(nPos) == '-') continue;`
goldPseudo
Hey guys, it's an **example** which solves the exact problem and suggests ways to take it further...I mean, you don't leave "really an error" in production code, do you? Sheesh, tough crowd. I'm a big believer in YAGNI myself.
egrunin
Yes, it's a tough crowd, but you also got a few up votes to go with the criticism. If it's any consolation, I'm kind of laughing at myself right now for spending FAR longer on this than I EVER would have if I had to solve this problem in anger.
spong
+3  A: 

Here's a complete program that shows how I'd do it:

#include <iostream>
#include <iomanip>
#include <sstream>

std::string formatInt (unsigned int i) {
    std::stringstream s;
    s << std::setfill('0') << std::setw(3) << ((i % 100000000) / 100000) << '-'
      << std::setfill('0') << std::setw(2) << ((i % 100000) / 1000) << '-'
      << std::setfill('0') << std::setw(3) << (i % 1000);
    return s.str();
}

int main (int argc, char *argv[]) {
    if (argc > 1)
        std::cout << formatInt (atoi (argv[1])) << std::endl;
    else
        std::cout << "Provide an argument, ya goose!" << std::endl;
    return 0;
}

Running this with certain inputs gives:

Input        Output
--------     ----------
12345678     123-45-678
0            000-00-000
7012         000-07-012
10101010     101-01-010
123456789    234-56-789
-7           949-67-289

Those last two show the importance of testing. If you want different behaviour, you'll need to modify the code. I generally opt for silent enforcement of rules if the caller can't be bothered (or is too stupid) to follow them but apparently some people like to use the principle of least astonishment and raise an exception :-)

paxdiablo
+1  A: 
#include <iostream>
#include <string>

using namespace std;

template<class Int, class Bi>
void format(Int n, Bi first, Bi last)
{
    if( first == last ) return;
    while( n != 0 ) {
        Int t(n % 10);
        n /= 10;
        while( *--last != 'X' && last != first);
        *last = t + '0';
    }
}

int main(int argc, char* argv[])
{
    int i = 23462345;
    string s("XXX-XX-XXX");
    format(i, s.begin(), s.end());
    cout << s << endl;
    return 0;
}
wilhelmtell
This algorithm will work with any integer so long as the format string has number of X characters equal to the length of the integer. For instance, in the case of an integer with 8 digit the format string must have 8 X characters.
wilhelmtell
+2  A: 

Here's a bit different way that tries to work with the standard library and get it to do most of the real work:

#include <locale>

template <class T>
struct formatter : std::numpunct<T> {
protected:
    T do_thousands_sep() const { return T('-'); }
    std::basic_string<T> do_grouping() const {
        return std::basic_string<T>("\3\2\3");
    }
};

#ifdef TEST

#include <iostream>

int main() {
    std::locale fmt(std::locale::classic(), new formatter<char>);   
    std::cout.imbue(fmt);

    std::cout << 12345678 << std::endl;
    return 0;
}

#endif

To return a string, just write to a stringstream, and return its .str().

This may be overkill if you only want to print out one number that way, but if you want to do this sort of thing in more than one place (or, especially, if you want to format all numbers going to a particular stream that way) it becomes more reasonable.

Jerry Coffin
I can't really see too many people solving this particular problem in this way for real, but +1 for the most awesome answer.
spong
+3  A: 

This is how I'd do it, personally. Might not be the fastest way of solving the problem, and definitely not as reusable as egrunin's function, but it strikes me as both clean and easy to understand. I'll throw it in the ring as an alternative to the mathier and loopier solutions.

#include <sstream>
#include <string>
#include <iomanip>

std::string format(long num) {
  std::ostringstream oss;
  oss << std::setfill('0') << std::setw(8) << num;
  return oss.str().insert(3, "-").insert(6, "-");
};
goldPseudo
definitely the easiest to understand (that is read) of all the answers.
Inverse