25. Getting Started with OLE

Chapter 12 - An Introduction to I/O in C++

Visual C++ 6: The Complete Reference
Chris H. Pappas and William H. Murray, III
  Copyright 1998 The McGraw-Hill Companies

Operators and Member Functions
The extraction operator and the << insertion operator have been modified to accept arguments of any of the built-in data types, including char *. They can also be extended to accept class argument types.
Probably the first upgrade incompatibility you will experience when converting a C++ program using the older I/O library will be the demised cout << form extension. Under the new release, each iostream library class object maintains a format state that controls the details of formatting operations, such as the conversion base for integral numeric notation or the precision of a floating-point value.
You can manipulate the format state flags by using the setf( ) and unsetf( ) functions. The setf( ) member function sets a specified format state flag. There are two overloaded instances:
setf(long);
setf(long,long);
The first argument can be either a format bit flag or a format bit field. Table 12-1 lists the format flags you can use with the setf(long) instance (using just the format flag).
Table 12-1: Format Flags
Bit Field
Meaning
Flags
ios::basefield
Integral base
ios::hex, ios::oct, ios::dec
ios::floatfield
Floating point
ios::fixed, ios::scientific
The following table lists some of the format bit fields you can use with the setf(long,long) instance (using a format flag and format bit field).
There are certain predefined defaults. For example, integers are written and read in decimal notation. You can change the base to octal, hexadecimal, or back to decimal. By default, a floating-point value is output with six digits of precision. You can modify this by using the precision member function. The following C++ program uses these new member functions:
//
//  advio.cpp
//  A C++ program demonstrating advanced conversions and
//  formatting member functions since Release 2.0. The program
//  will also demonstrate how to convert each of the older
//  Release 1.2 form statements.
//  Copyright (c) Chris H. Pappas and William H. Murray, 1998
//

#include <string.h>
#include <strstrea.h>

#define INULL_TERMINATOR 1

void row (void);
main( )
{
 char   c        =   ‘A’,
        psz1[]   =   “In making a living today many no ”,
        psz2[]   =   “longer leave any room for life.”;
 int    iln      =   0,
        ivalue   =   1234;
 double dPi      =   3.14159265;
 // new declarations needed for Release 2.0
 char psz_padstring5[5+INULL_TERMINATOR],
 psz_padstring38[38+INULL_TERMINATOR];

 // conversions

 // print the c
 // R1.2 cout << form(“\n[%2d] %c”,++ln,c);
 // Notice that << has been overloaded to output char
 row( ); // [ 1]
 cout << c;

 // print the ASCII code for c
 // R1.2  form(“\n[%2d] %d”,++ln,c);
 row( ); // [ 2]
 cout << (int)c;

 // print character with ASCII 90
 // R1.2  form(“\n[%2d] %c”,++ln,90);
 row( ); // [ 3]
 cout << (char)90;
 // print ivalue as octal value
 // R1.2  form(“\n[%2d] %o”,++ln,ivalue);
 row( ); // [ 4]
 cout << oct << ivalue;

 // print lowercase hexadecimal
 // R1.2  form(“\n[%2d] %x”,++ln,ivalue);
 row( ); // [ 5]
 cout << hex << ivalue;

 // print uppercase hexadecimal
 // R1.2  form(“\n[%2d] %X”,++ln,ivalue);
 row( ); // [ 6] cout.setf(ios::uppercase);
 cout << hex << ivalue;
 cout.unsetf(ios::uppercase);    // turn uppercase off
 cout << dec;                    // return to decimal base
 // conversions and format options

 // minimum width 1
 // R1.2  form(“\n[%2d] %c”,++ln,c);
 row( ); // [ 7]
 cout << c;

 // minimum width 5, right-justify
 // R1.2  form(“\n[%2d] %5c”,++ln,c);
 row( ); // [ 8]
 ostrstream(psz_padstring5,sizeof(psz_padstring5))
 << “    ” << c << ends;
 cout << psz_padstring5;
 // minimum width 5, left-justify
 // R1.2  form(“\n[%2d] %-5c”,++ln,c);
 row( ); // [ 9]
 ostrstream(psz_padstring5,sizeof(psz_padstring5))
 << c << “    ” << ends;
 cout << psz_padstring5;

 // 33 automatically
 // R1.2  form(“\n[%d] %s”,++ln,psz1);
 row( ); // [10]
 cout << psz1;

 // 31 automatically
 // R1.2  form(“\n[%d] %s”,++ln,psz2);
 row( ); // [11]
 cout << psz2;
 // minimum 5 overridden, auto
 // R1.2  form(“\n[%d] %5s”,++ln,psz1);
 // notice that the width of 5 cannot be overridden!
 row( ); // [12]
 cout.write(psz1,5);

 // minimum width 38, right-justify
 // R1.2  form(“\n[%d] %38s”,++ln,psz1);
 // notice how the width of 38 ends with garbage data
 row( ); // [13]
 cout.write(psz1,38);

 // the following is the correct approach
 cout << “\n\nCorrected approach:\n”;
 ostrstream(psz_padstring38,sizeof(psz_padstring38)) << “     ”
   << psz1 << ends;
 row( ); // [14]
 cout << psz_padstring38;
 // minimum width 38, left-justify
 // R1.2  form(“\n[%d] %-38s”,++ln,psz2);
 ostrstream(psz_padstring38,sizeof(psz_padstring38))
   << psz2 << “       ” << ends;
 row( ); // [15]
 cout << psz_padstring38;

 // default ivalue width
 // R1.2  form(“\n[%d] %d”,++ln,ivalue);
 row( ); // [16]
 cout << ivalue;

 // printf ivalue with + sign
 // R1.2  form(“\n[%d] %+d”,++ln,ivalue);
 row( ); // [17]
 cout.setf(ios::showpos);     // don’t want row number with +
 cout << ivalue;
 cout.unsetf(ios::showpos);
 // minimum 3 overridden, auto
 // R1.2  form(“\n[%d] %3d”,++ln,ivalue);
 row( ); // [18]
 cout.width(3); // don’t want row number padded to width of 3
 cout << ivalue;

 // minimum width 10, right-justify
 // R1.2  form(“\n[%d] %10d”,++ln,ivalue);
 row( ); // [19]
 cout.width(10);    // only in effect for first value printed
 cout << ivalue;

 // minimum width 10, left-justify
 // R1.2  form(“\n[%d] %-d”,++ln,ivalue);
 row( ); // [20]
 cout.width(10);
 cout.setf(ios::left);
 cout << ivalue;
 cout.unsetf(ios::left);
 // right-justify with leading 0’s
 // R1.2  form(“\n[%d] %010d”,++ln,ivalue);
 row( ); // [21]
 cout.width(10);
 cout.fill(‘0’);
 cout << ivalue;
 cout.fill(‘ ‘);
 // using default number of digits
 // R1.2  form(“\n[%d] %f”,++ln,dPi);
 row( ); // [22]
 cout << dPi;

 // minimum width 20, right-justify
 // R1.2  form(“\n[%d] %20f”,++ln,dPi);
 row( ); // [23]
 cout.width(20);
 cout << dPi;

 // right-justify with leading 0’s
 // R1.2  form(“\n[%d] %020f”,++ln,dPi);
 row( ); // [24]
 cout.width(20);
 cout.fill(‘0’);
 cout << dPi;
 cout.fill(‘ ‘);
 // minimum width 20, left-justify
 // R1.2  form(“\n[%d] %-20f”,++ln,dPi);
 row( ); // [25]
 cout.width(20);
 cout.setf(ios::left);
 cout << dPi;

 // left-justify with trailing 0’s
 // R1.2  form(“\n[%d] %-020f”,++ln,dPi);
 row( ); // [26]
 cout.width(20);
 cout.fill(‘0’);
 cout << dPi;
 cout.unsetf(ios::left);
 cout.fill(‘ ‘);
 // additional formatting precision

 // minimum width 19, print all 17
 // R1.2  form(“\n[%d] %19.19s”,++ln,psz1);
 row( ); // [27]
 cout << psz1;

 // prints first 2 chars
 // R1.2  form(“\n[%d] %.2s”,++ln,psz1);
 row( ); // [28]
 cout.write(psz1,2);

 // prints 2 chars, right-justify
 // R1.2  form(“\n[%d] %19.2s”,++ln,psz1);
 row( ); // [29]
 cout << “                  ”; cout.write(psz1,2);
 // prints 2 chars, left-justify
 // R1.2  form(“\n[%d] %-19.2s”,++ln,psz1);
 row( ); // [30]
 cout.write(psz1,2);

 // using printf arguments
 // R1.2  form(“\n[%d] %*.*s”,++ln,19,6,psz1);
 row( ); // [31]
 cout << “              ”; cout.write(psz1,6);

 // width 10, 8 to right of ‘.’
 // R1.2  form(“\n[%d] %10.8f”,++ln,dPi);
 row( ); // [32]
 cout.precision(9);
 cout << dPi;

 // width 20, 2 to right-justify
 // R1.2  form(“\n[%d] %20.2f”,++ln,dPi);
 row( ); // [33]
 cout.width(20);
 cout.precision(2);
 cout << dPi;
 // 4 decimal places, left-justify
 // R1.2  form(“\n[%d] %-20.4f”,++ln,dPi);
 row( ); // [34]
 cout.precision(4);
 cout << dPi;

 // 4 decimal places, right-justify
 // R1.2  form(“\n[%d] %20.4f”,++ln,dPi);
 row( ); // [35]
 cout.width(20);
 cout << dPi;
 // width 20, scientific notation
 // R1.2  form(“\n[%d] %20.2e”,++ln,dPi);
 row( ); // [36] cout.setf(ios::scientific); cout.width(20);
 cout << dPi; cout.unsetf(ios::scientific);

 return(0);
}

void row (void)
{
 static int ln=0;
 cout << “\n[”;
 cout.width(2);
 cout << ++ln << “] ”;
}
You can use the output from the program to help write advanced output statements of your own:
[ 1] A
[ 2] 65
[ 3] Z
[ 4] 2322
[ 5] 4d2
[ 6] 4d2
[ 7] A
[ 8]     A
[ 9] A    
[10] In making a living today many no
[11] longer leave any room for life.
[12] In ma
[13] In making a living today many no _U_ _
Corrected approach:

[14]      In making a living today many no
[15] longer leave any room for life.       
[16] 1234
[17] +1234
[18] 1234
[19]       1234
[20] 1234      
[21] 0000001234
[22] 3.14159
[23]              3.14159
[24] 00000000000003.14159
[25] 3.14159             
[26] 3.141590000000000000
[27] In making a living today many no
[28] In
[29]                   In
[30] In
[31]               In mak
[32] 3.14159265
[33]                  3.1
[34] 3.142
[35]                3.142
[36] 3.142
The following section highlights those output statements used in the preceding program that need special clarification. One point needs to be made: IOSTREAM.H is automatically included by STRSTREAM.H. The latter file is needed to perform string output formatting. If your application needs to output numeric data or simple character and string output, you will need to include only IOSTREAM.H.
C++ Character Output
In the new I/O library (since Release 2.0), the insertion operator << has been overloaded to handle character data. With the earlier release, the following statement would have output the ASCII value of c:
cout << c;
In the current I/O library, the letter itself is output. For those programs needing the ASCII value, a cast is required:
cout << (int)C;
C++ Base Conversions
There are two approaches to outputting a value using a different base:
cout << hex << ivalue;
and
cout.setf(ios::hex,ios::basefield);
cout << ivalue;
Both approaches cause the base to be permanently changed from the statement forward (not always the effect you want). Each value output will now be formatted as a hexadecimal value. Returning to some other base is accomplished with the unsetf( ) function:
cout.unsetf(ios::hex,ios::basefield);
If you are interested in uppercase hexadecimal output, use the following statement:
cout.setf(ios::uppercase);
When it is no longer needed, you will have to turn this option off:
cout.unsetf(ios::uppercase);
C++ String Formatting
Printing an entire string is easy in C++. However, string formatting has changed because the Release 1.2 cout << form is no longer available. One approach to string formatting is to declare an array of characters and then select the desired output format, printing the string buffer:
pszpadstring38[38+INULL_TERMINATOR];
.
.
.
ostrstream(pszpadstring38,sizeof(pszpadstring38))
  << “     ”   << psz1;
The ostrstream( ) member function is part of STRSTREAM.H and has three parameters: a pointer to an array of characters, the size of the array, and the information to be inserted. This statement appends leading blanks to right-justify psz1. Portions of the string can be output using the write form of cout:
cout.write(psz1,5);
This statement will output the first five characters of psz1.
C++ Numeric Formatting
You can easily format numeric data with right or left justification, varying precisions, varying formats (floating-point or scientific), leading or trailing fill patterns, and signs. There are certain defaults. For example, the default for justification is right, and for floating-point precision it is six. The following code segment outputs dPi left-justified in a field width of 20, with trailing zeroes:
cout.width(20);
cout.setf(ios::left);
cout.fill(‘0’);
cout << dPi;
Had the following statement been included, dPi would have been printed with a precision of two:
cout.precision(2);
With many of the output flags such as left justification, selecting uppercase hexadecimal output, base changes, and many others, it is necessary to unset these flags when they are no longer needed. The following statement turns left justification off:
cout.unsetf(ios::left);
Selecting scientific format is a matter of flipping the correct bit flag:
cout.setf(ios::scientific);
You can print values with a leading + sign by setting the showpos flag:
cout.setf(ios::showpos);
There are many minor details of the current I/O library functions that will initially cause some confusion. This has to do with the fact that certain operations, once executed, make a permanent change until turned off, while others take effect only for the next output statement. For example, an output width change, as in cout.width(20);, affects only the next value printed. That is why the function row( ) has to repeatedly change the width to get the output row numbers formatted within two spaces, as in [ 1]. However, other formatting operations like base changes, uppercase, precision, and floating-point/scientific remain active until specifically turned off.
C++ File Input and Output
All of the examples so far have used the predefined streams cin and cout. It is possible that your program will need to create its own streams for I/O. If an application needs to create a file for input or output, it must include the FSTREAM.H header file (FSTREAM.H includes IOSTREAM.H). The classes ifstream and ofstream are derived from istream and ostream and inherit the extraction and insertion operations, respectively. The following C++ program demonstrates how to declare a file for reading and writing using ifstream and ofstream, respectively:
//
//  fstrm.cpp
//  A C++ program demonstrating how to declare an
//  ifstream and ofstream for file input and output.
//  Copyright (c) Chris H. Pappas and William H. Murray, 1998
//

#include <fstream.h>

int main(void)
{
 char c;

 ifstream ifsin(“a:\\text.in”,ios::in);
 if( !ifsin )
   cerr << “\nUnable to open ‘text.in’ for input.”;

 ofstream ofsout(“a:\\text.out”,ios::out);
 if( !ofsout )
   cerr << “\nUnable to open ‘text.out’ for output.”;

 while( ofsout && ifsin.get(c) )
   ofsout.put(c);

 ifsin.close( );
 ofsout.close( );

 return(0);
}
The program declares ifsin to be of class ifstream and is associated with the file TEXT.IN stored in the A drive. It is always a good idea for any program dealing with files to verify the existence or creation of the specified file in the designated mode. By using the handle to the file ifsin, a simple if test can be generated to check the condition of the file. A similar process is applied to ofsout, with the exception that the file is derived from the ostream class.
The while loop continues inputting and outputting single characters while the ifsin exists and the character read in is not EOF. The program terminates by closing the two files. Closing an output file can be essential to dumping all internally buffered data.
There may be circumstances when a program will want to delay a file specification or when an application may want to associate several file streams with the same file descriptor. The following code segment demonstrates this concept:
ifstream ifsin;
.
.
.
ifsin.open(“week1.in”);
.
.
.
ifsin.close( );
ifsin.open(“week2.in”);
.
.
.
ifsin.close( );
Whenever an application wishes to modify the way in which a file is opened or used, it can apply a second argument to the file stream constructors. For example:
ofstream ofsout(“week1.out”,ios::app|ios::noreplace);
This statement declares ofsout and attempts to append it to the file named WEEK1.OUT. Because ios::noreplace is specified, the file will not be created if WEEK1.OUT doesn’t already exist. The ios::app parameter appends all writes to an existing file. The following table lists the second argument flags to the file stream constructors that can be logically ORed together:
Mode Bit
Action
ios::in
Opens for reading
ios::out
Opens for writing
ios::ate
Seeks to EOF after file is created
ios::app
All writes added to end of file
ios::trunc
If file already exists, truncates
ios::nocreate
Unsuccessful open if file does not exist
ios::noreplace
Unsuccessful open if file does exist
ios::binary
Opens file in binary mode (default text)
An fstream class object can also be used to open a file for both input and output. For example, the following definition opens the file UPDATE.DAT in both input and append mode:
fstream io(“update.dat”,ios::in|ios::app);
You can reposition all iostream class types by using either the seekg( ) or seekp( ) member function, either of which can move to an absolute address within the file or move a byte offset from a particular position. Both seekg( ) (sets or reads the get pointer’s position) and seekp( ) (sets or reads the put pointer’s position) can take one or two arguments. When used with one parameter, the iostream is repositioned to the specified pointer position. When it is used with two parameters, a relative position is calculated. The following listing highlights these differences, assuming the preceding declaration for io:
streampos current_position = io.tellp( );
io << obj1 << obj2 << obj3;
io.seekp(current_position);
io.seekp(sizeof(MY_OBJ),ios::cur);
io << objnewobj2;
The pointer current_position is first derived from streampos and initialized to the current position of the put_file pointer by the function tellp( ). With this information stored, three objects are written to io. Using seekp( ), the put_file pointer is repositioned to the beginning of the file. The second seekp( ) statement uses the sizeof( ) operator to calculate the number of bytes necessary to move one object’s width into the file. This effectively skips over obj1‘s position, permitting an objnewobj2 to be written.
If a second argument is passed to seekg( ) or seekp( ), it defines the direction to move: ios::beg (from the beginning), ios::cur (from the current position), and ios::end (from the end of the file). For example, this line will move into the get_file pointer file 5 bytes from the current position:
io.seekg(5,ios::cur);
The next line will move the get_file pointer 7 bytes backward from the end of the file:
io.seekg(-7,ios::end);
C++ File Condition States
Associated with every stream is an error state. When an error occurs, bits are set in the state according to the general category of the error. By convention, inserters ignore attempts to insert things into an ostream with error bits set, and such attempts do not change the stream’s state. The iostream library object contains a set of predefined condition flags, which monitor the ongoing state of the stream. The following table lists the six member functions that can be invoked:
Member
Function

Action
eof( )
Returns a nonzero value on end-of-file
fail( )
Returns a nonzero value if an operation failed
bad( )
Returns a nonzero value if an error occurred
good( )
Returns a nonzero value if no state bits are set
rdstate( )
Returns the current stream state
clear( )
Sets the stream state (int=0)
You can use these member functions in various algorithms to solve unique I/O conditions and to make the code more readable:
ifstream pfsinfile(“sample.dat”,ios::in);
if(pfsinfile.eof( ))
 pfsinfile.clear( ); // sets the state of pfsinfile to 0

if(pfsinfile.fail( ))
 cerr << “>>> sample.dat creation error <<<”;

if(pfsinfile.good( ))
 cin >> my_object;

if(!pfsinfile) // shortcut
 cout << “>>> sample.dat creation error <<<”;
This chapter has served as an introduction to C++ I/O concepts. To really understand various formatting capabilities, you’ll need to learn about C++ classes and various overloading techniques. Chapters 16 and 17 teach object-oriented programming concepts. With this information, you’ll be introduced to additional C++ I/O techniques in Chapter 18.

Books24x7.com, Inc 2000 –  


Visual C++ 6(c) The Complete Reference
Visual Studio 6: The Complete Reference
ISBN: B00007FYGA
EAN: N/A
Year: 1998
Pages: 207

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net