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:

<lang cpp>#include <vector>

  1. include <string> // for getline etc.
  2. include <iostream>
  3. include <sstream> // for istringstream
  4. include <algorithm> // for max
  5. include <iomanip> // for setw
  6. 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++;
       }
   }

}</lang>

A function object that fills an array with column widths:

<lang cpp>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 );

}</lang>

A function object that outputs fields formatted in columns:

<lang cpp>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;

}</lang>

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:

<lang cpp>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;
   }

}</lang>