C++ 13 Review Questions

gina123
input_function.pdf

INPUT AND OUTPUT PROCESSING

Please note that the material on this website is not intended to be exhaustive. This is intended as a summary and supplementary material to required textbook.

INTRODUCTION

Most computer programs spend very little processing time in obtaining input and producing output. Most of the work goes into the processing in between. The exception to this generalization is non- browser-based graphical user interfaces (GUIs). In these programs, often over 50% of the code can be dedicated to user input and output. In browser-based user interfaces, the browser does a lot of the work for you.

Input/output (I/O) comes in two flavors: interactive and non-interactive. Interactive I/O involves prompting the user for the input and displaying the output on the user's monitor. Non-interactive I/O involves reading and writing files, which for us will be text files.

INTERACTIVE I/O

The majority of interactive I/O is accomplished in C++ using cin and cout. For now all interactive I/O comes from and goes to the console window. Recall that in order to use cin and cout we have to #include <iostream>.

The cin input stream views the input the user types as if the data came from a text file. But, cin ignores as data all whitespace characters: the newline, the tab, and the space. As a result cin does some input formatting for the program, and only assigns non-whitespace data to the input variables.

If the user is entering several data items on one line (in the console window), cin will wait for the user to press the newline (Enter or Return) key before it will process the line of input data.

The input stream can consist of any array of characters, representing: integers, decimals, strings, single characters, .... Each array of characters is delimited by whitespace characters, and cin will interpret any of the whitespace characters as a delimiter (with some exceptions, of course).

When we invoke cin we always have to specify a variable in which to place the user input; that variable must have a type. And, cin is very persnickety about types. If cin is expecting an integer, it will not read an alphabetic character; it will generate an error condition. In the following code snippet cin will expect to see a single character, an integer, a decimal, and a string, in that order.

char yOrN; int userID; double hourlyWage; string lastName; ... cin >> yOrN >> userID >> hourlyWage >> lastName;

If the user typed the last name for the userID, cin will flag an error and not continue to read the input stream until your program deals with that error (see below).

The cout output stream has a lot more variety to it; cout is aware of the types of variables passed to it, and will format them accordingly, unless your program specifies another format to use. When thinking about the arrangement of your program's output, consider each string of output characters as a field, and the contents of each line of output as an array of fields. The following output has 4 fields in the one-line array of fields:

Y 1648942 25.00 Smith

Every field in the array has a width. In the example above you cannot easily tell by looking exactly what the width of each field is as each field has a tab character in front of it (which you cannot see in this document or on your monitor).

Some output will not have such an orderly appearance; you might get a two-line output that looks like (still using a tab in front of each field):

Y 1648942 25.00 Smith N 179240578352 35.375 Jones

As far as columnar output is concerned we have some generally accepted principles:

All columnar output fields should be "lined up" in their own columns, and each column should be wide enough in width to hold the longest field in that column. Non-numeric fields, whether strings of characters or single characters, should be arranged left- justified in their column – that is, so the first character is left-most within the field. Numeric fields, whether integers or decimals, should be arranged right-justified in their column – that is, the last character is right-most within the field. Furthermore, when decimals are used, principle 3 should be violated so every decimal in the column has the decimal point vertically in line.

You can probably think of a lot of cases where you might want to violate one or more of these principles, but before you do be aware of why they are principles.

Y 1648942 25.00 Smith N 179240578352 35.375 Jones N 15469 30.00 Rimsky-Korsakov

Users will expect to see non-numeric string data left-justified in their columns – it makes it easier to scan the column. User will also expect to see numeric integers right-justified in their columns – it makes it easier to tell the difference between 1000000 and 10000000 when scanning. And, users (especially accountants) will want to see all the decimal points lined up vertically for the same reason. Most spreadsheets assiduously follow these principles unless you tell them to do otherwise.

Notice that the second column above violates the 3rd principle, and it does so with good reason. The ID number is not really an integer – we do not expect to be doing arithmetic operations on the ID number. The ID number really should be declared as a string and treated just like any other alpha string. Consider U.S. zip codes – do you ever expect to add or subtract these numbers?

As programmers, then, we need to know the characteristics and intended uses of our output data. And, we need to format that output so that it is orderly, if not aesthetically, pleasing. Programmers are usually not good judges of either characteristic, and the users who will utilize a program's output should always

be consulted about such mundane matters as format.

FORMATTING OUTPUT

Formatting output can be daunting at first, and we will simplify it considerably here (but see iostream). In order to use the formatting functions you have to: #include <iomanip>.

The two most important formatting routines will be setw() and setprecision(). In addition, we will show you how to display decimal numbers in either scientific or fixed-point notation.

The setw() function is used to set a field width. The function is used directly in the cout line:

cout << setw(10) << numb1 << setw(10) << numb2 << endl;

The setw() function requires an integer parameter (in this case 10), and its effect is to specify the field width for each of the two numbers following in the cout statement.

If you wanted the second number in a different field width you need to use something like:

cout << setw(10) << numb1 << setw(5) << numb2 << endl;

If you try to output a string of characters that is too wide for the field width setting, C++ will not truncate the output; it will overfill the field, which will throw off all the rest of the columnar output. Lastly, (and this a real gotcha!) by default C++ will NOT left-justify any output. The default setting is to always right-justify output. You can set the justification on a case-by-case basis with:

cout << setiosflags(ios::left) << ...; cout << setiosflags(ios::right) << ...;

Whenever you insert a setiosgflags() call in a cout statement, it will apply to ALL subsequent cout statements, unless you explicitly change the flag setting.

You can set the number of decimal places to be represented in the output with setprecision() which takes an integer parameter:

cout << setprecision(2) < ...;

Bear in mind that setprecision() will NOT round off; it will only truncate. If you want to round money to the nearest cent use:

float money; ... cout << setprecision(2) << money + 0.005;

Also bear in mind that once you use setprecision() this setting will apply to all subsequent calls to cout, unless you explicitly change the precision setting.

You can choose to display decimals in either fixed-point or scientific notation. The default is fixed-point.

Use the following to toggle between the two formats:

cout << setiosflags(ios::fixed); cout << setiosflags(ios::scientific);

Once you set a format, it stays in effect until you explicitly change the setting.

MISCELLANEOUS

The getline() function (you must #include <string>) is particularly useful for reading a whole line or the first parts of lines. It has two forms:

getline(infile, dest_string); getline(infile, dest_string, delim_char);

The first form reads the entire line (up to the newline) from the input stream (infile) into the dest_string (not including the newline); dest_string must be of type string. This form is useful for reading whole lines at a time. The second form reads the line up the delimiter character passed (for instance, ',') into the dest_string (not including the delimiter character). In both cases the next read will continue with the character after the newline or delimiter.

You may have noticed that cin reads the input stream and skips over whitespace characters. If you want to read the input stream character by character (one character at a time), including the white space characters, you use the istream member function get():

cin.get(char_variable);

Unlike cin and getline(), the get() member function does not strip any characters from the input stream; instead it delivers all characters from the input stream including whitespace characters.

HANDLING I/O ERRORS

Once an input stream encounters an error, it sets an error flag, and it will refuse to read anything more, until the error flag is cleared. When using cin in situations where the user may mistype (which is really all situations), you would need to check the error flag, inform the user of their error, clear the error flag, and re-prompt the user.

We will not cover input error handling in detail yet. Just be aware that your program may just "hang" because of an I/O error. For now, we will program as if the user never makes a mistake typing.

NON-INTERACTIVE I/O

Non-interactive I/O involves reading and writing text files. In order to read and write files we have to be able to open files for reading and/or writing. When we are done we have to close the file. In order to use

the file libraries that contain the file manipulation functions you will have to #include <fstream> and also often <string>. The four operations on files are, then:

Open (a file for reading or for writing) Read (an open file) Write (an open file) Close (an open file)

Opening a file is straightforward, if you know the name of the file to open, and the file exists in your application program's current working directory, that is, the same directory or folder in which the application program itself is stored. In this case you just use the name of the file: "filename.txt".

If the text file is stored in another directory, then you will have to know the full (or relative) pathname of the file. This can get tricky for Windows-based systems, as the pathnames will have a backslash character (\) in them, which the C++ compiler uses as an escape character. In order to have \ characters in the pathname string you have to escape the escape character (with \\). Hence, the string naming a file: "C:\directory1\directory2\...\filename.txt" will often have to be named as: "C:\\directory1\\directory2\\...\\filename.txt."

In non-Windows OSs pathnames use the slash character (/), and that causes no problems for the C++ compiler. (Note: UNIX OSs have problems with file and directory names that contain spaces – ASCII hex 20 or decimal 32 – in them; a good naming practice to follow is to NEVER use spaces in file or directory names; if you ever move your programming working environment to a UNIX system, this practice will save you a lot of headaches.)

A further complication is caused by C++ and the C++ compiler. We are used to strings, and it is a string that we use to name files. However, the file open function requires that you use C-language string (which is a NULL-terminated array of characters). The workaround is fairly straightforward. In order to open a file named by a string variable, we will have to first convert it to a C-based character array (using the c_str() member function; see below).

Any input file stream will have the type: ifstream (used for reading) and any output file stream will have the type: ofstream (used for writing). We declare these streams as follows:

#include <fstream> ... ifstream input_stream_name; ofstream output_stream_name;

Most often we will do the declaration and the open in one line, as in:

ifstream is("filename.txt", ios::in); ofstream os("filename.txt", ios::out)

Once we have an open file we use the stream variable (either is or os in the example immediately above) just like we already use cin and cout:

is >> variable_name; os << "And the answer is: " << answer << endl;

However, if we name the file by means of a C++ string variable, we have to use the workaround

mentioned above. The workaround uses the string member function c_str(). For instance, your programs will often want to prompt the user for the filename and read the name into a string variable. In order to do that and open the file successfully you have to use something like:

#include <fstream> #include <string> ... string filename; ... // prompt user for input file name cout << "Enter the name of the input file: " cin >> filename; ifstream is(filename.c_str(), ios::in);

As users may mistype the name of the string, you have to be prepared for errors on open with something like:

if (is.fail()) { cout << "Cannot open " << filename << endl; // either re-prompt the user or exit your program ... }

Last, but not least, when you are done reading or writing a file you should close the file. In any case, you should close the file before exiting, with:

is.close(); os.close();

Nothing bad will happen if you do not close a file before exiting, as the OS will take care of that for you. Experienced programmers consider programmers who fail to close open files to be sloppy programmers.

REVIEW EXERCISES

1. * Write a program that opens a non-existent file for writing and output some text into the file. Did the underlying OS create a file for your text?

2. ** Write a program that prompts the user for an integer, then prompts the user for a string, then prompts the user for a decimal. Your program should then print out what the user entered. Experiment with various error input scenarios. Try entering 5o1 for the integer (instead of 501). Try entering two separate words for the string (instead of a single word. Try entering 10% for the decimal (instead of .10). Take note of your results!

3. *** Write a program that outputs the following tabular data correctly aligned. Note: you have to reset the I/O flags BEFORE you set the alignment right or left with: cout << resetiosflags(ios::adjustfield);

ID First Name Last Name Balance 1 Mary Worth 100.00 2 John Kildare 15.10 3 Harvey Jones 65.27 4 Wilbur Murphy 1145.43 5 Sandra Dee 0.00

6 Amy Santucci 231.55 7 Melissa Cox 2.01 8 Morgan Freeman 1789.03 9 Jack Nicholson 123.67 10 John Brown 426.87

© 2011 cad18; rcm27; cpsm