FBB::CSVTable is used to fill tables row-wise. By default the table's elements are comma-separated. The elements may contain any type of data that can also be inserted into std::ostreams, as may also contain horizontal lines (optionally spanning multiple columns).
Before inserting elements into the table the widths, alignment types and precisions of the table's columns are defined. By default values are right-aligned. While inserting table elements the alignment types and precisions may be altered for specific elements, optionally spanning multiple columns. When inserting values whose representations require more characters than the current widths of the columns receiving those values then those larger widths take precedence over the defined column widths.
Different from tables defined by FBB::Table(3bobcat) all data inserted into CSVTables do not have to be completely available before the table is inserted into a destination std::ostream. As the table's column formats are known before entering the data the CSVTable knows which format to use for which column. These column format specifications may be defined in multiple ways, e.g., by using text labels and values. CSVTable objects always use the widest column specifications and alignment types that were specified last.
When inserting elements into CSVTables the standard C++ IO manipulators can also be used. Table rows do not automatically end after the table's last column has been filled. But when inserting elements beyond the last column they are inserted as-is (but then the standard I/O format specifications can still be used).
Table column definitions and table rows end at the end of insertion statements (see below at the descriptions of the various operator<< functions). E.g.,
CSVTable tab;
...
tab << 1 << 2; // two elements in this row
tab << "one" << "two" << 3; // three elements in this row
CSVTable uses two support classes handling, respectively, the definitions of the characteristics of the table's columns and inserting values into the table's elements. CSVTabDef handles the table's column definitions, CSVTabIns handles insertions into the table elements. They offer various insertion operators which are described below.
Constructing tables normally consists of two steps: first the characteristics of the columns are defined, then values are inserted into the table's elements. This sequence is not enforced by CSVTable: after inserting values into the table column definitions may be updated, whereafter additional values may be inserted into the table which then use the updated column definitions.
FBB::FMT objects are returned by several free functions (like left, described below in section FREE FUNCTIONS), and FMT defines the enumeration Align (see the next section) specifying alignment types. FMT objects are internally used by CSVTable objects. A FMT object specifies the width, the precision when floating point numbers are inserted, the column's alignment type (left, right or centered), and the number of table columns to use.
FMT objects can be inserted into std::ostream objects showing its characteristics. FMT provides the following (const) accessors:
The static member char const *FMT::align(FMT::Align value) returns the textual label corresponding to value.
The enum FMT::Align defines values indicating the alignment types of the table's columns:
In addition, when inserting horizontal lines, the value FMT::Align::HLINE is used.
The move constructor and move assignment operator are available; the copy constructor and assignment operator are not available.
In the provided examples tab refers to an existing CSVTable object. Each insertion statement (note: not insertion expression) either defines or updates the table columns' definitions or fills the next row of the table with data.
Defining column characteristics
The return types and left-hand side operands of the following insertion operators are specified as CSVTabDef. The member fmt() (cf. section MEMBER FUNCTIONS) returns a CSVTabDef object which is then used in combination with the following insertion operators to define the characteristics of the table's columns.
// left align using 10 char. positions:
tab.fmt() << FBB::left(10);
// 1st col now right aligned, but its
// width remains 10
tab.fmr() << FBB::right(4);
// 2 columns, having widths 2 and 5:
tab.fmt() << 12 << "hello";
Inserting table elements
In addition to the insertion operator actually inserting a value into the next table's column(s) several format modifying insertion operators are available. When a series of specifications are inserted before the actual value is inserted then the specification inserted just before inserting the table's value is used, overruling that column's default specification. Format specifications other than those provided by the standard I/O manipulators are ignored when used beyond the table's last column.
The return types and left-hand side operands of the following insertion operators use CSVTabIns objects. CSVTable's conversion operator operator CSVTabIns() described below returns a CSVTabIns object which is used by the following insertion operators to insert values into the table.
// left align using precision 2. E.g.,
// e.g., '12.13 '
tab << left(2) << 12.1278;
// centers '12' in its column,
// e.g., ' 12 '
tab << FMT::CENTER << 12;
// 'hi' is left-aligned, using the
// using the default width and precision
tab << std::left << "hi";
// writes, e.g., 'one, hi there'
tab << "one" << FMT::Sep{" "} << "hi" << "there";
In the provided examples tab refers to an existing CSVTable object.
// Define three right-aligned columns,
// having widths of 3, 3 and 5.
tab.fmt("one, two, three");
// add columns 4 thru 6
tab.fmt("one, two, three", 3);
When using another argument then insertions start in column idx. If dx exceeds the last-used column index then intermediate columns remain empty.
If idx is less than the column index that is used at the next insertion an exception is thrown.
Insertions beyond the table's last column are processed, but then CSVTabIns's insertion operators are ignored, inserting values as-is. However, in that case the standard std::ostream manipulators can also be used;
Following more the current row doesn't end, but values inserted next are inserted into the same row. Example:
// a row containing one element:
tab << 1;
// the next row contains 2 elements:
tab.more() << 1 << 2;
// now containing 4 elements
// (element at idx 2 remains empty):
tab.more(3) << 4;
// completes the row, now having
// 5 elements:
tab << 5;
Following more calls the current row ends at the next tab.row call. If following more calls the current row should merely end then simply use tab.row();
The trimmed comma-separated elements of text are inserted into the current row, without ending the current row;
// a row containing one element:
tab << 1;
// the next row contains 2 elements:
tab.more() << 1 << 2;
// the now contains 4 elements
// (element at idx 2 remains empty):
tab.row(3) << 4;
The trimmed comma-separated elements of text are inserted into the current row, whereafter the row ends;
In the following examples tab.fmt() refers to a CSVTabDef object.
Defining Column Characteristics
The following functions are used to specify the alignment, width and optional precision of columns. The first argument of these functions specifies the column's width, the second argument is optional and specifies the column's precision (used when inserting floating point values). The precision is only used if its value is less than the column's width.
// values are centered in fields of 10
// characters wide, floating point values
// use 3 digit behind the decimal point:
tab.fmt() << center(10, 3);
// values are left-aligned in fields
// of 5 characters wide.
tab.fmt() << left(5);
// values are right-aligned in fields
// of 5 characters wide.
tab.fmt() << right(5);
Right-alignment is also used when using CSVTab's fmt(std::string)
member or when directly inserting values into CSVTabDef objects;
Inserting Table Elements
In the following examples tab refers to a CSVTable object returning a CSVTabIns object using its conversion operator.
Except for the function hline the following functions are used to alter the column's default alignment and precision. The precision is only used if its value is less than the column's width. By specifying ~0U the precision is ignored. If only the default alignment should be overruled then inserting the corresponding FMT::Align value suffices.
Altering the default alignment of individual columns:
// centers 9.87 in column 1
tab << center(2) << 9.876";
// left-aligns 9.87 in column 1
tab << left(2) << 9.876";
// right-aligns 9.87 in column 1
tab << right(2) << 9.876";
By default CSVTable uses right-alignment.
Joining columns:
Alignments specifications may span multiple columns. This is realized through the join functions. When inserting a value after inserting the return value of a join member then that value is inserted occupying all the columns and using the alignment type specified when calling join. If necessary the number of columns is reduced to avoid exceeding the table's last column.
// writes (assuming columns 2 and 3 occupy
// 10 characters):
// left, mid , right
tab << "left" << join(2, FMT::CENTER) << "mid" << "right"";
Inserting horizontal lines:
If a single table element should contain a horizontal line then simply inserting Align::HLINE works fine. The hline functions are used to insert horizontal lines spanning one or more table columns.
// columns 1 and 2: a horizontal line, column 3:
// contains 'hi' (preceded by the column separator)
tab << hline(2) << "hi";
#include <bobcat/csvtable>
using namespace FBB;
int main()
{
CSVTable tab;
tab.fmt() << "case" << right("length", 2) << right("weight", 1) <<
right("length", 2) << right("weight", 1);
tab.sep(" ");
tab << hline();
tab << "" << join(4, FMT::CENTER) << "Gender";
tab << "" << hline();
tab << "" << join(2, FMT::CENTER) << "Female" <<
join(2, FMT::CENTER) << "Male";
tab << "" << hline(2) << hline(2);
tab << "Case" << "Length" << "Weight" << "Length" << "Weight";
tab << hline();
tab << 1 << 1.744 << 55.345 << 1.7244 << 64.801;
tab << 2 << 1.58 << 57.545 << 1.8174 << 81.451;
tab << 3 << 1.674 << 62.125 << 1.8244 << 80.201;
tab << hline();
}
This program writes the following table to std::cout:
------------------------------------
Gender
------------------------------
Female Male
-------------- --------------
Case Length Weight Length Weight
------------------------------------
1 1.74 55.3 1.72 64.8
2 1.58 57.5 1.82 81.5
3 1.67 62.1 1.82 80.2
------------------------------------