|
course.wilkes.edu/CS125Labs |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Lab 8: File I/O
IntroductionThroughout this manual, we have made extensive use of files -- containers on a hard or floppy disk that can be used to store information for long periods of time. For example, each source program that we have written has been stored in a file, and each binary executable program has also been stored in a file. While they may seem to be the same, files differ from programs in that a program is a sequence of instructions, and a file is a container in which a program (among other things) can be stored. A different use for files is to store data. That is, where each of our exercises have thus far read data from the keyboard and written data to the screen, an alternative approach is to store the data to be read in a file, and read the data from there. Similarly, instead of writing data to the screen, there are situations where it is useful to instead have a program write its data to a file. This approach is particular useful for problems where the amount of data to be processed is so large that entering the data each time the program is executed (interactively) becomes inconvenient. That inconvenience can be eliminated by storing the data in a file, and then having the program read from the file, instead of the keyboard. Today's exercise is to learn how to do so. When many of us were younger, we enjoyed writing secret messages, in which messages were encoded in such a way as to prevent others from reading them, unless they were in possession of a secret that enabled them to decode the message. Coded messages of this sort have a long history. For example, the Caesar cipher is a simple means of encoding messages that dates from Roman times. To illustrate, the Caesar cipher produces the encoded sentence: Rqh li eb odqg, wzr li eb vhd.when applied to the historic phrase: One if by land, two if by sea.What is the relationship between the letters in the original sentence and those in the encoded sentence? Today's exercise is to use the Caesar cipher to encode and decode messages stored in files.
Getting StartedCreate a new directory for the files of this exercise, and in it, save copies of the files encode.cpp, decode.cpp, message.text, and alice.code. Then personalize the documentation in the file encode.cpp, and take a few moments to study its contents.
An Encoding ProgramThe first part of today's exercise is to write a program that can be used to encode a message that is stored in a file. To demonstrate both input from and output to a file, we will store the encoded message in a second file.
DesignAs usual, we will apply object-centered design to solve this problem. Behavior. Our program should display a greeting and then prompt for and read the name of the input file. It should then connect an input stream to that file so that we can read from it, and check that the stream opened correctly. It should then prompt for and read the name of the output file. It should then connect an output stream to that file so that we can write to it, and check that the stream opened correctly. For each character in the input file, our program should read the character, encode it using the Caesar cipher, and output the encoded character to the output file. Our program should conclude by disconnecting the streams from the files. Objects. Using this behavioral description, we can identify the following objects:
Using this list of objects, we might specify the behavior of our program as follows: Input(inFile), a sequence of unencoded characters. Output(outFile), a sequence of encoded characters. Operations. From our behavioral description, we have these operations:
Algorithm. We can organize these operations into the following algorithm: 0. Display a greeting. 1. Prompt for and read inFile, the name of the input file. 2. Create an
Coding
Opening a Connection to a File. An executing program is unable to interact directly with a file for a very simple reason: an executing program resides in main memory and a file resides on a secondary memory device, such as a hard disk. However, an executing program can interact indirectly with a file, by opening a connection between the program and that file. In C++, such connections are called fstream objects.
Like any other object, an fstream must be declared before it can be used.
If ifstream inFStreamName(inputFileName.data());constructs an object named inFStreamName as a connection
to that file.
(The string function member data() extracts the
actual characters from a string.
If your compiler is not fully ANSI compliant, you may have to use
the c_str() function member instead.)
An ifstream thus provides a connection to a file,
by which a program can read values from the file.
Using this information, implement step 2 of our algorithm
in
To perform step 5 of our algorithm, we must open an fstream for output
to the output file.
Such objects are of type ofstream outFStreamName(outputFileName.data());Such a declaration constructs an object named outFStreamName
as a connection to the file whose name is stored in
outputFileName.
If the file does not exist, then a file by that name is created in
the working directory.
If the file does exist, then its contents are erased
(unless ios::append is passed as a second argument,
in which case any values written to the file are appended to its end).
An ofstream thus provides a connection to a file,
by which a program can write values to a file.
Using this information, implement step 5 of our algorithm
by declaring an Checking that a Connection Opened Correctly. Opening files is an operation that is highly susceptible to user errors. For example, suppose the user has accidentally deleted the input file and our program tries to open a connection to it? If an fstream opens as expected, the operation is said to succeed, but if it does not open as expected, the operation is said to fail.
To detect the success of an open operation, fstream objects contain
an FStreamName.is_open()which returns true (1), if FStreamName is open,
and returns false (0), otherwise.
When combined with an assert(), the is_open() function
member provides a readable way to perform steps 3 and 6 of our algorithm.
In encode.cpp,
use this information to complete step 3 of our algorithm:
assert ( /* inStream opened successfully */ );
and also step 6 of our algorithm:
assert ( /* outStream opened successfully */);
by replacing the comments with appropriate calls to
the is_open() function member.
Then test what you have written by using the compiler to translate
encode.cpp.
Unless you're ahead of the game, the compiler should generate an error message.
What is it?
The reason for this error message is that When your source program compiles correctly, do not execute your source program yet (or an infinite loop will occur.) Instead, continue to the next part of the exercise.
Input from an fstream.
Just as we have used the inputFStreamName >> VariableNamethus serves to read values from an ifstream named
inputFStreamName
(and thus from the file to which inputFStreamName
is a connection) into the variable VariableName.
Of course, the type of the value being read must match the type of
VariableName, or the operation will fail.
While the input operator is the appropriate operator to solve many
problems involving file input, it is not the appropriate operator
for our problem. The reason is that like its interactive counter-part,
the One if by land. Two if by sea.and we were to use the >> operator (in a loop) to read each
of these characters:
inStream >> inChar;then all white space characters (blanks, tabs and newlines) would be skipped, so that only non- white space characters would be processed and output. Our encoded version message would then appear without white space (i.e., missing blanks and newlines) as follows: Rqhliebodqg.wzrliebvhd.To avoid this problem, ifstream objects (like istream
objects) contain a get() member function:
inputFStreamName.get( CharacterVariable );When execution reaches such a statement, the next character is read from inputFStreamName and stored in
CharacterVariable, even if it is a white space character.
The get() member function of inStream can thus be used
to perform step 7a of our algorithm.
In encode.cpp, place a call to get() in the appropriate
place to read a character from inStream into inChar.
Then compile your program, and continue when what you have written
is syntactically correct.
Controlling a File-Input Loop. Files are created by a computer's operating system. When the operating system creates a file, it marks the end of the file with a special end-of-file mark. Input operations are then implemented in such a way as to prevent them from reading beyond the end-of-file mark, since doing so could allow a programmer unauthorized access to the files of another programmer.
This end-of-file mark can be used to control a loop that is reading
data from the file.
Just as an inputFStreamName.eof()When execution reaches this expression, the eof() function
returns true (1) if the last read from inputFStreamName
tried to read the end-of-file mark, and returns false(0) otherwise.
In a forever loop like the one in the source program,
the if ( /* end-of-file has been reached */ ) break;following the input step, repetition will be terminated when all of the data in the file has been processed.
In your source program, place an if-break combination in the appropriate
place to perform step 7b of our algorithm, using the
File Output.
Just as we have used the outputFStreamName << Value ;where outputFStreamName is an ofstream,
and Value is a character or constant we wish to
store in the file to which outputFStreamName is a connection.
Use either of these statements to write the encoded character to
your output file via
Closing Files.
Once we are done using an fstream to read from or write to a file,
we should close it, to break the connection between our program
and the file.
This is accomplished using the fstream function member FStreamName.close();When execution reaches this statement, the program severs its connection to FStreamName.
In the appropriate place in the source program, place calls to
Then translate your source program, and ensure that it is free of syntax errors.
Testing and Debugging
When your program's syntax is correct,
test it using the provided file named Rqh Li Eb Odqg Wzr Li Eb VhdIf this file is not produced, then your program contains a logical error. Retrace your steps, comparing the statements in your source program to those described in the preceding parts of the exercise, until you find your error. Correct it, retranslate your source program and then re-test your program, until it performs correctly.
Applying What We Have Learned
The last part of today's exercise is for you to apply what you have learned
to the problem of decoding a file encoded using the Caesar cipher.
Complete the skeleton program
decode.cpp,
that can be used to decode a message encoded using the Caesar cipher.
Do all that is necessary to get this program operational,
so that messages encoded with
To test your program,
you can use the output file created by
Phrases you should now understand:
File,
Submit:
Hard copies of your final versions of
|
Home Membership
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Last update: Wednesday, August 23, 2000 at 4:15:21 PM. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||