Saving the Number of the Elements in the File

Give up? Okay, here it is: when we create the file that contains the data for the HomeItem objects, we can start by writing the number of HomeItem objects as the first line of the file. This is the solution we used to preallocate the m_Track Vec that holds the track names for a “Music” HomeItem. The disadvantage of this solution is that it is harder to apply when the input file is generated directly by a human being, who is likely to make a mistake in counting the elements. However, this is not much of a drawback when we consider that the most common way to generate such a file in the real world is to create, edit, and delete items via a program. This program will read any pre-existing data file, allow modifications to the items from the file, and write out the updated data to the file so that it will be there the next time we start the program. Of course, such a program provides other facilities such as producing reports and searching for individual items, but as long as we're maintaining the whole database in memory, those functions don't have to worry about the structure of the file.

Susan had some questions about the inventory file.

Susan: What file are you talking about?

Steve: The file that holds the information about all of the HomeItem objects in the inventory.

Susan: How was that file created?

Steve: Either by writing it with a text editor or by adding objects using the AddItem function and then telling the program to write it out.

Susan: How does the program know where the data for each item starts?

Steve: Our implementation of operator >> knows how many fields there are for each object; when the data for one object is finished, the data for the next object must be coming up next in the file.

Figure 11.17 is the version of the LoadInventory function that uses a file whose first line is the count of items.

Figure 11.17. Yet another implementation of LoadInventory (from codehmin3.cpp)
short HomeInventory::LoadInventory(ifstream& is)
{
  short i;
  short ElementCount;

  is >> ElementCount;
  is.ignore();

  m_Home.resize(ElementCount+1);

  for (i = 0; ; i ++)
   {
   is >> m_Home[i];

   if (is.fail() != 0)
     break;
   }

  if (i < ElementCount)
   {
   cerr << "Not enough items in input file" << endl;
   exit(1);
   }

  m_Home.resize(ElementCount);

  return i;
}

The first part of this should be fairly obvious; we are reading the number of elements from the file into a variable called ElementCount, and then ignoring the end of line character, as we must always do after reading a numerical value. However, the next statement might not be so obvious; it sets the size of the Vec to one more than the number of items that we expect to read. Why do we need an extra element in the Vec?

Handling the End-of-File Condition Properly

If we were to allocate exactly enough elements to store the data that we read from the file, we wouldn't be able to try to read one more element so that we could tell that we had reached the end of the file. The problem is that an attempt to use a Vec element that doesn't exist produces an “invalid element number” error from the Vec code, so we would never reach the statement that calls fail to find out that we are at the end of the file. For this reason, I've added one element to the number of items that we actually expect so that we can tell if we have reached the end of file on schedule.

Of course, after we have finished reading all the data and have reached the end of the file, we must make sure that we have read the number of items we expected. If we're short one or more items, we display an error and exit from the program; on the other hand, if the number of items is correct, we reset the size of the m_Home Vec to that number and return to the calling function.

Susan had a question about the way the error message was displayed.

Susan: What's cerr?

Steve: That's another automatically created ostream object, like cout. The difference is that you can make the output from cout go to a different file in a number of ways, both in the program and outside it. However, doing that doesn't affect where cerr sends its data. In other words, even if you change where the “normal” output goes, cerr will still send its data to the screen where the user can see the messages.

So that explains how the error message for a short file is displayed. However, we still need to consider the other possibility: having more items in the file than were supposed to be there. Why is this important? Because if there were actually more items in the file and we continued processing the data without telling the user about this problem, the information for those remaining items would be lost when we rewrote the file at the end of the program; obviously, that would be a serious mistake. It's almost always better to program “defensively” when possible rather than to assume that everything is as it is supposed to be and that no one has made any errors in the data.

So what will happen if there are more items in the file than there were supposed to be? We get an error from the Vec code, because we try to read into an element of the Vec that doesn't exist. Therefore, that possibility is covered.

Ignoring the possibility of errors in the data is just one way to produce a system that is overly susceptible to errors originating outside the code. Such errors can also result from the program being used in unexpected ways or even from the seemingly positive situation of a program with an unexpectedly long service life, as has occurred in some cases when the century part of the date changed from "19" to "20" (i.e., the "Year 2000 problem").

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset