Writing with Output Streams - Reading and Writing Files - C++ All-in-One For Dummies (2009)

C++ All-in-One For Dummies (2009)

Book V

Reading and Writing Files

Chapter 2: Writing with Output Streams

In This Chapter

Using the insertion operator

Working with manipulators

Formatting your output

Using flags to format your output

Specifying a precision for writing numbers

Setting field widths

Years ago, one of us had an old computer that had 3000 bytes of memory. (Yes, that’s three thousand bytes, not 3MB.) As an option, this computer came with a floppy disk drive that sat outside it. It did not have a hard drive. Therefore, if you didn’t have a disk drive but you wanted to use a program, you had to type it! Ah, those were the days.

Nowadays, the notion of a computer without a hard drive seems almost unthinkable. Not only do your programs sit on the hard drive in the form of files, but your programs also create files to store on the hard drive.

When you use a word processor, you save your documents to a file. Imagine if every time you needed the same document, you had to retype it. In this chapter, we show you the different ways you can write to a file.

Inserting with the << Operator

Writing to a file is easy in C++. You’re probably already familiar with how you can write to the console by using the cout object, like this:

cout << “Hey, I’m on TV!” << endl;

Operating the insertion operator

The insertion operator, <<, is an overloaded operator function. For the 100 percent ANSI-compliant libraries, inside the basic_ostream class (or, for the non-100 percent ANSI libraries, inside the ostream class), you can find several overloaded forms of the << operator function. Each one provides input for a basic type as well as for some of the standard C++ classes, such as string or one of its base classes. (We say or because most libraries that ship with compilers are written by compiler vendors — who may implement their code slightly differently but get the same results.)

Well, guess what! The cout object is a file stream! Amazing! And so to write to a file, you can do it the same way you would with cout: You just use the double-less-than symbol, called the insertion operator, like this: <<.

If you open a file for writing by using the ofstream class, you can write to it by using the insertion operator, as in Listing 2-1.

Listing 2-1: Using Code to Open a File and Write to It

#include <iostream>

#include <fstream>

using namespace std;

int main()

{

ofstream outfile(“outfile.txt”);

outfile << “Lookit me! I’m in a file!” << endl;

int x = 200;

outfile << x << endl;

outfile.close();

return 0;

}}

The first line inside the main creates an instance of ofstream, passing to it the name of a file called outfile.txt.

We then write to the file, first giving it the string, Lookit me! I’m in a file!, then a newline, then the integer 200, and finally a newline. And after that, we show the world what good programmers we are by closing our file.

Placing data in specific folders

Sometimes you want to place data in a specific common folder, such as the current working directory — the directory used by the application. C++ provides a method to obtain this information: getcwd(). This method appears in the <direct.h> header. Using the getcwd() method is relatively straightforward. You create a place to put the information, called a buffer, and then ask C++ to provide the information as shown here:

#include <iostream>

#include <direct.h>

using namespace std;

int main()

{

char CurrentPath[_MAX_PATH];

getcwd(CurrentPath, _MAX_PATH);

cout << CurrentPath << endl;

return 0;

}

The _MAX_PATH constant is the maximum size that you can make a path. So, what this code is saying is to create a char array that is the size of _MAX_PATH. Use the resulting buffer to hold the current working directory (which is where the name of the method getcwd() comes from). You can then display this directory on screen or use it as part of the path for your output stream — amazing!

Formatting Your Output

If you’re like us and are saving lists of numbers to a file, you may find that the process works better if the numbers are formatted in various ways. For example, you may want them all aligned on the right; or you might want your floating-point numbers to have a certain number of digits to the right of the decimal point.

There are three aspects to setting these formats. They are

Format flags: A format flag is a general style that you want your output to appear in. For example, you may want floating-point numbers to appear in scientific mode, or you may want to be able to print the words true and false for Boolean values, rather than their underlying numbers. To do these tasks, you specify format flags.

Precision: This refers to how many digits are on the right of the decimal point when you print floating-point numbers.

Field width: This refers to how much space the numbers take (both floating point and integer). This feature allows you to align all your numbers.

The next three sections discuss each of these in all their glory and grandeur.

image You can use the format flags (detailed in the upcoming section), as well as precision and width specifiers, when writing to your files — and also when writing to cout. Because cout is a stream object in the iostream hierarchy, it accepts the same specifiers as output files. So have at it!

Formatting with flags

The folks who made the ISO C++ standard gave us a slew of format flags.

image If you’re using a compiler that includes a library that’s not fully ANSI-compliant, you will be able to use most of these format flags but not all of them. Therefore, in this section, we’re giving you lists for

♦ ios_base (for ANSI-compliant libraries)

♦ ios (for non-compliant libraries)

To use the following format flags, you call the setf member function for the file object. (This can be either your own file object or the cout object.) For example, to turn on scientific notation, you would do this:

cout.setf(ios_base::scientific);

cout << 987654.321 << endl;

or, if you’re using a non-ANSI-compliant library:

cout.setf(ios::scientific);

cout << 987654.321 << endl;

To turn off scientific mode, you call the unsetf member function:

cout.unsetf(ios_base::scientific);

cout << 987654.321 << endl;

or, if you’re using a non-ANSI library:

cout.unsetf(ios::scientific);

cout << 987654.321 << endl;

And if you’re using your own file, you would do something like this:

ofstream myfile(“numbers.txt”);

myfile.setf(ios_base::scientific);

myfile << 154272.524 << endl;

myfile.close();

or, for the non-ANSI folks:

ofstream myfile(“numbers.txt”);

myfile.setf(ios::scientific);

myfile << 154272.524 << endl;

myfile.close();

When you run this code for writing to a file, the numbers.txt file will contain one of the following examples, depending on your particular compiler and library:

1.542725e+005

1.542725e+05

imageEach of the ios_base flags exists both as a format specifier and as a manipulator. (Don’t worry about the precise differences for right now; Book V, Chapter 5 explains manipulators in detail.) Therefore, you can, for example, use either of the following lines to set boolalpha:

cout.setf(ios_base::boolalpha);

cout << boolalpha;

image We’re talking only about the ios_base flags here. The ios flags do not coexist as manipulators. You can use these flags as manipulators only with an ANSI-compliant library.

image If you use the manipulator form of a format specifier, don’t put an endl at the end of the line unless you want an endline to print, as in this:

cout << boolalpha << endl;

Following is a rundown of the format flags available in both the ANSI-compliant and non-ANSI compliant libraries. To set these, you call setf. To turn off the flag, you either call unsetf, or for some flags, you set a different flag. For example, both of these turn off scientific mode:

cout.unsetf(ios_base::scientific);

cout.setf(ios_base::fixed);

In the following list, we point out when you can set another flag to turn off a particular flag. One of these, boolalpha, is available only to you ANSI-style folks. Remember, “ANSI-people” must use ios_base:: before each of these, while “non-ANSI-people” must use ios::before each of these.

♦ boolalpha: (ANSI only) Setting this flag causes Boolean variables to write with the words true or false (or the equivalent words for your particular locale). Clearing this flag causes Boolean variables to write 0 for false or 1 for true. (The default is for this flag to be cleared.)

♦ fixed: This flag specifies that, when possible, the output of floating-point numbers will not appear in scientific notation. (We say when possible because large numbers always appear as scientific notation, whether you specified scientific or fixed.)

♦ scientific: When you specify this flag, your floating-point numbers always appear in scientific notation.

♦ dec: When you set this flag, your integers will appear as decimal numbers. To turn this off, you turn on a different base, either hex (for hexadecimal) or oct (for octal).

♦ hex: With this flag, all your integers appear in hexadecimal format. To turn this on, choose a different base — dec or oct. Computer people like hexadecimal because it looks cool to see letters in your numbers.

♦ oct: When you turn on this flag, your integers will appear in octal format. Oh fun, fun.

♦ left: When you turn on this flag, all numbers will be left-aligned with a width field. (See “Setting the width and creating fields,” later in this chapter, for information on how to set the width.)

♦ right: With this flag, all your numbers will be right-aligned with a width field.

♦ showbase: When you turn on this flag and print an integer, the integer will be preceded with a super-special character that represents none other than the base — decimal, hexadecimal, or octal. That can be good because the number 153 can represent 153 in decimal or 153 in hexadecimal (which is equivalent to 339 in decimal) or 153 in octal (which is equivalent to 107 in decimal). Yikes.

♦ showpoint: With this flag, your floating-point numbers have a decimal point, even if they happen to be whole numbers. (That is, a floating-point variable that contains 10.0 will print as 10. with a decimal point after it. Without this flag, it will just print as 10 with no decimal point.)

♦ showpos: Normally, negative numbers get a minus sign before them, and positive numbers get no sign before them. But when you turn on this flag, your positive numbers will get a plus sign before them. Cool!

♦ unitbuf: This is for the advanced people. When you turn this on, your output will flush after each output operation. In other words, the library will not accumulate a certain amount of output before writing it in batches. Instead, the library will write the output all out each time you use the insertion operator, <<.

♦ uppercase: When you write hexadecimal or scientific numbers, the various letters in the number will appear as uppercase. Thus, the letters A, B, C, D, E, and F will appear in capitals in a hexadecimal number, and the E representing the exponent in scientific notation will print as a capital E. When this is not set, you will get a, b, c, d, e, and f for hexadecimal numbers and e for the exponent in scientific notation.

Table 2-1 shows the manipulator forms of some flags. We’re providing three columns in this table: First is the flag; then the manipulator to turn on the flag. Then comes the manipulator to turn off the flag. Yes, you need a way to turn it off. (We coined a new word: demanipulator. Yeah, that sounds good.) Remember, if you’re pre-ANSI, you don’t have access to these manipulators. Instead, you have to call setf.

Table 2-1 Using ANSI-Standard Manipulators and Demanipulators

Flag

Manipulator

Demanipulator

boolalpha

boolalpha

noboolalpha

showbase

showbase

noshowbase

showpoint

showpoint

noshowpoint

showpos

showpos

noshowpos

skipws

skipws

noskipws

uppercase

uppercase

nouppercase

fixed

fixed

scientific

scientific

scientific

fixed

The scientific flag and fixed flag are opposites: fixed turns off scientific, and scientific turns off fixed. The default if you don’t specify either is fixed.

Six manipulators aren’t in Table 2-1 because they don’t have a demanipulator. Instead, they are three-way:

Bases: dec, hex, and oct. Only one base can be active at a time. Activating a base automatically switches off the other bases.

Alignments: internal, left, and right. Only one alignment can be active at a time. Activating an alignment automatically switches off the other alignments.

Specifying a precision

When you are writing floating-point numbers to a file or to cout (that is, numbers stored in float or double variables), having all the numbers print with the same number of digits to the right of the decimal point is often handy. This feature is called the precision.

image Do not confuse this form of the word precision with the idea that double variables have a greater precision than float variables. Here, we’re just talking about the number of digits printed to either the file or cout. The value inside the variable does not change, nor does the precision of the variable’s type.

To set or read the precision, call the stream’s precision function. If you call precision with no parameters, you can find out the current precision. Or to set the precision, pass a number specifying how many digits you want to appear to the right of the decimal point.

For example, the following line sets the precision of an output:

cout.precision(4);

The output would take this form:

0.3333

image If you don’t set the precision, the stream will have a default precision, probably six, depending on your particular compiler.

Precision has an interesting effect if you use it with the showpoint format flag. In the scientific community, these three numbers have the same precision:

3.5672

8432.2259

0.55292

Even though the first two of the preceding numbers have the same number of digits to the right of the decimal point, scientists consider precision to mean the same number of total digits not counting leftmost 0s to the left of the decimal (as in the final of the three). Therefore, a scientist would consider the three following numbers to have the same precision because they all have four digits. (Again, for the final one, you don’t count the 0 because it’s to the left of the decimal point.) Scientific folks call these significant digits. You can accomplish significant digits with an output stream by combining precision with the showpoint flag. Listing 2-2 shows an example of showpoint and precision working together in perfect harmony.

3.567

8432.

0.1853

Listing 2-2: Using the Precision Function to Work with the showpoint Format Flag

#include <iostream>

using namespace std;

int main()

{

int i;

cout.setf(ios_base::showpoint);

cout.precision(4);

for (i=1; i<=10; i++) {

cout << 1.0 / i << endl;

}

cout << 2.0 << endl;

cout << 12.0 << endl;

cout << 12.5 << endl;

cout << 123.5 << endl;

cout << 1234.9 << endl;

cout << 12348.8 << endl;

cout << 123411.5 << endl;

cout << 1234111.5 << endl;

return 0;

}}

image If you’re using a non-ANSI-compliant compiler, you need to change ios_base to ios in the third line in main. Also, because Listing 2-2 is for a fully-ANSI-compliant compiler, we included the using namespace std; line. (You can always have this line, whether your compiler requires it or not.)

When you run this program, here’s the output you see:

1.000

0.5000

0.3333

0.2500

0.2000

0.1667

0.1429

0.1250

0.1111

0.1000

2.000

12.00

12.50

123.5

1235.

1.235e+004

1.234e+005

1.234e+006

The preceding output has a couple of interesting cases:

♦ The last three lines of the preceding output are scientific notation to maintain four significant digits.

♦ The fourth line from the end, 1235., is rounded up from 1234.9 because of this line:

cout << 1234.9 << endl;

The precision function has an associated manipulator. Instead of calling precision as a function, you can use it as a manipulator. But the manipulator’s name is slightly different: It’s setprecision. To use it, you include this header:

#include <iomanip>

These two lines cause the same thing to happen:

cout.precision(4);

cout << setprecision(4);

And these two lines are available for all the recent more-or-less-ANSI-compliant compilers, even those that aren’t fully compliant! Yay! Just make sure you remember to #include <iomanip>, or you will get a compiler error.

Setting the width and creating fields

This is where you can start making the numbers and data all nice and neat by aligning them in columns. To align your data, use the width member function for the stream or cout, passing the width of the field, like this:

cout.width(10);

Then, when you print a number, think of the number as sitting inside a field 10 spaces wide, with the number wedged against the right side of these 10 spaces. For example, look at this:

cout.width(10);

cout << 20 << endl;

This code produces this output:

20

Although seeing this in the printed text is hard, this 20 is pushed to the right of a field of spaces 10 characters wide. That is, because the 20 takes 2 character spaces, there are 8 spaces to the left of it.

imageIf you prefer, you can have the numbers pushed to the left of the field. To do this, set the left format flag by using setf. (Or, for absolutely perfectly ANSI-compliant libraries, you can use the left manipulator.)

For the width function, you can alternatively #include <iomanip> and then use a manipulator:

cout << setw(10);

This works for both the newer 100 percent ANSI-compliant compilers and the slightly older, slightly less compliant compilers.

image Due to some oddities in the libraries, when you set the width, it stays that way only for the next output operation. Call it forgetful if you will. Therefore, suppose you have code that looks like this:

cout.width(10);

cout << 20 << 30 << endl;

Only the first output, 20, will have a field width of 20. The 30 will just take as much space as it needs. Therefore, these lines of code produce this output, which is probably not what most people would intend:

2030

This is why we prefer to use the manipulator form: You precede each output item with a width specification. Try this instead:

cout << setw(10) << 20 << setw(10) << 30 << endl;

which writes this to cout:

20 30

That looks a little nicer!

Listing 2-3 shows the great things you can do when you set the width. This listing is for the absolute money-back-guarantee ANSI compilers. If yours is slightly less than ANSI compliant, you have to change these two lines:

sals << fixed;

sals << left;

to this:

sals.setf(ios::fixed);

sals.setf(ios::left);

Listing 2-3: Setting the Width of a Field Using the setw Manipulator or Width Function

#include <iostream>

#include <iomanip>

#include <fstream>

using namespace std;

int main()

{

ofstream sals(“salaries.txt”);

sals << setprecision(2);

sals << fixed;

sals << left;

sals << setw(20) << “Name” << setw(10) << “Salary”;

sals << endl;

sals << “------------------- “; // 19 hyphens, one space

sals << “----------” << endl; // 10 hyphens

sals << setw(20) << “Hank Williams”;

sals << setw(10) << 28422.82 << endl;

sals << setw(20) << “Buddy Holly”;

sals << setw(10) << 39292.22 << endl;

sals << setw(20) << “Otis Redding”;

sals << setw(10) << 43838.55 << endl;

sals.close();

return 0;

}}

When you run Listing 2-3, you get a file called salaries.txt, like this:

Name Salary

------------------- ----------

Hank Williams 28422.82

Buddy Holly 39292.22

Otis Redding 43838.55

See how it’s neatly lined up? That’s pretty! Notice one thing we did, however: The first field, Name, is 20 characters wide. For the hyphens, we put only 19 to give the appearance of a space between the two fields. In fact, the two fields are wedged against each other with no space between them.

imageIf you ran Listing 2-3 and each salary printed in a scientific format as in 2.8e+04, you need to use sals.setf(ios::fixed); and sals.setf(ios::left);.

imageWe used the left format flag so that the data in each field is aligned to the left end of the field. By default, each field is aligned to the right.

image Although you can specify the field width, really you’re specifying a minimum. If the characters in the output are less than the field width, the runtime library will pad them with spaces to make them that minimum size. If they are bigger than that width, the library will not chop them off to make them fit. If you add letters to the Hank Williams line in Listing 2-3 (like this: sals << setw(20) << “Hank WilliamsABCDEFGHIJ”;), the output looks like the following example instead. The Hank Williams line runs beyond the 20 characters into the next field.

Name Salary

------------------- ----------

Hank WilliamsABCDEFGHIJ28422.82

Buddy Holly 39292.22

Otis Redding 43838.55