TITLE: notes on formatted streams io (Source: Jamshid Afshar, jamshid@ses.com) NG: C Ng (el934931@stoat.shef.ac.uk) |> I am a Pascal programmer and new to C++. |> I have certain queries about formatted I/O, I read several books on C++ but |> apparently I don't seem to get a specific answer. KANZE: kanze@gabi-soft.fr (J. Kanze), 22 Jun 95 This seems to be the single biggest problem with iostream; most books just skip over it. A shame, since it is incredibly flexible and powerful. NG: |> Suppose I want to write two column of variables a and b, both real to an |> output file. If my data are 1.23e10, 21e19, 7.4, 127e3 etc. my output will |> read 1.23e10, 2.1e20, 7.4, 127000. Now what I want is some kind of formatting |> which I can do easily in Pascal, that is, I can specify that I want all data |> to be in decimal format with so many no. of decimal places, or exponential |> format with so many decimal places for the pre-exponent and the exponent, and |> any unfilled spaces will be padded with blanks. KANZE: You have several aspects of the format, which are controlled separately. There are three basic output formats for floating point data: fixed, scientic, and mixed. (The default is mixed.) You can choose by means of the setf member function of your output stream: cout.setf( ios::fixed , ios::floatfield ) ; The exact meaning of precision depends on the format. For fixed, it is the number of digits after the decimal point; for the other formats, it is the total number of digits displayed. The precision is set by: cout.precision( 3 ) ; The width is the minimum number of characters output. (There may be more, if the combination of value and format require it.) To set: cout.width( 7 ) ; There are a couple of other odd flags, for example: ios::showpoint always display the decimal point, even if nothing follows. ios::uppercase in scientific format, use E instead of e. These can be set with the single parameter form of setf, and reset using unsetf, e.g.: cout.setf( ios::showpoint ) ; cout.unsetf( ios::showpoint ) ; The fill character can be set using: cout.fill( '0' ) ; With the exception of width, these parameters are `sticky'; they retain the last value set. For this reason, it is generally considered polite to save them before modification, and to restore them when you are through. The normal way of doing this is to use an iosave class: iosave save( cout ) ; This class will restore the values when it goes out of scope. Regretfully, this class is not provided by the implementations, and at present, there is no portable implementation possible. An implementation for SunCC 4.0.1 is appended. All of these parameters can be set by means of `manipulators', as well. In the case of width, this is the usual method, since it must be set for each value output: cout << setw( 7 ) << d ; Precision is also occasionally set this way (manipulator setprecision). It is somewhat more difficult to set the format, since it is necessary to first reset the preceding value: cout << resetiosflags( ios::floatfield ) << setiosflags( ios::fixed) << d ; (I think that you will agree that if nothing else, the cout.setf format is less verbose.) Thus, if in a function, you want to output the classical fortran 6.2 format: iosave save( cout ) ; cout.setf( ios::fixed , ios::floatfield ) ; cout.precision( 2 ) ; cout << setw( 6 ) << d ; For those already familiar with iostreams, it isn't difficult to encapsulate this into a fmt manipulator, which takes a string describing the format, sets all of the necessary flags to implement it, after saving the preceding configuration in a temporary variable, which restores it when the temporary is destructed (at the end of the full expression with a modern compiler). This is not a job for a beginner with iostreams (or C++), though. iosave class: ------------------------------------------------------ class iosave { public : iosave( ios& stream ) ; ~iosave() ; private : iosave( iosave const& ) ; iosave& operator=( iosave const& ) ; ios* theStream ; long theFlags ; char theFill ; int thePrecision ; } ; iosave::iosave( ios& stream ) : theStream( &stream ) , theFlags( stream.flags() ) , theFill( stream.fill() ) , thePrecision( stream.precision() ) { } iosave::~iosave() { theStream->flags( theFlags ) ; theStream->fill( theFill ) ; theStream->precision( thePrecision ) ; }