Why won't my output align properly in columns? - c++

I have no clue why this wont align properly. I have looked at every other post and topic on aligning basic columns in cout, and I have tried them and it just wont do it.
I need 3 columns to look like so:
1 0 /sbin/init
949 1 /usr/sbin/sshd -D -d
27282 949 sshd: salvucwa [ priv]
27323 27282 sshd: salvucwa#pts/31
27324 27323 -bash
27425 27324 script
27426 27425 bash -i
To do this, I am using this code:
void printData(vector<string> tree) {
for (int i = tree.size()-1; i >= 0; i--) {
string print = tree.at(i);
string pid = print.substr(0, print.find(" "));
print = print.substr(print.find(" "));
string ppid = print.substr(0, print.find(" "));
print = print.substr(print.find(" "));
string cmd = print;
cout << left << setw(5) << pid;
cout << ppid;
cout << setw(50) << cmd << endl;
}
}
The 50 was just a placeholder as I was trying much too large numbers to get it to work, but it hasn't. It aligns the first and second columns just fine, but it ends up printing like this:
1 0 /sbin/init
949 1 /usr/sbin/sshd -D -d
27282 949 sshd: salvucwa [ priv]
27323 27282 sshd: salvucwa#pts/31
27324 27323 -bash
27425 27324 script
27426 27425 bash -i
This first 2 columns are aligned perfectly, but the last one always follows the 2nd column and will not change.
Changing the length of the second setw(5) in the code block also only increases or decreases the space between the first 2 columns, not the last one. Also, changing the last setw() does not seem to have an affect.
Why is this not aligning properly? Sorry, as this is probably a dumb question, but every other post on the same topic I have found does not appear to have a fix and only talks about 2 columns (which my first 2 columns are fine).

Without the full code it's impossible to tell precisely what you're doing wrong, but I'm sure that the code you've provided is not the code that you used to print this.
You are most likely using an insufficient column size to print the numeric data. If your data is 5 characters long, printing as you have:
cout << left << setw(5) << "01234";
cout << left << setw(5) << "56789" << endl;
will result in something which looks like this:
0123456789
I would encourage that you use a larger number, such as 10 in your setw manipulation. This will align your data and prevent you from needing to add spaces, so long as it's larger than your largest value.
I wrote a horrifying test to prove this out:
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
using namespace std;
int main( int argc, char** argv )
{
vector<int> numbers = { 1, 0, 949, 1, 27282, 949, 27323, 27282, 27324, 27323, 27425, 27324, 27426, 27525 };
vector<string> words =
{
"/sbin/init",
"/usr/sbin/sshd -D -d",
"sshd: salvucwa [ priv]",
"-bash",
"script",
"bash - i"
};
cout << left << setw(10) << numbers[0]
<< setw(10) << numbers[1]
<< setw(50)<< words[0] << endl;
cout << left << setw(10) << numbers[2]
<< setw(10) << numbers[3]
<< setw(50)<< words[1] << endl;
cout << left << setw(10) << numbers[4]
<< setw(10) << numbers[5]
<< setw(50)<< words[2] << endl;
cout << left << setw(10) << numbers[6]
<< setw(10) << numbers[7]
<< setw(50)<< words[3] << endl;
cout << left << setw(10) << numbers[8]
<< setw(10) << numbers[9]
<< setw(50)<< words[4] << endl;
cout << left << setw(10) << numbers[10]
<< setw(10) << numbers[11]
<< setw(50)<< words[5] << endl;
}
This outputs:
1 0 /sbin/init
949 1 /usr/sbin/sshd -D -d
27282 949 sshd: salvucwa [ priv]
27323 27282 -bash
27324 27323 script
27425 27324 bash - i

There are a couple of problems in the posted approach.
It doesn't take into account the real length of the strings, using std::setw(5) and only before pid.
It doesn't consider that the source string may contain more then one space between the fields. For example, "27324 27323 -bash" would be parsed as pid = "27324", ppid = "", cmd = "27323 -bash"
A lot of copies are also unnecessary performed.
A possible implementation that takes care of the previous points may be the following
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <vector>
#include <string>
#include <cctype>
void printData(std::vector<std::string> const& tree)
{
auto is_space = [](char ch) -> bool {
return std::isspace(static_cast<unsigned char>(ch));
};
for (auto row{std::rbegin(tree)}, last_row{std::rend(tree)};
row != last_row; ++row)
{
auto first = std::begin(*row);
auto last = std::end(*row);
auto pid_first = std::find_if_not(first, last, is_space);
auto pid_last = std::find_if(pid_first, last, is_space);
std::string pid(pid_first, pid_last); // You may use a std::string_view
auto ppid_first = std::find_if_not(pid_last, last, is_space);
auto ppid_last = std::find_if(ppid_first, last, is_space);
std::string ppid(ppid_first, ppid_last);
auto cmd_first = std::find_if_not(ppid_last, last, is_space);
std::string cmd(cmd_first, last);
std::cout << std::left << std::setw(8) << pid
<< std::setw(8) << ppid
<< std::setw(50) << cmd << '\n';
}
}

Related

How to ignore certain input lines in C++?

Okay so a little background this code is supposed to read through a file containing DNA and calculate the number of nucleotides A, C, T, G and print them out and also do some other slight calculations. My code runs fine for most files except for files that contain lines that start with # and + in the file. I need to skip those lines in order to get an accurate number. So my question is how to skip or ignore these lines in my calculations.
My code is
#include <iostream>
#include <stream>
#include <string>
#include <vector>
#include <map>
int main(int argc, char** argv) {
// Ignore how the above argc and argv are used here
auto arguments = std::vector<std::string>(argv, argv + argc);
// "arguments" box has what you wrote on the right side after &&
if (arguments.size() != 2) {
// ensure you wrote a file name after "./a.out"
std::cout << "Please give a file name as argument\n";
return 1;
}
auto file = std::fstream(arguments[1]);
if (!file) {
// ensure the file name you gave is from the available files
std::cout << "Cannot open " << arguments[1] << "\n";
return 1;
}
auto counts = std::map<char,int>({{'G',0.0},{'A',0.0},{'C',0.0},{'T',0.0}});
// Just a test loop to print all lines from the file
for (auto dna = std::string(); std::getline(file, dna); ) {
//std::cout << dna << "\n";
for (auto nucleotide:dna) {
counts[nucleotide]=counts[nucleotide] + 1;
}
}
double total = counts['A'] + counts['T'] + counts['G'] + counts['C'];
double GC = (counts['G'] + counts['C'])*100/total;
double AT = (counts['A'] + counts['T'])*100/total;
double ratio = AT/GC;
auto classification = "";
if ( 40.0 < GC < 60.0) {
classification = "moderate GC content";
}
if (60 <= GC) {
classification = "high GC content";
}
if (GC <= 40.0) {
classification = "low GC content";
}
std::cout << "GC-content: " << GC << "\n";
std::cout << "AT-content: " << AT << "\n";
std::cout << "G count: " << counts['G'] << "\n";
std::cout << "C count: " << counts['C'] << "\n";
std::cout << "A count: " << counts['A'] << "\n";
std::cout << "T count: " << counts['T'] << "\n";
std::cout << "Total count: " << total << "\n";
std::cout << "AT/GC Ratio: " << ratio << "\n";
std::cout << "GC Classification: " << classification << "\n";
}
The file that is giving me trouble is this which is like this
#ERR034677.1 HWI-EAS349_0046:7:1:2144:972#0 length=76
NGATGATAAACAAGAGGGTAAAAAGAAAAAAGCTACAGACATTTCTGCTAATCTATTATTTTGTTCCTTTTTTTTT
+ERR034677.1 HWI-EAS349_0046:7:1:2144:972#0 length=76
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
If anyone can help me with this. I will be very grateful. I only need a hint or an idea of the concept I am missing so I can make my code compatible with all files. Thanks in advance
Your actual problem seems to be the standard case of "input is not always clean syntax".
The solution is always "do not expect clean syntax".
First read whole lines into a buffer.
Then check for syntax.
Skip broken syntax.
Scan clean syntax from buffer.

Trouble formatting fstream output with unknown strength and int length

I am finishing up this c++ project I'm working on to take user input, calculate some values, and then write all of the data to a file. My problem is that I can't get the values to align correctly in the text file. I am using setw(), but this doesn't correctly align everything when the length of the user input is unknown. It just messes with the columns and makes them unaligned.
I've tried using the fixed operator, left align, right align, without much luck
Here's my code pertaining to writing to the file.
if (myfile.is_open()){
myfile << "BASKETBALL COURTS AREA REPORT\n\n";
myfile << "Court" << setw(25) << "Height" << setw(25) << "Width\n";
for(int i=0; i<n; i++){
myfile << names[i] << setw(25) << " " << arr1[i] << setw(25) << arr2[i] <<"\n\n";
}
}
myfile << "\nThe largest court is " << maxName << ": " << maximum << "\n" << "\n";
myfile << "Total area covered by all courts: " << totalArea;
I expect the columns to be completely aligned like in this picture:
However the actual output looks more like this:
If anyone could assist me with what to do I would greatly appreciate it. Thank you so much for your time!
The fist (most obvious) problem is that you're not setting the field width for the name of the court. By default, it's set to 0, so each name is printed in the minimum width necessary to display the whole name. Setting the other column widths doesn't accomplish much after that.
To set the width, you probably want to go through the items, find the widest one, then add on a few extra spaces to give a margin between columns.
#include <iostream>
#include <sstream>
#include <iomanip>
#include <ios>
#include <string>
#include <algorithm>
#include <vector>
struct court {
std::string name;
int height;
int width;
};
int main() {
std::vector<court> courts {
{ "Auburn park", 12, 16},
{ "Alabama", 14, 17},
{"Wilsonville Stadium", 51, 123}
};
auto w = std::max_element(courts.begin(), courts.end(), [](court const &a, court const &b) { return a.name.length() < b.name.length(); })->name.length();
for (auto const &c : courts) {
std::cout << std::left << std::setw(w+5) << c.name
<< std::right << std::setw(5) << c.height
<< std::setw(5) << c.width << "\n";
}
}
Result:
Auburn park 12 16
Alabama 14 17
Wilsonville Stadium 51 123

How can I easily format my data table in C++?

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?
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;
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.
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;
}
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..
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
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.
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..
use setw() to set the width while printing a string
use setprecision to set the precision for floating values
read MSDN
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

C++ can setw and setfill pad the end of a string?

Is there a way to make setw and setfill pad the end of a string instead of the front?
I have a situation where I'm printing something like this.
CONSTANT TEXT variablesizeName1 .....:number1
CONSTANT TEXT varsizeName2 ..........:number2
I want to add a variable amount of '.' to the end of
"CONSTANT TEXT variablesizeName#" so I can make ":number#" line up on the screen.
Note: I have an array of "variablesizeName#" so I know the widest case.
Or
Should I do it manually by setting setw like this
for( int x= 0; x < ARRAYSIZE; x++)
{
string temp = string("CONSTANT TEXT ")+variabletext[x];
cout << temp;
cout << setw(MAXWIDTH - temp.length) << setfill('.') <<":";
cout << Number<<"\n";
}
I guess this would do the job but it feels kind of clunky.
Ideas?
You can use manipulators std::left, std::right, and std::internal to choose where the fill characters go.
For your specific case, something like this could do:
#include <iostream>
#include <iomanip>
#include <string>
const char* C_TEXT = "Constant text ";
const size_t MAXWIDTH = 10;
void print(const std::string& var_text, int num)
{
std::cout << C_TEXT
// align output to left, fill goes to right
<< std::left << std::setw(MAXWIDTH) << std::setfill('.')
<< var_text << ": " << num << '\n';
}
int main()
{
print("1234567890", 42);
print("12345", 101);
}
Output:
Constant text 1234567890: 42
Constant text 12345.....: 101
EDIT:
As mentioned in the link, std::internal works only with integer, floating point and monetary output. For example with negative integers, it'll insert fill characters between negative sign and left-most digit.
This:
int32_t i = -1;
std::cout << std::internal
<< std::setfill('0')
<< std::setw(11) // max 10 digits + negative sign
<< i << '\n';
i = -123;
std::cout << std::internal
<< std::setfill('0')
<< std::setw(11)
<< i;
will output
-0000000001
-0000000123
Something like:
cout << left << setw(MAXWIDTH) << setfill('.') << temp << ':' << Number << endl;
Produces something like:
derp..........................:234
herpderpborp..................:12345678
#include <iostream>
#include <iomanip>
int main()
{
std::cout
<< std::setiosflags(std::ios::left) // left align this section
<< std::setw(30) // within a max of 30 characters
<< std::setfill('.') // fill with .
<< "Hello World!"
<< "\n";
}
//Output:
Hello World!..................

Formatting C++ console output

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.
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;
}
......
......
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++.
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;
}
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.
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;
}
}