䊐 MotivationAfter you have completed file manipulation, the file should always be closed for the fol-lowing reasons: ■ data may be lost, if for some reason the program is not terminated
Trang 1䊐 Motivation
After you have completed file manipulation, the file should always be closed for the fol-lowing reasons:
■ data may be lost, if for some reason the program is not terminated correctly
■ there is a limit to the number of files that a program can open simultaneously
A program that terminates correctly will automatically close any open files before exit-ing A file stream destructor will also close a file referenced by a stream However, if the file is no longer in use before this point, you should close the file explicitly
Each of the file stream classes contains a definition of a void type method called
close(), which is used to close the file belonging to the stream
Example: myfile.close();
However, the file stream continues to exist It is therefore possible to use the stream
to open and manipulate another file
If you are not sure whether a file stream is currently accessing a file, you can always perform a test using the is_open()method In the case of the myfilefile stream, the test is as follows:
Example: if( myfile.is_open() )
{ /* */ } // File is open
Open files are also closed when you call the global function exit() The actual reason for using this function is to terminate a program in an orderly manner and return an error code to the calling process
Prototype: void exit( int status );
The calling process, to which the status error code is passed for evaluation, will often be the command interpreter—a Unix shell, for example Successful program execu-tion normally produces the error code 0 The statement return n;is thus equivalent
to the statement exit(n);when used in the main()function
The program on the opposite page copies a file stated in the command line If the user forgets to state a second (target) file, the source file is copied to standard output In this case, the source file will need to be a text file
Trang 2390 C H A P T E R 1 8 F U N D A M E N T A L S O F F I L E I N P U T A N D O U T P U T
// Pizza_W.cpp // Demonstrating output of records block by block
//
-#include <iostream>
#include <fstream>
using namespace std;
char header[] =
" * * * P I Z Z A P R O N T O * * *\n\n";
// Record structure:
struct Pizza { char name[32]; float price; };
const int MAXCNT = 10;
Pizza pizzaMenu[MAXCNT] = {
{ "Pepperoni", 9.90F }, { "White Pizza", 15.90F }, { "Ham Pizza", 12.50F }, { "Calzone", 14.90F } }; int cnt = 4;
char pizzaFile[256] = "pizza.fle";
int main() // To write records {
cout << header << endl;
// To write data into the file:
int exitCode = 0;
ofstream outFile( pizzaFile, ios::out|ios::binary );
if( !outFile) {
cerr << "Error opening the file!" << endl;
exitCode = 1;
} else { for( int i = 0; i < cnt; ++i) if( !outFile.write( (char*)&pizzaMenu[i],
sizeof(Pizza)) ) { cerr << "Error writing!" << endl;
exitCode = 2;
} } if( exitCode == 0) cout << "\nData has been added to file "
<< pizzaFile << "\n" << endl;
return exitCode;
}
■ READING AND WRITING BLOCKS
Sample program
Trang 3The file stream classes can use all the public operations defined in their base classes This means you can write formatted or unformatted data to a file or read that data from the file block by block or character by character
䊐 Formatted and Unformatted Input and Output
The previous sample programs illustrated how to use the methods get(),getline(), andput()to read or write data from or to text files Formatted input and output of numerical values, for example, requires the >> and << operators and appropriate manip-ulators or formatting methods
Example: double price = 12.34;
ofstream textFile("Test.txt");
textFile << "Price: " << price << "Dollar" << endl;
The file Test.txtwill contain a line of text, such as "Price " that exactly matches the screen output
Converting binary data to legible text is not practicable if you are dealing with large amounts of data It makes sense to write the data for a series of measurements to a binary file in the order in which they occur in the program To do so, you simply open the file
in binary mode and write the data to the file, or read it from the file, block by block
䊐 Transferring Data Blocks
Theostreammethodwrite()transfers given number of bytes from main memory to a file
Prototype: ostream& write( const char *buf, int n);
Sincewrite()returns a reference to the stream, you can check to ensure that the write operation was successful
Example: if( ! fileStream.write("An example ", 2) )
cerr << "Error in writing!" << endl;
A warning is issued if an error occurs while writing the characters "An" You can use the
read() method to read data blocks from the file The method transfers a data block from a file to a program buffer
Prototype: istream& read( char *buf, int n);
The methods read()andwrite()are often used for files with fixed length records The block that needs to be transferred can contain one or more records The buffer in main memory is either a structure variable or an array with elements belonging to the structure type You need to cast the address of this memory area to (char *)as shown
in the example opposite
Trang 4392 C H A P T E R 1 8 F U N D A M E N T A L S O F F I L E I N P U T A N D O U T P U T
// Class Account with methods read() and write() //
-class Account
{
private:
string name; // Account holder unsigned long nr; // Account number double balance; // Balance of account
public:
// Constructors, destructor,
// access methods,
ostream& Account::write(ostream& os) const;
istream& Account::read(istream& is)
};
// write() outputs an account into the given stream os // Returns: The given stream
ostream& Account::write(ostream& os) const
{
os << name << '\0'; // To write a string os.write((char*)&nr, sizeof(nr) );
os.write((char*)&balance, sizeof(balance) );
return os;
}
// read() is the opposite function of write()
// read() inputs an account from the stream is // and writes it into the members of the current object
istream& Account::read(istream& is)
{ getline( is, name, '\0'); // Read a string is.read( (char*)&nr, sizeof(nr) );
is.read( (char*)&balance, sizeof(balance));
return is;
}
■ OBJECT PERSISTENCE
Class Account
Implementing methods read() and write()
Trang 5䊐 Storing Objects
Objects are created during program runtime and cleaned up before the program
termi-nates To avoid this volatility, you can make an object persistent, that is, you can store
the object in a file However, you must ensure that the object can be reconstructed, as it was, when read This means dealing with the following issues:
■ Objects can contain other objects You will generally not know how to store a member object
■ Objects can contain references to other objects However, it does not make sense
to store pointer values in a file, as the memory addresses will change each time you re-launch the program
For example, the class Account on the opposite page contains the member object
name, which is a string type As string type objects are used to handle variable length strings, the object just contains a reference to the string It therefore makes no sense to save the memory content of size sizeof(name)occupied by the object name
in a file Instead, you should write the string itself to a file
One possible solution to this issue is to store the data to allow them to be passed to a constructor for the class when read Another solution involves providing methods to allow the objects to write their own data members to files or read them from files This technique is normally preferable since the class can now handle data storage itself, allow-ing it to write internal status data while simultaneously preventallow-ing external access to that data
䊐 Storing Account Class Objects
The opposite page shows the Accountclass, with which you are already familiar File input and output methods have been added to the class A file stream that references a file opened in binary mode is passed as an argument to the methods read() and
write() The return value is the stream in both cases, so the status can be queried when the function is called
Example: if( ! anAccount.write( outFile) )
cerr << "Error in writing!" << endl;
When you read an account, you can simultaneously create an empty object that the
read()method can access
Example: if( ! anAccount.read( inFile) )
cerr << "Error in reading!" << endl;
The member object nameis saved as a C string, that is, as a string terminated by the null character,'\0' The << operator and the function getline()are available for this task
Trang 6394 C H A P T E R 1 8 F U N D A M E N T A L S O F F I L E I N P U T A N D O U T P U T
fcopy file1 file2
A file,file1, is copied to file2 If file2already exists, it is overwritten fcopy file1
A file,file1, is copied to standard output, that is, to the screen if standard output has not been redirected
fcopy For calls without arguments, the source and destination files are entered
in a user dialog
Ifisis a file stream that references a file opened for reading, the following call
Example: char buf[1024];
is.read(buf, 1024);
transfers the next 1024 bytes from file to the buffer buf Provided that no error occurs, no less than 1024 bytes will be copied unless end-of-file is reached In this case the failandeofbits are set.The last block of bytes
to be read also has to be written to the destination file.The method
gcount()returns the number of bytes transferred by the last read operation
Example: int nread = is.gcount(); // Number of bytes
// in last read op
■ EXERCISES
For exercise 1 Possible calls to the program fcopy :
More details on the istream class method read()
Trang 7Exercise 1
The sample program fcopy1, which copies a file to the screen or to a second file, was introduced in this chapter.Write a program named fcopyto enhance
fcopy1as follows:
■ If the program is launched without any arguments, it does not issue an error message and terminate but requests user input for the names of the source and target files If an empty string is given as the name of the tar-get file, that is, the Return key is pressed, the source file is displayed on screen
■ If the command line or the user dialog contains valid source and target
file names, a binary copy operation is performed.
■ Copy the data block by block with the read()andwrite()methods The default block size is 1024 bytes
■ Thecopy()function returns falseif an error occurs while copying and
truein all other cases
Also refer to the notes on the opposite page
Exercise 2
a Modify the sample program Pizza_w.cppin this chapter to allow the user to add new pizza records to the four standard pizzas and store these records on file
b Then write a program called Pizza_r.cpp, which displays the pizza menu, that is, outputs the contents of the pizza file
Exercise 3
Test the methods read()andwrite()in the Accountclass.To do so, write a program called Account_rw.cppthat
■ initializes an array with account objects and stores the array in a file
■ reads the contents of the file to a second array and displays the accounts
in that array to allow you to check them
Use binary mode for read or write access to the file
Trang 8396 C H A P T E R 1 8 F U N D A M E N T A L S O F F I L E I N P U T A N D O U T P U T
New data members:
string filename; // File name bool dirty; // true, if data is not
// stored yet
New methods:
const string& getFilename() const;
bool setFilename( const string& fn);
bool isDirty() const;
bool load(); // Read data from the file bool save(); // Save data
bool saveAs(); // Save data as
* * * * * Telephone List * * * * *
S = Show all entries
F = Find a telephone number
A = Append an entry
D = Delete an entry
-O = -Open a file
W = Save in the file
U = Save as
-Q = -Quit the program
Your choice:
For Exercise 4 New members of class TelList
Extended menu of the application program
Trang 9Exercise 4
The program TelList, which was written as an exercise for Chapter 16, needs
to be modified to allow telephone lists to be saved in a file
To allow this, first add the data members and methods detailed on the
opposite page to TelList.The string filenameis used to store the name of the file in use.The dirty flag is raised to indicate that the phone list has been changed but not saved.You will need to modify the existing methods append()
anderase()to provide this functionality
The strings in the phone list must be saved as C strings in a binary file,
allowing for entries that contain several lines
Add the following items to the application program menu:
O = Open a file
Read a phone list previously stored in a file
W = Save
Save the current phone list in a file
U = Save as
Save the current phone list in a new file
Choosing one of these menu items calls one of the following methods as applicable:load(), save()orsaveAs().These methods return truefor a successful action and false otherwise.The user must be able to supply a file name for the save()method, as the list may not have been read from a file previously
If the phone list has been modified but not saved, the user should be
prompted to save the current phone list before opening another file or
terminating the program
Trang 10398 C H A P T E R 1 8 F U N D A M E N T A L S O F F I L E I N P U T A N D O U T P U T
■ SOLUTIONS Exercise 1
// -// fcopy.cpp
// Copy files // Call: fcopy [ source [ destination ] ] //
-#include <iostream>
#include <fstream>
using namespace std;
char usage[] = "Call: fcopy [source [destination]}";
inline void openerror( const char *file) {
cerr << "Error opening the file " << file << endl;
exit(1);
}
bool copy( istream& is, ostream& os), // Prototype,
ok = true; // ok flag
int main(int argc, char *argv[]) {
char source[256] = "", dest[256] = "";
switch( argc ) {
case 1: // No file declared
// ==> input file name cout << "Copying source file to "
"destination file!\n"
"Source file: ";
cin.getline( source, 256);
if( strlen(source) == 0) { cerr << "No source file declared!" << endl;
return 1;
} cin.sync(); // No previous input cout << "Destination file: ";
cin.getline( dest, 256);
break;
case 2: // One file is declared strcpy( source, argv[1]);
break;
case 3: // Source and destination files are declared strcpy( source, argv[1]);
strcpy( dest, argv[2]);
break;