Align columns/C++

From Rosetta Code
Align columns/C++ is part of Column Aligner. You may find other members of Column Aligner at Category:Column Aligner.

The following code fragments are all in one source file for this example (and in this order), but broken up here for clarity.

A reusable template function that handles the tokenizing, and is independent of any work that might wish to be done with the results:

#include <vector>
#include <string> // for getline etc.
#include <iostream>
#include <sstream> // for istringstream
#include <algorithm> // for max
#include <iomanip> // for setw
#include <fstream> // for ofstream
 
using namespace std;
 
template< typename C >
void enumerateFields( const string& strInput, char chDelim, C callback )
{
istringstream issFile( strInput );
string strLine;
string strField;
size_t nColIndex;
 
while ( getline( issFile, strLine ) )
{
istringstream issLine( strLine );
nColIndex = 0;
 
while ( getline( issLine, strField, chDelim ) )
{
callback( nColIndex, strField );
 
nColIndex++;
}
}
}

A function object that fills an array with column widths:

typedef vector< size_t > ColWidths;
 
struct MaxColWidthsDeterminer
{
explicit MaxColWidthsDeterminer( ColWidths& colWidths )
: m_colWidths( colWidths ) {}
 
void operator()( size_t nColIndex, const string& strField );
 
ColWidths& m_colWidths;
};
 
void MaxColWidthsDeterminer::operator()( size_t nColIndex,
const string& strField )
{
size_t nWidth = strField.length();
 
if ( nColIndex >= m_colWidths.size() )
m_colWidths.push_back( nWidth );
else
m_colWidths[ nColIndex ] = max( m_colWidths[ nColIndex ], nWidth );
}

A function object that outputs fields formatted in columns:

struct FormattedLister
{
enum Alignment { eLeft, eRight, eCenter };
 
FormattedLister( const ColWidths& colWidths, ostream& os,
Alignment alignment = eLeft )
: m_colWidths( colWidths ), m_os( os ), m_alignment( alignment ),
m_nPrevColIndex( 0 )
{
m_savedStreamFlags = os.flags();
m_os.setf( ( m_alignment == eRight ) ? ios::right : ios::left,
ios::adjustfield );
}
 
~FormattedLister()
{
m_os.flags( m_savedStreamFlags );
}
 
void operator()( size_t nColIndex, const string& strField );
 
const ColWidths& m_colWidths;
ostream& m_os;
Alignment m_alignment;
size_t m_nPrevColIndex;
ios::fmtflags m_savedStreamFlags;
};
 
void FormattedLister::operator()( size_t nColIndex, const string& strField )
{
if ( nColIndex < m_nPrevColIndex )
m_os << '\n';
 
if ( m_alignment == eCenter )
{
size_t nSpacesBefore = ( m_colWidths[ nColIndex ] - strField.length() )
/ 2;
size_t nSpacesAfter = m_colWidths[ nColIndex ] - strField.length()
- nSpacesBefore + 1;
 
m_os << string( nSpacesBefore, ' ' ) << strField
<< string( nSpacesAfter, ' ' );
}
else
{
m_os << setw( static_cast< streamsize >( m_colWidths[ nColIndex ] ) )
<< strField << ' ';
}
 
m_nPrevColIndex = nColIndex;
}

The test program, that makes a pass through the data to determine the column widths, and then three more for outputting in each of the three column alignments:

int main()
{
const string strInput(
"Given$a$text$file$of$many$lines,$where$fields$within$a$line$\n"
"are$delineated$by$a$single$'dollar'$character,$write$a$program\n"
"that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$\n"
"column$are$separated$by$at$least$one$space.\n"
"Further,$allow$for$each$word$in$a$column$to$be$either$left$\n"
"justified,$right$justified,$or$center$justified$within$its$column." );
 
const char chDelim = '$';
 
ColWidths colWidths;
 
enumerateFields( strInput, chDelim, MaxColWidthsDeterminer( colWidths ) );
 
ofstream outFile( "ColumnAligner.txt" );
if ( outFile )
{
enumerateFields( strInput, chDelim,
FormattedLister( colWidths, outFile ) );
outFile << '\n';
 
enumerateFields( strInput, chDelim,
FormattedLister( colWidths, outFile,
FormattedLister::eRight ) );
outFile << '\n';
 
enumerateFields( strInput, chDelim,
FormattedLister( colWidths, outFile,
FormattedLister::eCenter ) );
outFile << endl;
}
}