14.10.1. rolodexdb.cpp(1): Random and Direct Access Example

A Rolodex again serves as the example for the next demonstration program. However, we make two significant changes as we move forward. First, the program forms a simple database with a few basic operations. Second, we implement our program with classes that span four files:

A file of card objects represented as rows in a table. Each row has three columns, corresponding to the three member variables in a card object.
File of card objects. Each row represents one instance of the card class, which has three fields. The first card object is at position 0 and subsequent cards are offset by a multiple of sizeof(card).
card.h
Contains the card class specification. All member functions are inline, so there isn't a source code file.
rolodex.h
Contains the rolodex class specification. Member functions are prototyped.
rolodex.cpp
Contains the rolodex class member function definitions.
rolodexdb.cpp
Contains main and other application functions.

Each file isolates and organizes some basic program functionality. All four files, the complete working program, are available at the bottom of the page. As only the Rolodex member functions contain code about this chapter, they are the only functions described in detail here. The functions contained in the other files are prototype below to provide a context for the Rolodex member functions.

Feature Description
enum {  NAME_SZ = 20,
	ADDR_SZ = 40,
	PHONE_SZ = 15 };
Symbolic constants for the length of the C-string members
char	name[NAME_SZ];
char	address[ADDR_SZ];
char	phone[PHONE_SZ];
Private C-string member fields for the name, address, and phone number
card()
Default constructor
card(char* n, char* a, char* p)
General constructor
bool equals(char* target)
Compares a target string with the name in an instance of card
void print()
Prints the data stored in a card, name, address, and phone, to the console
card.h. Specifies the card class. An instance of the card class represents one card or record in the Rolodex database or file.

 

Feature Description
fstream data;
Stream object to access the rolodex data file; a private data member.
rolodex();
Default constructor.
~rolodex() { data.close(); }
Destructor (b) that closes the data file.
void add(char* name, char* address, char* phone);
add function detail.
void search(char* name);
search function detail.
void list();
list function detail.
void edit(char* name, char* address, char* phone);
edit function detail.
rolodex.h. Specifies class rolodex. An instance of the rolodex class represents a simple Rolodex database. All of the class's member functions, prototyped here, are public. Notice that the database doesn't include a delete operation: once an object is written to a file, it's difficult to physically remove it (unless it's a the end of the file). So, a data record may be logically empty (flagged in some way) in the sense that it doesn't contain active or valid data, but it still takes space in the file.

 

Feature Description
int main();
Creates an instance of the rolodex class. In an infinite loop, calls the menu function and carries out the user-selected operation by calling one of the rolodex functions.
char menu();
Prints a menu, prompts the user to select an operation, and returns the selected operation to main.
void input(char* prompt, char* field, int size);
Prompts the user for and reads from the console. Input values are stored in the C-string field, which has a length of size.
void pause();
A system independent console pause function.
rolodexdb.cpp: Defines the program start and the user interface functions.

 

rolodex::rolodex()
{
	data.open("rolodex.bin", ios::binary | ios::in | ios::out);		// (a)

	if (!data.is_open())							// (b)
	{
		ofstream make("rolodex.bin");					// (c)
		make.close();							// (d)
		data.open("rolodex.bin", ios::binary | ios::in | ios::out);	// (e)
	}

	if (!data.good())							// (f)
	{
		cerr << "Unable to open data file." << endl;
		exit(1);
	}
}
The rolodex constructor. Creates the data file if it doesn't exit. Opens the data file for all database operations.
  1. Attempts to open the data file in binary mode for reading and writing - this operation fails if the file does not already exist. The second parameter, needed to open the file in binary mode, replaces the fstream default open mode of in|out, so these flags must be included with the open opertion's modes.
  2. Tests the file to see if it opened - will be false if the file doesn't exist.
  3. Creates a new file using a temporary ofstream object.
  4. Closes the new file
  5. Makes another attempt to open the data file in binary mode for reading and writing.
  6. Aborts the program if the data file cannot be created or opened.

 

void rolodex::add(char* name, char* address, char* phone)
{
	card	c(name, address, phone);		// (a)

	data.seekp(0, ios::end);			// (b)
	data.write((char *) &c, sizeof(card));		// (c)
	data.flush();					// (d)
}
The add member function. Adds a card object to the data file with the argument information about a person.
  1. Constructs the card object to add to the data file.
  2. Moves the "put" or write pointer to the end of the data file.
  3. Writes the contents of the card to the data file.
  4. Forces the data just added to the file to be written. Recall that the file buffer is only written when filled, closed, or flushed; so, this step is needed in case the next operation needs to read the just-written record.

 

 

void rolodex::list()
{
	card	c;						// (a)

	data.seekg(0);						// (b)
	while (data.read((char *) &c, sizeof(card)))		// (c)
		c.print();					// (d)
	data.clear();						// (e)
}
The rolodex list member function. Lists all of the information in the data file (i.e., displays each card or record as one row in a table printed on the console).
  1. Defines a temporary card object to hold each record as it is read and printed.
  2. Rewinds (i.e., moves to) the beginning of the data file.
  3. Reads each record from the data file. Reaching the end of the file breaks out of the while-loop.
  4. Prints each record to the console.
  5. Clears the end-of-file flag. The last read function call does not read any data but it does set the EOF flag.

 

void rolodex::edit(char* name, char* address, char* phone)
{
	card	current;						// (a)
	data.seekg(0);							// (b)


	while (data.read((char *) &current, sizeof(card)))		// (c)
		if (current.equals(name))				// (d)
		{
			cout << "Current card data:" << endl;
			current.print();				// (e)
			break;
		}

	if (data.eof())							// (f)
	{
		cout << "Not found: " << name << endl;
		data.clear();						// (g)
		return;
	}
	
	card	c(name, address, phone);				// (h)
	data.seekp(data.tellg() - (streampos)sizeof(card));		// (i)
	data.write((char *) &c, sizeof(card));				// (j)
	data.flush();							// (k)
}
The rolodex edit member function. Searches the data file for a card that matches the name. If a match is found, the current data is printed to the console and the card is replaced by a new card with the argument data. The function demonstrates how to read from and write to a file at a specific position, but it is not a robust or "production grade" edit function.
  1. A card object to hold the card data read from with file while searching for a match.
  2. Moves the get or read pointer to the beginning of the file.
  3. Reads each record from the data file. Reaching the end of the file breaks out of the while-loop.
  4. Compares the name in the card just read to see if a match is found.
  5. If a match is found, the data currently stored in the file is printed to the console.
  6. If a match is not found, the file will be at the end.
  7. Clears the EOF flag if a match was not found.
  8. Creates a new or replacement card with the argument data.
  9. Backs up the put or write pointer one record: tellg is the current location of the get pointer following the last read and sizeof returns the number of bytes just read.
  10. Replaces or overwrites the information in the data file with the information in the newly created card.
  11. Forces the update to be written. The file buffer is only written when filled, closed, or flushed, so flushing the edited record makes it available for the next I/O operation.

Downloadable Files