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";