Exercise 5-5
Write a function named center(const vector<string>&) that returns a picture in which all the lines of the original picture are padded out to their full width, and the padding is as evenly divided as possible between the left and right sides of the picture. What are the properties of pictures for which such a function is useful? How can you tell whether a given picture has these properties?
Solution
When I first saw this question, straight away it reminded me of the frame function that the author built in Chapter 5 / my Solution to Exercise 5-0 (Part 3/3). The original frame function effectively pad all the strings with empty spaces, making all strings equal width and left aligned, and put a frame around it to make it “box-like” when we display the vector<string> line by line vertically – like a formatted article.
Also recall that in my Solution to Exercise 5-1 (about building a permuted index page), I modified the frame function so that it takes on additional parameters regarding whether the padding should be left-aligned or right-aligned. So instead of creating a brand new function center(const vector<string>&), I may as well reuse the frame function and enhance it so that we have an extra option to center-aligned.
In this post I shall describe how I modify the frame function to allow user to center-align the vector<string> “picture text”.
Algorithm
We know what the algorithm looks like for left-align and right-align for the frame function from Solution to Exercise 5-1. The algorithm for center-aligned is very similar. i.e. we use the width function to identify the max length of the string (within the vector<string>). We then compute how many padding spaces are required on both (left and right) sides of each individual text string – the padding on the left should be more or less the same as the right. The diagram below essentially summarises the equations in computing the various lengths required.
These equations are reflected in the enhanced frame.cpp code below.
The Project
Let me wrap up the whole project here – we should be able to figure out how the enhanced frame function works, and how we can easily use it. (Note that I have reused the knowledge / experience gained from the previous exercises in Chapter 5).
(The enhanced frame function is the answer to the exercise – the other codes are wrapped together for demonstration purposes.)
- main.cpp
- frame.h
- frame.cpp
- read_lines.h
- read_lines.cpp
- vout.h
- vout.cpp
Source and Header Files
main.cpp
#include <iostream> #include <string> #include <vector> #include "read_lines.h" #include "frame.h" #include "vcout.h" using std::string; using std::vector; int main() { // read lines via standard console input and append to article vector<string> article = read_lines(); // padded each line of the article vector<string> paddedArticle = frame(article,"center",'*'); // display the now padded article vcout(paddedArticle); return 0; }
frame.h
#ifndef GUARD_FRAME_H #define GUARD_FRAME_H #include <string> #include <vector> std::string::size_type width(const std::vector<std::string>&); std::vector<std::string> frame(const std::vector<std::string>&, const std::string&, const char); #endif // GUARD_FRAME_H
frame.cpp
#include <string> // string #include <vector> // vector #include <algorithm> // max using std::string; using std::vector; using std::max; string::size_type width(const vector<string>& v) { string::size_type maxlen = 0; for(vector<string>::size_type i = 0; i != v.size(); ++i) maxlen = max(maxlen, v[i].size()); return maxlen; } vector<string> frame(const vector<string>& v, const string& align, char c) { vector<string> ret; typedef string::size_type stringSize; stringSize maxlen = width(v); string symbol(1, c); string border(maxlen + 4, c); // write the top border ret.push_back(border); // write each interior row, bordered by an asterisk and a space for (vector<string>::size_type i = 0; i != v.size(); ++i) if (align == "left") ret.push_back(symbol + " " + v[i] + string(maxlen - v[i].size(), ' ') + " " + symbol); else if (align == "right") ret.push_back(symbol + " " + string(maxlen - v[i].size(), ' ') + v[i] + " " + symbol); else if (align == "center") { stringSize leftPadSize = (maxlen - v[i].size() ) / 2 ; stringSize midLineSize = v[i].size(); stringSize rightPadSize = maxlen - leftPadSize - midLineSize; string padLine = symbol + " " + string(leftPadSize, ' ') + v[i] + string(rightPadSize, ' ') + " " + symbol; ret.push_back(padLine); } // write the bottom border ret.push_back(border); return ret; }
read_lines.h
#ifndef GUARD_READ_LINES_H #define GUARD_READ_LINES_H #include <string> #include <vector> std::vector<std::string> read_lines(); #endif // GUARD_READ_LINES_H
read_lines.cpp
#include <string> // string #include <vector> // vector #include <iostream> // cin, getline using std::string; using std::vector; using std::cin; using std::getline; // Read lines and return the line collection vector<string> read_lines() { string line; vector<string> ret; while (getline(cin, line)) ret.push_back(line); return ret; }
vout.h
#ifndef GUARD_VCOUT_H #define GUARD_VCOUT_H #include <string> #include <vector> int vcout(const std::vector<std::string>&); #endif // GUARD_VCOUT_H
vout.cpp
#include <iostream> #include <string> // string #include <vector> // vector using std::cout; using std::endl; using std::string; using std::vector; int vcout(const vector<string>& v) { for (vector<string>::const_iterator iter = v.begin(); iter != v.end(); ++iter) { cout << (*iter) << endl; } return 0; }
Test Program
Test 1 – Center Align
Update this line in main program:
vector<string> paddedArticle = frame(article,"center",'*');
Test Result:
this is an example to illustrate framing ^Z ************** * this is an * * example * * to * * illustrate * * framing * **************
Test 2 – Left Align
Update this line in main program:
vector<string> paddedArticle = frame(article,"left",'*');
Test Result:
this is an example to illustrate framing ^Z ************** * this is an * * example * * to * * illustrate * * framing * **************
Test 3 – Right Align
Update this line in main program:
vector<string> paddedArticle = frame(article,"right",'*');
Test Result:
this is an example to illustrate framing ^Z ************** * this is an * * example * * to * * illustrate * * framing * **************
You have centred a picture within a frame. My solution centres a picture not a frame, which is what the question asks. Of course, my output then could be passed into another function to add a frame! This splits to two processes.