41

I'm not sure, but I think I remember there being something in Java that can specify how far from the left of a window that a string or digit begins..

How to easily format a table? I have this (using setw):

Bob Doe     10.96      7.61     14.39      2.11     47.30     14.21     44.58      5.00     60.23
Helen City     10.44      7.78     16.27      1.99     48.92     13.93     53.79      5.00     70.97
Joe Green     10.90      7.33     14.49      2.05     47.91     14.15     44.45      4.70     73.98

and ideally would like:

Bob           Doe        BLR  10.96   7.61  14.39   2.11  47.30  14.21  44.58   5.00  60.23  4:27.47
Helen         City       CUB  10.90   7.33  14.49   2.05  47.91  14.15  44.45   4.70  73.98  4:29.17
Joe           Green      USA  10.44   7.78  16.27   1.99  48.92  13.93  53.79   5.00  70.97  5:06.59

Is the only way calculations? Or is there some magical even more simple way?

pearbear
  • 1,025
  • 4
  • 18
  • 23
  • 1
    You should be able to do this with `setw`. You say you're using `setw` but you don't show how. – bames53 Feb 08 '13 at 16:21

8 Answers8

67

In C++, you have three functions to help you do what you want. There are defined in <iomanip>.
- setw() helps you defined the width of the output.
- setfill() Fill the rest with the character you want (in your case ' ').
- left (or right) allow you to define the alignment.

Here is the code to write your first line :

#include <iostream>
#include <iomanip>

using namespace std;

int main() {
    const char separator    = ' ';
    const int nameWidth     = 6;
    const int numWidth      = 8;

    cout << left << setw(nameWidth) << setfill(separator) << "Bob";
    cout << left << setw(nameWidth) << setfill(separator) << "Doe";
    cout << left << setw(numWidth) << setfill(separator) << 10.96;
    cout << left << setw(numWidth) << setfill(separator) << 7.61;
    cout << left << setw(numWidth) << setfill(separator) << 14.39;
    cout << left << setw(numWidth) << setfill(separator) << 2.11;
    cout << left << setw(numWidth) << setfill(separator) << 47.30;
    cout << left << setw(numWidth) << setfill(separator) << 14.21;
    cout << left << setw(numWidth) << setfill(separator) << 44.58;
    cout << left << setw(numWidth) << setfill(separator) << 5.00;
    cout << left << setw(numWidth) << setfill(separator) << 60.23;
    cout << endl;

    cin.get();
}


EDIT : To reduce the code, you can use a template function :

template<typename T> void printElement(T t, const int& width)
{
    cout << left << setw(width) << setfill(separator) << t;
}

That you can use like this :

printElement("Bob", nameWidth);
printElement("Doe", nameWidth);
printElement(10.96, numWidth);
printElement(17.61, numWidth);
printElement(14.39, numWidth);
printElement(2.11, numWidth);
printElement(47.30, numWidth);
printElement(14.21, numWidth);
printElement(44.58, numWidth);
printElement(5.00, numWidth);
printElement(60.23, numWidth);
cout << endl;
Cyril Leroux
  • 2,599
  • 1
  • 26
  • 25
  • 5
    Never use `system("PAUSE")` as it is said that it might cause some undefined effects. Instead, put a call to `cin.get()` and that will work more cross-platform. – Sri Harsha Chilakapati Aug 31 '15 at 14:28
24

Here are the various functions I use to display data in an organized, tabular form, along with an example demonstrating a possible use scenario.

Because the functions use stringstreams, they aren't as fast as other solutions, but for me that never matters --- the computing bottlekneck is elsewhere.

One advantage of using stringstreams is that the functions alter the precision of their own (internal scope) stringstreams, instead of changing the static cout precision. So you never have to worry about unintentionally modifying precision in a way that persists to affect other parts of your code.


DISPLAYING ARBITRARY PRECISION

This prd function (short for "print double") simply prints a double value with a specified precision.

/* Convert double to string with specified number of places after the decimal. */
std::string prd(const double x, const int decDigits) {
    stringstream ss;
    ss << fixed;
    ss.precision(decDigits); // set # places after decimal
    ss << x;
    return ss.str();
}

The following is just a variant that allows you to specify a blank-space padding to the left of the number. This can be helpful in displaying tables.

/* Convert double to string with specified number of places after the decimal
   and left padding. */
std::string prd(const double x, const int decDigits, const int width) {
    stringstream ss;
    ss << fixed << right;
    ss.fill(' ');        // fill space around displayed #
    ss.width(width);     // set  width around displayed #
    ss.precision(decDigits); // set # places after decimal
    ss << x;
    return ss.str();
}

CENTER-ALIGN FUNCTION

This function simply center-aligns text, padding left and right with blank spaces until the returned string is as large as the specified width.

/*! Center-aligns string within a field of width w. Pads with blank spaces
    to enforce alignment. */
std::string center(const string s, const int w) {
    stringstream ss, spaces;
    int padding = w - s.size();                 // count excess room to pad
    for(int i=0; i<padding/2; ++i)
        spaces << " ";
    ss << spaces.str() << s << spaces.str();    // format with padding
    if(padding>0 && padding%2!=0)               // if odd #, add 1 space
        ss << " ";
    return ss.str();
}

EXAMPLE OF TABULAR OUTPUT

So, we could use the prd and center functions above to output a table in the following fashion.

The code:

std::cout << center("x",10)       << " | "
          << center("x^2",10)     << " | "
          << center("(x^2)/8",10) << "\n";

std::cout << std::string(10*3 + 2*3, '-') << "\n";

for(double x=1.5; x<200; x +=x*2) {
    std::cout << prd(x,1,10)       << " | "
              << prd(x*x,2,10)     << " | "
              << prd(x*x/8.0,4,10) << "\n";
}

will print the table:

    x      |    x^2     |  (x^2)/8  
------------------------------------
       1.5 |       2.25 |     0.2812
       4.5 |      20.25 |     2.5312
      13.5 |     182.25 |    22.7812
      40.5 |    1640.25 |   205.0312
     121.5 |   14762.25 |  1845.2812

RIGHT- and LEFT-ALIGN FUNCTIONS

And, of course, you can easily construct variants of the center function that right- or left-align and add padding spaces to fill the desired width. Here are such functions:

/* Right-aligns string within a field of width w. Pads with blank spaces
   to enforce alignment. */
string right(const string s, const int w) {
    stringstream ss, spaces;
    int padding = w - s.size();                 // count excess room to pad
    for(int i=0; i<padding; ++i)
        spaces << " ";
    ss << spaces.str() << s;                    // format with padding
    return ss.str();
}

/*! Left-aligns string within a field of width w. Pads with blank spaces
    to enforce alignment. */
string left(const string s, const int w) {
    stringstream ss, spaces;
    int padding = w - s.size();                 // count excess room to pad
    for(int i=0; i<padding; ++i)
        spaces << " ";
    ss << s << spaces.str();                    // format with padding
    return ss.str();
}

I'm sure there are plenty of more-elegant ways to do this kind of thing --- certainly there are more concise ways. But this is what I do. Works well for me.

synaptik
  • 8,971
  • 16
  • 71
  • 98
  • 1
    For padding error on utf8 in center() for some French accent, you could dirty fix with small code given here: http://stackoverflow.com/a/4063229/3066309 // utf8 stuff const char *c_str = s.c_str(); int len = 0; while (*c_str) // count only real utf8 char len += (*c_str++ & 0xc0) != 0x80; padding += s.size() - len; – Sylvain Feb 23 '17 at 06:18
3

Just use sprintf with format specifiers to format fields. You can also use MFC CString

#include <iostream>
#include "stdio.h"

using namespace std;

int main()
{
    char buf[256];
    char pattern[]  = "%10s %10s     %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f";
    sprintf(buf, pattern, "Bob",  "Doe",     10.96,      7.61,     14.39,      2.11,     47.30,     14.21,     44.58,      5.00,     60.23);
    cout << buf << endl;
    sprintf(buf, pattern, "Helen", "City",     10.44,      7.78,     16.27,      1.99,     48.92,     13.93,     53.79,      5.00,     70.97);
    cout << buf << endl;
    sprintf(buf, pattern, "Joe", "Green",     10.90,      7.33,     14.49,      2.05,     47.91,     14.15,     44.45,      4.70,     73.98);
    cout << buf << endl;
}
blackbada_cpp
  • 422
  • 4
  • 11
2

You could do something like this to simplify the process a bit.

#include <iomanip>
#include <iostream>

struct TableFormat {
    int width;
    char fill;
    TableFormat(): width(14), fill(' ') {}
    template<typename T>
    TableFormat& operator<<(const T& data) {
        std::cout << data << std::setw(width) << std::setfill(fill);
        return *this;
    }
    TableFormat& operator<<(std::ostream&(*out)(std::ostream&)) {
        std::cout << out;
        return *this;
    }
};

int main() {
    TableFormat out;
    out << "Bob" << "Doe";
    out.width = 8;
    out << "BLR" << 10.96 << 7.61 << 14.39 << 2.11 << 47.30;
}

Which would print out (horribly in my case, but it's "customisable" to a degree):

Bob           Doe           BLR   10.96    7.61   14.39    2.11    47.3

The code is pretty self-explanatory, it's just a wrapper around std::cout to allow you to make the tedious calls easier, the second overload for operator<< is to allow you send std::endl..

Rapptz
  • 20,807
  • 5
  • 72
  • 86
1

C++20 includes <format> but it's not supported by libc++ for now.
I suggest to use {fmt} library since it could be obtained easily in Ubuntu20.

According to the doc, you may specify the width as an argument as well.

Format example: {2:<{0}}

`2`   -> Use second arg as value.\
`:`   -> Use non-default format.\
`<`   -> Align to left\
`{0}` -> Use argument 0 as width.

Live Demo

#include <string>
#include <iostream>
#include <fmt/core.h>
#include <tuple>
#include <vector>
int main()
{
    using Row = std::tuple<std::string, std::string, double>;

    std::vector<Row> table = {
        std::make_tuple("Bob", "Doe", 10.96),
        std::make_tuple("Helen", "City", 10.44),
        std::make_tuple("Joe", "Green", 10.90)
    };
    
    size_t nameWidth{12};
    size_t valWidth{7};

    for(const auto& row: table){
        std::cout << fmt::format("{2:<{0}} {3:<{0}} {4:<{1}} \n", 
        nameWidth, valWidth, std::get<0>(row), std::get<1>(row), std::get<2>(row) );
    }
}

Output

Bob          Doe          10.96   
Helen        City         10.44   
Joe          Green        10.9   
Louis Go
  • 2,213
  • 2
  • 16
  • 29
0

Assuming you want to format your output to resemble a table, what you need is I/O manipulators.
You can use setw() manipulator to set the output width and setfill() to set the filling character.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
0

Considering an example:

string firstname = "Bob";
string lastname = "Doe";
string country = "BLR";
float f1 = 10.96f, f2=7.61f, f3=14.39f, f4=2.11f, f5=47.30f, f6=14.21f, f7=44.58f, f8=5.00f, f9=60.23f;
string time = "4:27.47";
cout << setw(12) << firstname << set(12) << lastname;
cout << setw(5) << country << setprecision(2) << f1 << setprecision(2) << f2 << setprecision(2) << f3..
  1. use setw() to set the width while printing a string
  2. use setprecision to set the precision for floating values
  3. read MSDN
Aniket Inge
  • 25,375
  • 5
  • 50
  • 78
0

I'm not sure what you wrote so I can't see what's wrong, but you can get the results you want with std::setw:

#include <iostream>
#include <iomanip>

int main() {
   std::cout << std::left << std::setw(20) << "BoB" << std::setw(20) << 123.456789 << '\n';
   std::cout << std::left << std::setw(20) << "Richard" << std::setw(20) << 1.0 << '\n';
}

http://ideone.com/Iz5RXr

bames53
  • 86,085
  • 15
  • 179
  • 244