Homework 5: I/O

Educational Objectives: After completing this assignment the student should have the following knowledge, ability, and skills:

Operational Objectives: Create a file formatting program bases.cpp as described in the code requirements and specifications below.

Deliverables: One file bases.cpp.

I/O Manipulators

The goal for this assignment is learn about and use I/O manipulators. We will use the standard I/O manipulators defined in the namespace std.

An I/O manipulator may seem mysterious at first, but it is really a simple function. (The manipulators that take parameters are somewhat more complex, requiring function objects.) Here's how it works:

Various properties of I/O are controlled by "flags", which are public member variables of the base class std::ios and hence inherited by other classes such as std::istream, std::ostream, std::ifstream, and std::ofstream. These flag names are:

boolalpha , dec , fixed , hex , internal , left , oct , right , scientific ,
showbase , showpoint , showpos , skipws , unitbuf , uppercase , adjustfield ,
basefield , floatfield

These names can be manipulated as if they were single bits (and in most implementations they are single bits, although this is not specified by the language). There are also member functions of class ios

void setf   (ios::flag F);
void unsetf (ios::flag F);

that take a flag as argument and either set or unset the flag. If, for example, you want to unset the flags dec and oct and set the hex flag for the stream object s, you would make these two calls:

s.unsetf (std::ios::dec | std::ios::oct);
s.setf (std::ios::hex);

Note that the scope of the flag must be resolved (std::ios::). Note also the use of bitwise OR to combine the dec and oct flags into a single argument.

An I/O manipulator has the following prototype pattern:

std::ios& M(std::ios& s);  

where M is the name of the manipulator. Note that it takes a stream base object by reference and then returns the same object (modified), so that it makes sense to call the manipulator using the syntax

os << ... << M << ... // manipulator call syntax for an ostream os
is >> ... >> M >> ... // manipulator call syntax for an istream is

This syntax is facilitated by overloads of the input and output operators something like this:

ios& operator << (ios& s, F())
{
  return F();
}
ios& operator >> (ios& s, F())
{
  return F();
}

These overloads are already defined in the iostream library. So, the only thing left to do is actually write the manipulator function (the parameter F() in the operator overloads above). Here is how to write the hex manipulator function:

std::ios& hex (std::ios& s)
{
  s.unsetf(std::ios::oct | std::ios::dec);
  s.setf(std::ios::hex);
  return s;
}

Note the return of the incoming stream object (after its state is modified by changing the flag settings). The effect of this code:

is >> hex;
os << hex;

is to set the hex flag (by calling hex(os)) and return the modified stream object for further processing. For input streams, this will mean that numerical input is assumed to be in hexadecimal notation. For output streams, this will mean that numbers are output in hexadecimal notation.

Procedural Requirements

  1. Create and work within a separate subdirectory cop3330/hw5. Review the COP 3330 rules found in Introduction/Work Rules.

  2. Begin by copying the following files from the course directory: into your hw5 directory:

    [LIB]/hw5/data?.in
    [LIB]/hw5/hw5submit.sh
    [LIB]/area51/bases_s.x
    [LIB]/area51/bases_i.x
    

    The naming of these files uses the convention that _s are compiled for Sun/Solaris and _i are compiled for Intel/Linux. Use one of the sample client executables to experiment to get an understanding of how your program should behave.

  3. Create the file bases.cpp that meets the specifications required below.

  4. Turn in the file bases.cpp using the hw5submit.sh submit script.

    Warning: Submit scripts do not work on the program and linprog servers. Use shell.cs.fsu.edu to submit projects. If you do not receive the second confirmation with the contents of your project, there has been a malfunction.

Code Requirements and Specifications

  1. Do not use any "using" directives in your code.

  2. The following constants should be declared and used in bases.cpp:

    static const unsigned short
      cwh = 16, // max digits of unsigned long hexadecimal
      cwd = 20, // max digits of unsigned long decimal
      cwo = 26, // max digits of unsigned long octal
      cs1 = 4, cs2 = 2// col spacers
    

    Do not hard code literal values for column widths and column spacers. These constants are set up to produce tables when the integers are 64-bit (eight byte) words (the size of type unsigned long). The various cw values are the max size of a 64-bit number in the various notations. The cs values are the spacing preceeding the column.

  3. The application bases should take two file names at the command line. The first is an input file, which contains documentation, instructions, and data. The second is an output file in which a table should be created according to the input file instructions and data.

    Note that there are options specifying the numerical notation for input and for the notation in two columns of output. The output file should be a table with header (giving the names of the output file and the input file) and column headers indicating the notation used in that column. The data in the table consists of one line for each number in the input file, in the notation specified by the formatting characters. See bases.x and the example input data files for clarification of this behavior.

  4. The input file is formatted as follows:

    input file format:
    =================
    
      1: file documentation lines at top of file begin with '#'
    
      2: next string of data: (five format control characters)
    
        format for input file
           first character - how to interpret input numbers:
            'o' or 'O': read in octal notation
            'd' or 'D': read in decimal notation
            'h' or 'H': read in hexadecimal notation
    
        next characters format output, read in pairs
        (two for col 1, two for col 2)
    
          first character of pair: numerical notation
            'd' = decimal
            'D' = decimal
            'h' = hex/lower case
            'H' = hex/upper case
            'o' = octal
            'O' = octal
          second character of pair: fill (affects hex and oct only)
            'f' = fill hex and octal with leading zero, 64-bit
            'n' = no fill for hex and octal
    
      3: remainder of input file: unsigned long integers in notation designated
         by first format control character
    

    The program should skip the documentation, then read the five formatting characters, and then proceed to read the file data, constructing the table in the output file as it goes. (There is no need to store more than one integer at a time, just read an integer and then write the line of the output file table corresponding to that input.)

  5. The output file should begin with two lines of information giving the names of the file and the input data file. The third line should be blank, and the fourth line should have the headers for the table columns. The actual table entries should commence below, and allign correctly with, the column headers.

  6. Be sure that hex and octal numbers are output with the correct number of leading zeros in case 'f'. The defined constants cwx above give the correct size needed for 64-bit numbers.

  7. Output from your bases.x should duplicate exactly the output produced by the distributed executables. When in doubt, use the distributed executables for guidance in constructing the output file.

Hints