user69514
user69514

Reputation: 27629

C++ alignment when printing cout <<

Is there a way to align text when printing using std::cout? I'm using tabs, but when the words are too big they won't be aligned anymore.

Sales Report for September 15, 2010
Artist  Title   Price   Genre   Disc    Sale    Tax Cash
Merle   Blue    12.99   Country 4%  12.47   1.01    13.48
Richard Music   8.49    Classical   8%  7.81    0.66    8.47
Paula   Shut    8.49    Classical   8%  7.81    0.72    8.49

Upvotes: 78

Views: 166633

Answers (7)

C++20 std::format options <, ^ and > (GCC 13, CLANG 14)

main.cpp

#include <iostream>
#include <format>

int main() {
    std::cout << "123456.\n";
    // left
    std::cout << std::format("{:<6}.\n", 42);
    // right
    std::cout << std::format("{:>6}.\n", 42);
    // center
    std::cout << std::format("{:^6}.\n", 42);
}

Compile and run:

g++ -ggdb3 -O0 -std=c++20 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

Output:

123456.
42    .
    42.
  42  .

Tested on GCC 13.2.0, Ubuntu 24.04.

Documented at:

libfmt-dev: compatible library if your stdlib doesn't have std::format yet

libfmt-dev is the awesome library that became C++20 std::format, so if your C++ Standard Library doesn't have the feature yet you can also:

sudo apt install libfmt-dev

Modify source to replace:

  • <format> with <fmt/core.h>
  • std::format to fmt::format

main2.cpp

#include <iostream>

#include <fmt/core.h>

int main() {
    std::cout << "123456.\n";
    // left
    std::cout << fmt::format("{:<6}.\n", 42);
    // right
    std::cout << fmt::format("{:>6}.\n", 42);
    // center
    std::cout << fmt::format("{:^6}.\n", 42);
}

and compile and run with:

g++ -std=c++11 -o main2.out main2.cpp -lfmt
./main2.out

Output:

123456.
42    .
    42.
  42  .

Tested on Ubuntu 22.04.

More information at: std::string formatting like sprintf

Upvotes: 6

Marine Galantin
Marine Galantin

Reputation: 2279

The easiest way, without the use of "complex" functions, is to format by hand:

an example:

  std::cout << std::setprecision(10)
            << "integral of sin(x) from x = 0 to x = pi" << '\n'
            << "approximate: " << integrate(a, b, nbins) << '\n'
            << "exact:       " << 2.0                    << '\n';

Upvotes: 0

Nguyen
Nguyen

Reputation: 2123

Another way to make column aligned is as follows:

using namespace std;

cout.width(20); cout << left << "Artist";
cout.width(20); cout << left << "Title";
cout.width(10); cout << left << "Price";
...
cout.width(20); cout << left << artist;
cout.width(20); cout << left << title;
cout.width(10); cout << left << price;

We should estimate maximum length of values for each column. In this case, values of "Artist" column should not exceed 20 characters and so on.

Upvotes: 15

Herb Sutter
Herb Sutter

Reputation: 2639

The ISO C++ standard way to do it is to #include <iomanip> and use io manipulators like std::setw. However, that said, those io manipulators are a real pain to use even for text, and are just about unusable for formatting numbers (I assume you want your dollar amounts to line up on the decimal, have the correct number of significant digits, etc.). Even for just plain text labels, the code will look something like this for the first part of your first line:

// using standard iomanip facilities
cout << setw(20) << "Artist"
     << setw(20) << "Title"
     << setw(8) << "Price";
// ... not going to try to write the numeric formatting...

If you are able to use the Boost libraries, run (don't walk) and use the Boost.Format library instead. It is fully compatible with the standard iostreams, and it gives you all the goodness for easy formatting with printf/Posix formatting string, but without losing any of the power and convenience of iostreams themselves. For example, the first parts of your first two lines would look something like:

// using Boost.Format
cout << format("%-20s %-20s %-8s\n")  % "Artist" % "Title" % "Price";
cout << format("%-20s %-20s %8.2f\n") % "Merle" % "Blue" % 12.99;

Upvotes: 147

Loki Astari
Loki Astari

Reputation: 264331

See also: Which C I/O library should be used in C++ code?

struct Item
{
   std::string     artist;
   std::string     c;
   integer         price;  // in cents (as floating point is not acurate)
   std::string     Genre;
   integer         disc;
   integer         sale;
   integer         tax;
};

std::cout << "Sales Report for September 15, 2010\n"
          << "Artist  Title   Price   Genre   Disc    Sale    Tax Cash\n";
FOREACH(Item loop,data)
{
    fprintf(stdout,"%8s%8s%8.2f%7s%1s%8.2f%8.2f\n",
          , loop.artist
          , loop.title
          , loop.price / 100.0
          , loop.Genre
          , loop.disc , "%"
          , loop.sale / 100.0
          , loop.tax / 100.0);

   // or

    std::cout << std::setw(8) << loop.artist
              << std::setw(8) << loop.title
              << std::setw(8) << fixed << setprecision(2) << loop.price / 100.0
              << std::setw(8) << loop.Genre
              << std::setw(7) << loop.disc << std::setw(1) << "%"
              << std::setw(8) << fixed << setprecision(2) << loop.sale / 100.0
              << std::setw(8) << fixed << setprecision(2) << loop.tax / 100.0
              << "\n";

    // or

    std::cout << boost::format("%8s%8s%8.2f%7s%1s%8.2f%8.2f\n")
              % loop.artist
              % loop.title
              % loop.price / 100.0
              % loop.Genre
              % loop.disc % "%"
              % loop.sale / 100.0
              % loop.tax / 100.0;
}

Upvotes: 11

Alex Martelli
Alex Martelli

Reputation: 881487

At the time you emit the very first line,

Artist  Title   Price   Genre   Disc    Sale    Tax Cash

to achieve "alignment", you have to know "in advance" how wide each column will need to be (otherwise, alignment is impossible). Once you do know the needed width for each column (there are several possible ways to achieve that depending on where your data's coming from), then the setw function mentioned in the other answer will help, or (more brutally;-) you could emit carefully computed number of extra spaces (clunky, to be sure), etc. I don't recommend tabs anyway as you have no real control on how the final output device will render those, in general.

Back to the core issue, if you have each column's value in a vector<T> of some sort, for example, you can do a first formatting pass to determine the maximum width of the column, for example (be sure to take into account the width of the header for the column, too, of course).

If your rows are coming "one by one", and alignment is crucial, you'll have to cache or buffer the rows as they come in (in memory if they fit, otherwise on a disk file that you'll later "rewind" and re-read from the start), taking care to keep updated the vector of "maximum widths of each column" as the rows do come. You can't output anything (not even the headers!), if keeping alignment is crucial, until you've seen the very last row (unless you somehow magically have previous knowledge of the columns' widths, of course;-).

Upvotes: 4

Eli Bendersky
Eli Bendersky

Reputation: 273366

IO manipulators are what you need. setw, in particular. Here's an example from the reference page:

// setw example
#include <iostream>
#include <iomanip>
using namespace std;

int main () {
  cout << setw (10);
  cout << 77 << endl;
  return 0;
}

Justifying the field to the left and right is done with the left and right manipulators.

Also take a look at setfill. Here's a more complete tutorial on formatting C++ output with io manipulators.

Upvotes: 13

Related Questions