Making a table with printf in c++

You can use printf with left-justify flag (-).

printf("%-10s", "title"); // this will left-align "title" in space of 10 characters

Here is a sample program:

#include <string>
using namespace std;

int main()
{
    string name = "Bob Cratchit";
    string title = "Clerk";
    float gross = 15;
    float tax = 2;
    float net = 13;

    printf("%-25s%-20s%-10s%-10s%-10s\n", "Name", "Title", "Gross", "Tax", "Net"); 
    printf("%-25s%-20s%-10.2f%-10.2f%-10.2f\n", name.c_str(), title.c_str(), gross, tax, net); 
    return 0;
}

Output:

Name                     Title               Gross     Tax       Net
Bob Cratchit             Clerk               15.00     2.00      13.00

The most obvious question is: why use printf, when other tools are more adapt? Another question, often forgotten, is what is the (final) output medium? If the text is going to end up on a printer or in a text box in a windowing system, you may have your work cut out for you. The fonts on such systems are rarely fixed width, so you'll have to take into account the width of the individual characters. For output to a printer, I would suggest outputting LaTeX and then postprocessing it. For outputting to a window, the library you're using probably has some sort of table component which will do the formatting for you.

If you're outputing to some fixed width font device—a teletyp, for example, then you can either use iostream manipulators or user defined types. (There's no way to do this cleanly with printf—you need iostreams.) Abstractly speaking, defining types like Name, Title and MonitaryAmount is the cleanest solution. In which case, you just define an appropriate << operator for the type. Using a user defined type for name and title, instead of just std::string, may be overkill, however, and the manipulator approach may be preferred. (In a very large application, where the separate types would be justified, you're likely to need output in different contexts, and want manipulators to specify them as well.)

In the simplest solution, you could get by with just two manipulators: TextField and MoneyField: each manipulator would take the field width as an argument to the constructor, and set the appropriate format fields in its << operator, e.g.:

class TextField
{
    int myWidth;
public:
    TextField( int width ) : myWidth( width ) {}
    friend std::ostream&
    operator<<( std::ostream& dest, TextField const& manip )
    {
        dest.setf( std::ios_base::left, std::ios_base::adjustfield );
        dest.fill( ' ' );
        dest.width( manip.myWidth );
        return dest;
    }
};

and

class MoneyField
{
    int myWidth;
public:
    MoneyField( int width ) : myWidth( width ) {}
    friend std::ostream&
    operator<<( std::ostream& dest, MoneyField const& manip )
    {
        dest.setf( std::ios_base::right, std::ios_base::adjustfield );
        dest.setf( std::ios_base::fixed, std::ios_base::floatfield );
        dest.fill( ' ' );
        dest.precision( 2 );
        dest.width( manip.myWidth );
        return dest;
    }
};

(Practically speaking, it's probably better to use a class for Money. You'll want special rounding rules for multiplication, for example; if you're calculating tax, in fact, you'll probably need to use some sort of decimal type, rather than double, in order to meet legal requirements as to how it is calculated.)

Anyway, given the above manipulators, you can write something like:

TextField  name( 15 );
TextField  title( 8 );
MoneyField gross( 8 );
MoneyField tax( 6 );
MoneyField net( 8 );
for ( std::vector< Employee >::const_iterator employee = employees.begin();
        employee != employees.end();
        ++ employee ) {
    std::cout << name  << employee->name()
              << title << employee->title()
              << gross << employee->salary()
              << tax   << calculateTax( employee->salary() )
              << net   << calculateNet( employee->salary() )
              << std::endl;
}

(This assumes that you've cleaned up the rest to make it idiomatic and maintainable C++ as well.)


Instead of using tabs to position at specific columns, use standard stream I/O manipulators. To be more specific, check out std::setw and std::left.

Something like this:

std::cout << std::left << std::setw(25) << "Name" << std::setw(12) << "Title"
          << std::setw(11) << "Gross" << std::setw(9) << "Tax" << "Net\n";