This example continues using the Rolodex problem to demonstrate I/O operations by making two significant changes. First, it changes how the program represents a Rolodex card from a delimiter-separated line to a structure named card, whose fields correspond to a name, address, and phone number. Second, it replaces the line-oriented operations with block read and write functions.
const int NAME_SZ = 20; // (a) const int ADDRESS_SZ = 35; const int PHONE_SZ = 20; struct card // (b) { char name[NAME_SZ]; char address[ADDRESS_SZ]; char phone[PHONE_SZ]; };
The example's primary goal is demonstrating the block read and write functions. Consequently, it oversimplifies the initial data collection and omits the searching and deletion operations supported by most rudimentary database programs. Subsequent versions provide more robust and authentic implementations. The following figure excerpts the opening and validation operations, providing a context for the I/O operations and making the example easier to present.
#include <iostream> #include <fstream> #include <iomanip> //#include <cstring> // Version 2: strcmp //#include <stdlib.h> // Version 2: qsort #include "rolodex.h" using namespace std; //int order(const void* e1, const void* e2); // Version 2: function prototype int main() { ofstream out("rolodex", ios::binary); // (a) if (!out.good()) { cerr << "Unable to open \"rolodex\"\n"; exit(1); } // code to define variables and write the data out.close(); // (b) ifstream in("rolodex", ios::binary); // (c) if (!in.good()) { cerr << "Unable to open \"rolodex\"\n"; exit(1); } // code to define variables and read the data return 0; // (d) }
Each card or record entered into the file is an instance of the card structure. So, the file representing the Rolodex looks very much like Figure 2(a) where each square is an instance of card.
card c0 = { "Bill Gates", "1 Microsoft Way, Redmond, WA", "(403) 123-4567" }; // (a) card c1 = { "Cranston Snort", "1600 Pennsylvania Ave", "(306) 678-9876" }; card c2 = { "Albert Einstein", "Princeton, NJ", "(456) 123-8765" }; card c3 = { "John Smith", "123 Elm St.", "801-555-1234" }; out.write((char *) &c0, sizeof(card)); // (b) out.write((char *) &c1, sizeof(card)); out.write((char *) &c2, sizeof(card)); out.write((char *) &c3, sizeof(card));
sizeof
operator, which returns the size, measured in bytes, of the structure object.card c; // (a) while (in.read((char *) &c, sizeof(card))) // (b) { cout << left << setw(NAME_SZ) << c.name; cout << setw(ADDRESS_SZ) << c.address; cout << setw(PHONE_SZ) << c.phone << endl; }
sizeof
operator finds the number of bytes in the structure, forming the second argument.The final block read example adds authenticity by reading the card objects into an array and sorting them alphabetically by name using the qsort library function. qsort compares card objects two at a time with an ordering function, rearranging them as necessary to affect the alphabetical ordering. The following figures illustrate the programmer-defined card ordering function and how qsort uses it.
int order(const void* e1, const void* e2) { return strcmp(((card *)e1)->name, ((card *)e2)->name); }
card cards[100]; // (a) int count = 0; while (count < 99 && in.read((char *) &cards[count], sizeof(card))) // (b) count++; qsort(cards, count, sizeof(card), order); // (c) for (int i = 0; i < count; i++) // (d) { cout << left << setw(NAME_SZ) << cards[i].name; cout << setw(ADDRESS_SZ) << cards[i].address; cout << setw(PHONE_SZ) << cards[i].phone << endl; }
View | Download | Comments |
---|---|---|
rolodex.h | rolodex.h | The rolodex structure specification and associated field size definitions. |
b-rolodex1.cpp | b-rolodex1.cpp | The first version reads and formats objects in a loop. |
b-rolodex2.cpp | b-rolodex2.cpp | The second version reads objects into an array with a loop, sorts the array, and formats and prints the objects in a second loop. |