If you have defined your own input or output operator for a class type, you may want to offer some flexibility in how you handle input or output of instances of that class. The I/O Stream Classes let you define stream-specific flags that you can then use with the format state member functions such as setf() and unsetf(). You can then code checks for these flags in the input and output operators you write for your class types, and determine how to handle input and output according to the settings of those flags.
For example, suppose you develop a program that processes customer names and addresses. In the original program, the postal code for each customer is written to the output file before the country name. However, because of postal regulations, you are instructed to change the record order so that the postal code appears after the country name. The following example shows a program that translates from the old file format to the new file format, or from the new file format to the old.
The program checks the input file for an exclamation mark as the first byte. If one is found, the input file is in the new format, and the output file should be in the old format. Otherwise the reverse is true. Once the program knows which file should be in which format, it requests a free flag from each file's stream object. It reads in and writes out each record, and closes the file. The input and output operators for the class check the format state for the defined flag, and order their output accordingly.
// Defining your own format flags
#include <fstream.h> #include <stdlib.h>
long InFileFormat=0; long OutFileFormat=0;
class CustRecord { public: int Number; char Name[48]; char Phone[16]; char Street[128]; char City[64]; char Country[64]; char PostCode[10]; };
ostream& operator<<(ostream &os, CustRecord &cust) { os << cust.Number << '\n' << cust.Name << '\n' << cust.Phone << '\n' << cust.Street << '\n' << cust.City << '\n'; if (os.flags() & OutFileFormat) // New file format os << cust.Country << '\n' << cust.PostCode << endl; else // Old file format os << cust.PostCode << '\n' << cust.Country << endl; return os; }
istream& operator>>(istream &is, CustRecord &cust) { is >> cust.Number; is.ignore(1000,'\n'); // Ignore anything up to and including new line is.getline(cust.Name,48); is.getline(cust.Phone,16); is.getline(cust.Street,128); is.getline(cust.City,64); if (is.flags() & InFileFormat) { // New file format! is.getline(cust.Country,64); is.getline(cust.PostCode,10); } else { is.getline(cust.PostCode,10); is.getline(cust.Country,64); } return is; }
void main(int argc, char* argv[]) { if (argc!=3) { // Requires two parameters cerr << "Specify an input file and an output file\n"; exit(1); } ifstream InFile(argv[1]); ofstream OutFile(argv[2],ios::out);
InFileFormat = InFile.bitalloc(); // Allocate flags for OutFileFormat = OutFile.bitalloc(); // each fstream
if (InFileFormat==0 || // Exit if no flag could OutFileFormat==0) { // be allocated cerr << "Could not allocate a user-defined format flag.\n"; exit(2); }
if (InFile.peek()=='!') { // '!' means new format InFile.setf(InFileFormat); // Input file is in new format OutFile.unsetf(OutFileFormat); // Output file is in old format InFile.get(); // Clear that first byte } else { // Otherwise, write '!' to OutFile << '!'; // the output file, set the OutFile.setf(OutFileFormat); // output stream's flag, and InFile.unsetf(InFileFormat); // clear the input stream's } // flag
CustRecord record; while (InFile.peek()!=EOF) { // Now read the input file InFile >> record; // records and write them OutFile << record; // to the output file, }
InFile.close(); // Close both files OutFile.close(); }
The following shows sample input and output for the program. If you take the output from one run of the program and use it as input in a subsequent run, the output from the later run is the same as the input from the preceding one.
Input File | Output File |
---|---|
3848 John Smith 4163341234 35 Baby Point Road Toronto M6S 2G2 Canada 1255 Jean Martin 0418375882 48 bis Ave. du Belloy Le Vesinet 78110 France |
!3848 John Smith 4163341234 35 Baby Point Road Toronto Canada M6S 2G2 1255 Jean Martin 0418375882 48 bis Ave. du Belloy Le Vesinet France 78110 |
Note that, in this example, a simpler implementation could have been to define a global variable that describes the desired form of output. The problem with such an approach is that later on, if the program is enhanced to support input from or output to a number of different streams simultaneously, all output streams would have to be in the same state (as far as the user-defined format variable is concerned), and all input streams would have to be in the same state. By making the user-defined format flag part of the format state of a stream, you allow formatting to be determined on a stream-by-stream basis.