Formatting C++ console output

32,602

Solution 1

You can write a procedure that always print the same number of characters to standard output.

Something like:

string StringPadding(string original, size_t charCount)
{
    original.resize(charCount, ' ');
    return original;
}

And then use like this in your program:

void list::displayByName(ostream& out) const
{
    node *current_node  = headByName;

    out << StringPadding("Name", 30)
        << StringPadding("Location", 10)
        << StringPadding("Rating", 10)
        << StringPadding("Acre", 10) << endl;
    out << StringPadding("----", 30)
        << StringPadding("--------", 10)
        << StringPadding("------", 10)
        << StringPadding("----", 10) << endl;

    while ( current_node)
    {
        out << StringPadding(current_node->item.getName(), 30)
            << StringPadding(current_node->item.getLocation(), 10)
            << StringPadding(current_node->item.getRating(), 10)
            << StringPadding(current_node->item.getAcres(), 10)
            << endl;
        current_node = current_node->nextByName;
    }
}

Solution 2

Think of it like using Microsoft Excel :) You think of your stream as fields. So you set the width of the field first then you insert your text in that field. For example:

#include <iostream>
#include <iomanip>
#include <string>

int main()
{
    using namespace std;

    string firstName = "firstName",
            secondName = "SecondName",
            n = "Just stupid Text";
    size_t fieldWidth = n.size(); // length of longest text

    cout << setw(fieldWidth) << left << firstName << endl // left padding
         << setw(fieldWidth) << left << secondName << endl
         << setw(fieldWidth) << left << n << endl;

    cout << setw(fieldWidth) << right << firstName << endl // right padding
         << setw(fieldWidth) << right << secondName << endl
         << setw(fieldWidth) << right << n << endl;
}

......

alt text

......

The field width means nothing but the width of the text + spaces. You could fill anything other than spaces:

string name = "My first name";
cout << setfill('_') << setw(name.size() + 10) << left << name;

.....

output::
My first name__________

......

I think the best way is to figure out your format then, write a new formatter that does all what you want:

#include <iostream>
#include <iomanip>
#include <string>

std::ostream& field(std::ostream& o)
{
    // usually the console is 80-character wide.
    // divide the line into four fields.
    return o << std::setw(20) << std::right;
}

int main()
{
    using namespace std;

    string firstName = "firstName",
            secondName = "SecondName",
            n = "Just stupid Text";
    size_t fieldWidth = n.size();

    cout << field << firstName << endl
         << field << secondName << endl
         << field << n << endl;
}

If you started thinking about parametrized manipulators, only that accept one int or long parameter are easy to implement, other types are really obscure if you are not familiar with streams in C++.

Solution 3

Boost has a format library that allows you to easily format the ourput like the old C printf() but with type safety of C++.

Remember that the old C printf() allowed you to specify a field width. This space fills the field if the output is undersized (note it does not cope with over-sized fields).

#include <iostream>
#include <iomanip>
#include <boost/format.hpp>

struct X
{  // this structure reverse engineered from
   // example provided by 'Mikael Jansson' in order to make this a running example

    char*       name;
    double      mean;
    int         sample_count;
};
int main()
{
    X   stats[] = {{"Plop",5.6,2}};

    // nonsense output, just to exemplify

    // stdio version
    fprintf(stderr, "at %p/%s: mean value %.3f of %4d samples\n",
            stats, stats->name, stats->mean, stats->sample_count);

    // iostream
    std::cerr << "at " << (void*)stats << "/" << stats->name
              << ": mean value " << std::fixed << std::setprecision(3) << stats->mean
              << " of " << std::setw(4) << std::setfill(' ') << stats->sample_count
              << " samples\n";

    // iostream with boost::format
    std::cerr << boost::format("at %p/%s: mean value %.3f of %4d samples\n")
                % stats % stats->name % stats->mean % stats->sample_count;
}

Solution 4

Give up on the tabs. You should be able to use io manipulators to set the field width, the fill character, and the format flag (to get left or right justification). Use the same values for the headings as you do for the data, and everything should come out nicely.

Also beware that you've switched Rating and Acres in your example.

Share:
32,602
user40120
Author by

user40120

Updated on July 23, 2022

Comments

  • user40120
    user40120 almost 2 years

    I've been trying to format the output to the console for the longest time and nothing is really happening. I've been trying to use as much of iomanip as I can and the ofstream& out functions.

    void list::displayByName(ostream& out) const
    {
        node *current_node  = headByName;
    
        // I have these outside the loop so I don't write it every time.
    
        out << "Name\t\t" << "\tLocation" << "\tRating " << "Acre" << endl;
        out << "----\t\t" << "\t--------" << "\t------ " << "----" << endl;
    
        while (current_node)
        {
            out << current_node->item.getName() // Equivalent tabs don't work?
                << current_node->item.getLocation()
                << current_node->item.getAcres()
                << current_node->item.getRating()
                << endl;
    
            current_node = current_node->nextByName;
        }
    
        // The equivalent tabs do not work because I am writing names,
        // each of different length to the console. That explains why they
        // are not all evenly spaced apart.
    }
    

    Is their anything that I can use to get it all properly aligned with each other? The functions that I'm calling are self-explanatory and all of different lengths, so that don't align very well with each other.

    I've tried just about everything in iomanip.

  • outis
    outis over 14 years
    +1 for standard io manipulators (setw & al), +1 for defining a custom iomanip, -1 for Excel
  • Vargas
    Vargas over 14 years
    And you can also limit the size of the string to be printed just in case you have large strings (such as complete names)