Spaces / Columns

Hey so I have a question regarding something about spaces.

ok so lets say the program uses input from the user, it then auto creates like a card. Now everything is fine , code works and all but the spacing is weird. so lets say I have 2 words. Apple - Coconut
Apple is 5 letters
Coconut is 7 Letters

Now it makes the spacing weird. Giving 2 extra spaces on Coconut than the apple (column form) and makes the table look weird

so my table looks something like this

Fruits    Rate
Apple      5
Coconut       7


I tried
 
setw(insert space amount)

aswell as tabs , nothing seems to fix it

The above isn't the real output , its just an example of how it looks
Last edited on
Never mind , I found a way around my problem , instead of having trouble correcting the spaces for the longer words , I made the longer words at the end so that the spacing is correct
For the record:

You can determine a field with:

http://www.cplusplus.com/reference/ios/ios_base/width/

This would align those fields.
The trick is to call setw before outputting the value. It sets the minimum width of the next thing that is outputted. You could think of it as the column width.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>

struct Fruit
{
	std::string name;
	int rate;
};

int main()
{
	std::vector<Fruit> fruits = 
	{
		{"Apple", 5},
		{"Coconut", 7},
		{"Banana", 15},
	};
	
	const int fruit_name_column_width = 10;
	const int fruit_rate_column_width = 5;
	
	std::cout << std::left << std::setw(fruit_name_column_width) << "Fruits";
	std::cout << std::right << std::setw(fruit_rate_column_width) << "Rate";
	std::cout << "\n";
	
	for (const Fruit& fruit : fruits)
	{
		std::cout << std::left << std::setw(fruit_name_column_width) << fruit.name;
		std::cout << std::right << std::setw(fruit_rate_column_width) << fruit.rate;
		std::cout << "\n";
	}
}
Fruits     Rate
Apple         5
Coconut       7
Banana       15

When having multiple columns with different alignment you might want to add an additional space character between the columns to avoid that the content of two columns ends up right next to each other. This was not an issue in the example above because there are only two columns and they are aligned away from each other.
Last edited on
C++20 introduced std::format to the toolbox:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
#include <string>
#include <vector>
#include <format>

struct Fruit
{
   std::string name;
   int         rate;
};

int main()
{
   std::vector<Fruit> fruits { { "Apple", 5 },
                               { "Coconut", 7 },
                               { "Banana", 15 } };

   std::cout << std::format("{:<10}{:>5}\n", "Fruits", "Rate");

   for (const Fruit& fruit : fruits)
   {
      std::cout << std::format("{:<10}{:>5}\n", fruit.name, fruit.rate);
   }
}
Fruits     Rate
Apple         5
Coconut       7
Banana       15
Even better, you can specify the line format as a string - so that the format for the header and the item lines uses the same format! if the format is changed, then it applies to both without having to remember.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string>
#include <vector>
#include <format>

struct Fruit {
	std::string name;
	int         rate {};
};

int main() {
	constexpr const char* linFormat { "{:<10}{:>5}\n" };
	const std::vector<Fruit> fruits { { "Apple", 5 },
					{ "Coconut", 7 },
					{ "Banana", 15 } };

	std::cout << std::format(linFormat, "Fruits", "Rate");

	for (const Fruit& fruit : fruits)
		std::cout << std::format(linFormat, fruit.name, fruit.rate);
}

Last edited on
Ah ha, seeplpus! I originally tried using a C++ string as the format specifier and Visual Studio vomited up all over the place. I didn't think to try a C string.

Ooops!

I haven't looked at all of what <format> has to offer yet. I'd bet using a a variable to set the size of a field is possible.
> I'd bet using a a variable to set the size of a field is possible.

std::vformat https://en.cppreference.com/w/cpp/utility/format/vformat
I originally tried using a C++ string as the format specifier and Visual Studio vomited up all over the place. I didn't think to try a C string.


If you want a variable to be a string that doesn't change - such as with linFormat - then const char* can be specified as constexpr whereas const std::string can't (as yet).
I'd bet using a a variable to set the size of a field is possible.


https://en.cppreference.com/w/cpp/utility/format/formatter

Anything printf() can do, format can do better...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <string>
#include <vector>
#include <format>

struct Fruit {
	std::string name;
	int         rate {};
};

int main() {
	constexpr size_t fsize { 10 };
	constexpr size_t rsize { 5 };
	constexpr const char* linFormat { "{:<{}}{:>{}}\n" };
	const std::vector<Fruit> fruits { { "Apple", 5 },
								{ "Coconut", 7 },
								{ "Banana", 15 } };

	std::cout << std::format(linFormat, "Fruits", fsize, "Rate", rsize);

	for (const Fruit& fruit : fruits)
		std::cout << std::format(linFormat, fruit.name, fsize, fruit.rate, rsize);
}


Last edited on
> I'd bet using a a variable to set the size of a field is possible

Yes. Repeat: std::vformat https://en.cppreference.com/w/cpp/utility/format/vformat

For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <string>
#include <vector>
#include <format>

template< typename... ARGS >
std::string vformat( std::string_view fmt, ARGS&&... args )
{
    return std::vformat( fmt, std::make_format_args( std::forward<ARGS>( args )... ) ) ;
}

struct Fruit
{
    std::string name;
    int         rate = 0 ;
};

int main()
{
    const std::vector<Fruit> fruits { { "Apple", 5 },
                                      { "Coconut", 7 },
                                      { "Watermelon", 10 },
                                      { "Banana", 15 } };

    std::size_t max_sz = 0 ;
    for( const auto& f : fruits ) if( f.name.size() > max_sz ) max_sz = f.name.size() ;

    std::string fmt = "{:<" + std::to_string( max_sz+2 ) + "}{:>5}\n" ;

    std::cout << vformat( fmt, "Fruits", "Rate" );

    for( const Fruit& fruit : fruits )
    {
        std::cout << vformat( fmt, fruit.name, fruit.rate );
    }
}
Thanks, seeplus, JLBorges. Those snippets are suh-weet! since the size of the field(s) can be determined at run-time. :)

Hmmmmm, looks a bit more complicated than what I've seen C's printf can do:
https://stackoverflow.com/questions/22132216/can-you-use-variables-to-specify-field-width

Given the power that C++ can leverage I'm not too surprised it takes a bit more coding to make it work.

cppreference may be one of the best online C++ references around, but a lot of C++20 "stuff" still doesn't have examples, or is "buried: in other example snippets. That makes it hard(er) for this self-taught hobbyist to suss out some of the finer details.
Hmmmmm, looks a bit more complicated than what I've seen C's printf can do:


In printf() you use * to specify width using a passed param. In format use {}. Just as easy! See my example above.

Note though the order is reversed in format() to printf(). In printf() the width variable comes before the value whereas in format the width comes after. This has teeth and bites!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
using namespace std;

struct FMT
{
   int w, p;
   FMT( int w, int p = 6 ) : w(w), p(p) {}
   friend ostream & operator << ( ostream &out, const FMT &fmt ){ return out << setw( fmt.w ) << setprecision( fmt.p ) << fixed; }
};

struct Fruit
{
   string name;
   int number;
   double unitPrice;
};

int main()
{
   vector<Fruit> fruits{ { "Apple", 5, 0.35 }, { "Coconut", 7, 0.6 }, { "Watermelon", 10, 0.8 }, { "Banana", 15, 0.12 } };
   int W = 0;
   for ( Fruit &f: fruits ) if ( f.name.size() > W ) W = f.name.size();
   for ( Fruit &f: fruits ) cout << left << FMT( W + 1 ) << f.name << right << FMT( 3 ) << f.number << FMT( 6, 2 ) << f.number * f.unitPrice << '\n';
}


Apple        5  1.75
Coconut      7  4.20
Watermelon  10  8.00
Banana      15  1.80
C++20 String Formatting Library: An Overview and Use with Custom Types - Marc Gregoire - CppCon 2020

https://www.youtube.com/watch?v=IdM0Z2a4fjU
Topic archived. No new replies allowed.