pickzy.com

C  |  C++  |  Objective-C  |  VC++  |  Win32  |  MFC  |  Java  |  Php  |  Delphi  |  Visual Basic  |  .Net  |  Networking  |  General  |  Games  |  Jobs  |  Javascript  |  




Menu

pickSourcecode.com


        

 




 

Cpp > Articles

 

Fundamentals of File Input and Output

The File Stream Classes in the iostream Library

The class hierarchy on the opposite page shows that the file stream classes contain the stream classes, with which you are already familiar, as base classes:

the ifstream class derives from the istream class and allows file reading

the ofstream class derives from the ostream stream class and supports writing to files

the fstream class derives from the iostream stream class. As you would expect, it supports both read and write operations for files.

The file stream classes are declared in the fstream header file. An object that belongs to a file stream class is known as a file stream.

Functionality
The file stream classes inherit the functionality of their base classes. Thus, the methods, operators, and manipulators you have already used for cin and cout are also available here. Thus every file stream has:

methods for non-formatted writing and reading of single characters and/or data blocks

the operators << or >> for formatted reading and writing from or to files

methods and manipulators for formatting character sequences

methods for state queries.

File handling methods, particularly methods for opening and closing files, round off the package.

Creating File Streams

#include <iostream>
#include <fstream>

using namespace std;

int main( int argc, char *argv[])
{
   if( argc != 2 )              // File declared?
   {
       cerr << "Use: showfile filename" << endl;
       return 1;
   }
   ifstream file( argv[1]);   // Create  a file stream
   if( !file )                // Get status.
   {
     cerr << "An error occurred when opening the file"<< argv[1] << endl;
     return 2;
   }
   char line[80];
   int cnt = 0;
   while( file.getline( line, 80))  // Copy the file
   {                                 // to standard
     cout << line << endl;           // output.
     if( ++cnt == 20)
    {
      cnt = 0;
       cout << " ---- <return> to continue ---- " << endl;
       cin.sync(); cin.get();
      }
   }
  if( !file.eof() )         // End-of-file occurred?
   {
     cerr << "Error reading the file "<< argv[1] << endl;
     return 3;
   }
   return 0;
}

Opening a File
You need to open a file before you can manipulate it. To do so, you can state the file name, which can also contain a path define a so-called file access mode.

If the path is not explicitly stated, the file must be in the current directory. The file access mode specifically defines whether read and/or write access to the file is permitted.

Any files still open when a program terminates are automatically closed.

File Stream Definition
You can open a file when you create a file streamyou simply state the file name to do so. In this case default values are used for the file access mode.

Example:

ifstream myfile("test.fle");
The file name test.fle is passed to the constructor of the ifstream class, which opens the file for reading. Since the path was not stated, the file must be in the current directory. When a file is opened, the current file position is the beginning of the file.

If you create a file stream for write-only access, the file you state need not exist. In this case a new file is created.

Example:

ofstream yourfile("new.fle");
This statement creates a new file called new.fle and opens the file for writing. But be careful! If the file already exists, it will be truncated to a length of zero bytes, or in other words deleted.

You can create a file stream which does not reference a specific file and use the open() method to open a file later.

Example:

ofstream yourfile;
yourfile.open("new.fle");
This example has the same effect as the previous example. More specifically, open() uses the same default values for file access when opening a file as the default constructor for the class.

It rarely makes sense to use fixed file names. In the case of the sample program on the opposite page, you state the file name in the command line when you launch the program. If no file name is supplied, the program issues an error message and terminates. Using interactive user input is another possible way to define a file name.

Open Mode Flags
In addition to the file name, you can pass a second argument for the open mode to the constructors and the open() method. The open mode is determined by using flags. A flag represents a single bit in a computer word. If the flag is raised, the bit in question will contain the value 1, with 0 representing all other cases.
You can use the bit operator, |, to combine various flags. Either the flag ios::in or ios::out must be stated in all cases. If the flag ios::in is raised, the file must already exist. If the flag ios::in is not used, the file is created, if it does not already exist.
Example:
fstream addresses("Address.fle", ios::out | ios::app);
This opens a file for writing at end-of-file. The file is created, if it does not already exist. The file will automatically grow after every write operation.
You can use the default mode for the fstream class, that is, ios::in | ios::out, to open an existing file for reading and writing. This so-called update mode is used for updating the information in a file and is often seen in conjunction with random file access.
Error Handling
Errors can occur when opening a file. A user may not have the required access privileges, or the file you want to read may not exist. The state flag failbit of the ios base class is raised in this case. The flag can either be queried directly using the fail() method, or indirectly by querying the status of a file stream in an if condition.
Example:
if( !myfile)          // or: if( myfile.fail())
The fail bit is also set if a read or write error occurs. If a read operation fails, the end of the current file may have been reached. To distinguish this normal behavior from a read error, you can use the eof() method (eof = end-of-file) to query the eof bit:
Example:
if( myfile.eof())     // At end-of-file?
The eof bit is set if you try to carry on reading at the end
Closing Files

#include <iostream>

#include <fstream>

using namespace std;

inline void openerror( const char *file)
{
  cerr << "Error on opening the file " << file << endl;
  exit(1);                // Ends program closing
}                         // all opened files.

void copy( istream& is, ostream& os);    // Prototype

int main(int argc, char *argv[])
{
  if( argc < 2 || argc > 3)
  { 
cerr << "Call: fcopy1 source [ destination ]" << endl;
    return 1;                      // or: exit(1);
  }
  ifstream infile(argv[1]);         // Open 1st file
  if( !infile.is_open())
    openerror( argv[1]);
  if( argc == 2)               // Just one sourcefile.
     copy( infile, cout);
  else                         // Source and destination
  {
     ofstream outfile(argv[2]);     // Open 2nd file
     if( !outfile.is_open() )
        openerror( argv[2]);
     copy( infile, outfile);
     outfile.close();               // Unnecessary.
  }
  infile.close();                   // Unnecessary.
  return 0;
}
  
void copy( istream& is, ostream& os)  // Copy it to os.
{
  char c;
  while( is.get(c) )
  os.put(c);                  // or:  os << c ;
}


Motivation
After you have completed file manipulation, the file should always be closed for the following 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 exiting. 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.

Methods close() and is_open()
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 myfile file stream, the test is as follows:

Example:

if( myfile.is_open() )
{ /* . . . */ }            // File is open
The exit() Function
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 interpretera Unix shell, for example. Successful program execution 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.

Reading And Writing Blocks

#include <iostream>
#include <fstream>
using namespace std;
  
char header[] =
"    * * *  P I Z Z A  P R O N T O  * * * ";
// 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 << " Data has been added to file "
          << pizzaFile << " " << endl;
   return  exitCode;
}

The 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(), and put() 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 manipulators or formatting methods.
Example:
double price = 12.34;
ofstream textFile("Test.txt");
textFile << "Price: " << price << "Dollar" << endl;
The file Test.txt will 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
The ostream method write() transfers given number of bytes from main memory to a file.
Prototype:
 ostream& write( const char *buf, int n);
Since write() 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() and write() 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.
Object Persistence
Class Account

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)
};

Implementing methods read() and write()
// write() outputs an account into the given stream os.
// Returns: The given stream.
ostream& Account::write(ostream& os) const
{
   os << name << '';         // 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, '');   // Read a string
   is.read( (char*)&nr, sizeof(nr) );
   is.read( (char*)&balance, sizeof(balance));
   return is;
}

Storing Objects
Objects are created during program runtime and cleaned up before the program terminates. 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, allowing it to write internal status data while simultaneously preventing external access to that data.

Storing Account Class Objects
The opposite page shows the Account class, 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 name is saved as a C string, that is, as a string terminated by the null character, ''. The << operator and the function getline() are available for this task.

Exercises
For exercise 1
Possible calls to the program fcopy:
fcopy file1 file2

A file, file1, is copied to file2. If file2 already 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.

More details on the istream class method read()
If is is 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 fail and eof bits 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.

Exercise 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 fcopy to enhance fcopy1 as 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 target 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() and write() methods. The default block size is 1024 bytes.

The copy() function returns false if an error occurs while copying and true in all other cases.

Also refer to the notes on the opposite page.

Exercise 2
Modify the sample program Pizza_w.cpp in this chapter to allow the user to add new pizza records to the four standard pizzas and store these records on file.

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() and write() in the Account class. To do so, write a program called Account_rw.cpp that

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.

For Exercise 4
New members of class TelList
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 ...
Extended menu of the application program
        * * * * *  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:

Exercise 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 filename is 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() and erase()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() or saveAs(). These methods return true for 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.  


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! "
             "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;
   default:             // Invalid call to the program.
     cerr << usage << endl;
     return 2;                // or: exit(2);
  }
  
  if( strlen(dest) == 0)      // Only source file?
  {                           // yes ==> output to cout.
    ifstream infile(source);
    if( !infile )
        openerror( source);
    ok = copy( infile, cout);
    // The file is closed by the ifstream destructor.
  }
  else                        // Copy source to destination
  {                           // file in binary mode.
    ifstream infile( source, ios::in | ios::binary);
    if( !infile )
        openerror( source);
    else
    {
      ofstream outfile( dest, ios::out | ios::binary);
      if( !outfile)
         openerror( dest);
      ok = copy( infile, outfile);
      if( ok)
        cerr << "File " << source << " to file "
             << dest <<" copied!"<< endl;
    }
  }
  if(!ok)
  {  cerr << "Error while copying!" << endl;
     return 3;
  }
  return 0;
}
  
bool copy( istream& is, ostream& os)  // To copy
{                                     // is to os.
  const int BufSize = 1024;
  char buf[BufSize];
  do
  {
     is.read( buf, BufSize);
     if( is.gcount() > 0)
       os.write(buf, is.gcount());
  }
  while( !is.eof() && !is.fail() && !os.fail() );
  if( !is.eof() ) return false;
  else            return true;
}
Exercise 2
// ----------------------------------------------------
// Pizza.h
// Header file for Pizza_W.cpp and Pizza_R.cpp.
// ----------------------------------------------------
#include <iostream>
#include <iomanip>
#include <fstream>
using namespace std;
// Structure of a record:
struct Pizza {  char  name[32];  float price;  };
#define MAXCNT     20           // Maximum number of pizzas
#define FILENAME  "pizza.fle"
  
inline void header()
{
  cout << "      * * *  P I Z Z A  P R O N T O  * * * "
       << endl;
}
  
// ----------------------------------------------------
// Pizza_w.cpp
// Demonstrating blockwise writing of records.
// ----------------------------------------------------
#include "Pizza.h"
Pizza pizzaMenu[MAXCNT] =
{
   { "Pepperoni", 9.90F },  { "White Pizza", 15.90F },
   { "Ham Pizza", 12.50F }, { "Calzone", 14.90F } };
  
int  cnt = 4;
char pizzaFile[256] = FILENAME;
  
int main()                         // Write records.
{
   int i;
   header();
   cout << " Our standard offer: " << endl;
   cout << fixed << setprecision(2);
   for( i = 0; i < cnt; ++i)
      cout << setw(20) << pizzaMenu[i].name
           << setw(10) << pizzaMenu[i].price << endl;
   cout << " ----------------------------------------- "
        << endl;
   // Input more pizzas via keyboard:
   while( cnt < MAXCNT)
   {
     cin.sync(); cin.clear();
     cout << "What pizza should be added "
          << "to the menu? " << "Name:  ";
     cin.getline( pizzaMenu[cnt].name, 32);
     if( pizzaMenu[cnt].name[0] == '')
       break;
  
     cout << "Price: ";
     cin >> pizzaMenu[cnt].price;
  
     if( !cin)
       cerr << "Invalid input!" << endl;
     else
       ++cnt;
  
     if( cnt < MAXCNT)
       cout << " ... and the next pizza! "
            << "Stop with <Return>. ";
   }
  
   // Add data to 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 to file!"
                << endl;
           exitCode = 2;
        }
   }
   if( exitCode == 0)
     cout << " Data added to file " << pizzaFile
          << ". " << endl;
  
   return exitCode;
}
// ----------------------------------------------------
// Pizza_r.cpp
// Demonstrating block by block reading of records.
// ---------------------------------------------------
  
#include "Pizza.h"
  
char pizzaFile[256] = FILENAME;
  
int main()              // Read and display records.
{
   header();
  
   ifstream inFile( pizzaFile, ios::in | ios::binary);
   if( !inFile)
   {
      cerr << "Pizza file does not exist!" << endl;
      return 1;
   }
   Pizza onePizza;
   int cnt = 0;
  
   cout << " -------------------------------------------"
        << " The available pizzas: " << endl;
  
   cout << fixed << setprecision(2);
   while( true)
      if( !inFile.read( (char*)&onePizza, sizeof(Pizza)) )
          break;
      else
      {
          cout << setw(20) << onePizza.name
               << setw(10) << onePizza.price << endl;
          ++cnt;
      }
  
   cout << " ------------------------------------------ "
        << endl;
  
   if( !inFile.eof())
   {
      cerr << "Error reading file!" << endl;
      return 2;
   }
   else
      cerr << "These are " << cnt << " pizzas! " << endl;
  
   return 0;
}
Exercise 3
// -------------------------------------------------------
// Account_rw.cpp
// Writes an array with objects of class Account to
// a file and feed the array into another array.
// -------------------------------------------------------
  
#include "Account.h"     // Definition of the class Account
#include <iostream>
#include <fstream>
using namespace std;
  
Account AccTab1[3] =
{
  Account("Lucky, Luke", 707070, -1200.99),
  Account("Mickey, Mouse", 123000, 2500.0),
  Account("Snoopy, Dog "        // String can contain more
          "Cell #: 01771234567", 543001)  // than one line.
};
  
Account AccTab2[3];     // Calls to default constructor
  
int cnt = 3;
  
char file[] = "account.fle";
  
int main()
{
   int i = 0;
  
   // --- Write accounts to file ---
  
   ofstream outFile( file, ios::out | ios::binary );
   if( ! outFile)
   {
     cerr << "Error opening file " << file
          << endl;
     return 1;
   }
   for( i = 0; i < cnt; ++i)
     if( !AccTab1[i].write(outFile) )
     {
       cerr << "Error writing to file " << file
            << endl;
       return 2;
     }
   outFile.close();
   // --- Reads accounts from file ---
  
   ifstream inFile( file, ios::out | ios::binary );
   if( ! inFile)
   {
     cerr << "Error opening file " << file
          << endl;
     return 3;
   }
   for( i = 0; i < cnt; ++i)
     if( !AccTab2[i].read(inFile) )
     {
       cerr << "Error reading file " << file
            << endl;
       return 4;
     }
   inFile.close();
  
   // --- Displays the accounts read ---
  
   cout << "The file " << file << " contains the "
        << "following accounts:" << endl;
  
   for( i = 0; i < cnt; ++i)
      AccTab2[i].display();
  
   cout << endl;
  
   return 0;
}
Exercise 4
// -------------------------------------------------------
// telList.h
// A class TelList to represent a list
// containing names and telephone numbers.
// The methods load(), save(), and saveAs() serve for
// loading and saving a telephone list.
// --------- ---------------------------------------------
  
#ifndef _TelList_
#define _TelList_
  
#include <string>
using namespace std;
  
#define PSEUDO -1          // Pseudo position
#define MAX 100            // Maximum number of elements
// Type of a list element:
struct Element { string name, telNr; };
  
class TelList
{
  private:
    Element v[MAX];        // The array and the actual
    int count;             // number of elements.
  
    string filename;       // File name
    bool dirty;            // true if data has been changed
                           // but not yet saved.
  public:
    TelList() : count(0), filename(""), dirty(false)
    {}
  
    int getCount() { return count; }
  
    Element *retrieve( int i )
    {
       return (i >= 0 && i < count)? &v[i] : NULL;
    }
    bool append( const Element& el )
    {
       return append( el.name, el.telNr);  
    }
    bool append( const string& name, const string& telNr);
    bool erase( const string& name);
    int  search( const string& name) const;
    void print() const;
    int  print( const string& name) const;
    int  getNewEntries();
  
    const string& getFilename() const { return filename; }
    bool setFilename( const string& fn)
    {  if( fn.empty()
          return false;
       else { filename = fn;  dirty = true; return true; }
    }
    bool isDirty() const { return dirty; }
    bool load();
    bool save();
    bool saveAs();
};
#endif  // _TelList_
  
// -------------------------------------------------------
// TelList.cpp
// Implements the methods of class TelList.
// -------------------------------------------------------
#include "telList.h"      // Definition of class TelList
#include <iostream>
#include <iomanip>
#include <fstream>
using namespace std;
bool TelList::append( const string& name,
                      const string& telNr)
{
    if( count < MAX                 // Any space
        && name.length() > 1        // minimum 2 characters
        && search(name) == PSEUDO)  // does not exist
    {
      v[count].name  = name;
      v[count].telNr = telNr;
      ++count;
      dirty = true;
      return true;
    }
    return false;
}
  
bool TelList::erase( const string& key )
{
   int i = search(key);
   if( i != PSEUDO )
   {
     if( i != count-1)            // Copy the last element
         v[i] = v[count-1];       // to position i.
     --count;
  dirty = true;
     return true;
   }
   return false;
}
// --------------------------------------------------
// Methods search(), print(), getNewEntries()
// are unchanged (refer to solutions of chapter 16).
// --------------------------------------------------
  
// Methods for loading and saving the telephone list.
  
bool TelList::load()
{
   cout << " --- Load the telephone list "
        << "from a file. ---" << " File: ";
  
   string file;                      // Input file name.
   cin.sync(); cin.clear();          // No previous input
   getline( cin, file);
   if( file.empty())
   {
      cerr << "No filename declared!" << endl;
      return false;
   }
// Open the file for reading:
   ifstream infile( file.c_str(), ios::in | ios::binary);
   if( !infile )
   {
      cerr << "File " << file
           << " could not be opened!" << endl;
      return false;
   }
   int i = 0;
   while( i < MAX)
   {
      getline( infile, v[i].name, '');
      getline( infile, v[i].telNr, '');
      if( !infile)
         break;
      else
         ++i;
   }
   if( i == MAX)
      cerr << "Max capacity " << MAX
           << " has been reached!" << endl;
   else if( !infile.eof())
   {
      cerr << "Error reading file " << file << endl;
      return false;
   }
   count = i;
   filename = file;
   dirty = false;
   return true;
}
  
bool TelList::saveAs()
{
   cout << "-- Save the telephone list in a file. --"
        << " File: ";
   string file;                    // Input file name.
   cin.sync(); cin.clear();        // No previous input
   getline( cin, file);
   if( !setFilename(file))
   {
      cerr << "No file name declared!" << endl;
      return false;
   }
   else
     return save();
}
bool TelList::save()      // Save the telephone list.
{
   if( filename.empty())
      return saveAs();
   if( !dirty)
       return true;
  
   ofstream outfile( filename.c_str(),
                     ios::out | ios::binary);
   if( !outfile )
   {
      cerr << "File " << filename
           << " could not be opened!" << endl;
      return false;
   }
  
   int i = 0;
   while( i < count)
   {
      outfile << v[i].name  << '';
      outfile << v[i].telNr << '';
      if( !outfile)
         break;
      else
         ++i;
   }
   if( i < count)
   {
      cerr << "Error writing to file " << filename << endl;
      return false;
   }
   dirty = false;
   return true;
}
  
// -------------------------------------------------------
// TelList_.cpp
// Organize a telephone list with class TelList.
// -------------------------------------------------------
#include "telList.h"      // Definition of class TelList
#include <iostream>
#include <string>
#include <cctype>
using namespace std;
  
inline void cls()
{
   cout << " "; // If ANSI control characters are
}                       // not available, output new-lines.
inline void go_on()
{
   cout << " Go on with return! ";
   cin.sync();  cin.clear();        // No previous input
   while( cin.get() != ' ')
       ;
}
int menu();               // Enter a command
char askForSave();        // Prompt user to save.
char header[] =
"           * * * * *  Telephone List  * * * * * ";
TelList myFriends;       // A telephone list
  
int main()
{
  int action = 0;        // Command
  string name;           // Read a name
  while( action != 'Q')
  {
    action = menu();
    cls();  cout << header << endl;
    switch( action)
    {
// ---------------------------------------------------
//   case 'S':  case 'F':  case 'A':  case 'D':
//   unchanged (refer to the solutions of chapter 16).
// ---------------------------------------------------
     case 'O':                      // To open a file
        if(myFriends.isDirty() && askForSave() == 'y')
           myFriends.save();
        if( myFriends.load())
           cout << "Telephone list read from file "
                << myFriends.getFilename() <<"!"
                << endl;
        else
           cerr << "Telephone list not read!"
                << endl;
        go_on();
        break;
     case 'U':                      // Save as ...
        if( myFriends.saveAs())
          cout << "Telephone list has been saved in file: "
               << myFriends.getFilename() << " !" <<endl;
        else
           cerr << "Telephone list not saved!" << endl;
        go_on();
        break;
     case 'W':                      // Save
        if( myFriends.save())
           cout << "Telephone list has been saved in "
                << "the file "
                << myFriends.getFilename() << endl;
        else
           cerr << "Telephone list not saved!"
                << endl;
        go_on();
        break;
  
     case 'Q':                     // Quit
        if( myFriends.isDirty()  &&  askForSave() == 'Y')
           myFriends.save();
        cls();
        break;
    }
  } // End of while
  return 0;
}
  
int menu()
{
   static char menuStr[] =
// . . .
   "              -------------------------------------"
   "              O = Open a file"
   "              W = Save "
   "              U = Save as ..."
   "              -------------------------------------"
   "              Q = Quit the program"
 " Your choice:  ";
  
// ---------------------------------------------------
// everything else unchanged (cf. solutions in Chapter 16)
// ---------------------------------------------------
   return choice;
}
  
char askForSave()
{
   char c;
   cout <<  "Do you want to save the phone list(y/n)? ";
 &nb

 
Privacy Policy | About Us