Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Advanced C 1992

.pdf
Скачиваний:
93
Добавлен:
17.08.2013
Размер:
4.28 Mб
Скачать

Part II • Managing Data in C

TempCustomer = FirstCustomer;

printf(“Display customers\n”);

while (TempCustomer)

{

if (nDebug)

{

printf(

“Name ‘%10s’ Me %lp Next %lp Prev %lp\n”, TempCustomer->szName,

TempCustomer, TempCustomer->NextCustomer, TempCustomer->PrevCustomer);

}

else

{

printf(

“Name ‘%10s’ City ‘%10s’ State ‘%2s’ “ “ZIP ‘%5.5d’\n”, TempCustomer->szName, TempCustomer->szCity, TempCustomer->szState, TempCustomer->nZip);

}

TempCustomer = TempCustomer->NextCustomer;

}

break;

First, the program gets a pointer to the first member of the list and saves the pointer in FirstCustomer. When the first member of the linked list is obtained, it is displayed. The first member (and each following member, except the last one) has a pointer to the next member. The final member in the list has a pointer to NULL, which ends the while() loop. Just before the end of the while() loop, the pointer to the next customer record is assigned to TempCustomer. This allows the loop to display all the records.

The loop’s output depends on the program’s debug mode. In debug mode (used when the program is developed), the pointers are printed; otherwise, the names and addresses are printed.

366

Data Management: Sorts, Lists, and Indexes

C C C

 

10C

 

C C C

 

C C

With a linked list, it is easy to retrieve records in sorted order. Using multiple links, a program can retrieve records based on different criteria. A double linked list enables you to access the list in either forward order or backward order.

Linked lists do create a problem, however. The only way to access a specific member in the list is with a linear search. Because the list’s members may be located randomly in memory, the only access you usually have to the list’s members is to follow the chain of links. Therefore, finding a member in the middle of the list is not more efficient than finding a specific member in an unsorted list. Your program will know when the key field is greater than the member being tested, without searching the entire list. But you typically will be looking at approximately n/2 members (where n is the number of members in the list) to retrieve a specific member.

Indexing

Using an index to access data in a file is one way of gaining fast access to a large file of large data objects. Rarely can all of a user’s data fit in memory at one time, so you must use a file as temporary or permanent storage.

With an index, the program’s data is separated into two objects: the data and the index. The data is usually not arranged in a specific order; new records are added to the end of the block or the file. The index (there may be more than one index) is always sorted. It contains the minimum necessary to allow the program to access the data, typically a key value that the index is sorted on and a pointer to the corresponding data.

Figure 10.9 shows an indexed data file system that consists of a data file and two index files used to access the data. The records in this example are simple; many applications have thousands of bytes per record.

Each record in the data file is 183 bytes long. Each record contains a name, a company name, and an address that consists of the street, city, state, and ZIP code. The two index files are an index for the name field and an index for ZIP codes. Note that you cannot predict the order of records that do not have unique ZIP codes. In this example, either record with the ZIP code of 03468 could have been first.

The main factors for choosing an indexed data access system follow:

The main data file does not need to be sorted.

There can be more than one index, resulting in fast access to a given record.

Indexes can be created “on the fly,” as the need arises.

367

Part II • Managing Data in C

The ZIP code index in Figure 10.9 has only 13 bytes per record. These short records can be sorted more quickly than the 183-byte records that make up the entire file.

The INDEX.C program in Listing 10.5 creates an indexed structure. This program writes records to a data file and retains an index array in memory. The array is then used to access the records.

Figure 10.9. An indexed data file system.

Listing 10.5. INDEX.C.

/* INDEX, written 1992 by Peter D. Hipson

*This program shows indexed access to a file. It

*has better error checking than the CDB program in

*Chapter 8.

*/

#include <search.h> #include <string.h> #include <ctype.h> #include <stdio.h> #include <process.h> #include <stdlib.h>

#define TRUE

1

 

#define FALSE

(!TRUE)

#define INCREMENT_AMOUNT

1

/* Add one record at a time */

#define INDEX_SIZE

400

/* Maximum number of records */

#define CUSTOMER_RECORD

1

 

#define SUPPLIER_RECORD

2

 

368

Data Management: Sorts, Lists, and Indexes

C C C

 

10C

 

C C C

 

C C

/* Define the structure for the customer database. */

struct _CUSTNAME;

typedef struct

_CUSTNAME {

 

 

 

int

nRecordType;

//

1 == Customer record

struct

_CUSTNAME *NextCustomer; // Link to next, or NULL if none

struct

_CUSTNAME *PrevCustomer; // Link to previous, or NULL if none

char

szName[61];

//

60

chars for name; 1 for null at end

 

 

 

//

In some cases, you would not need to

 

 

 

//

duplicate this field in both the index and

 

 

 

//

the record.

char

szAddr1[61];

//

60

chars for address; 1 for null at end

char

szAddr2[61];

//

60

chars for address; 1 for null at end

char

szCity[26];

//

25

chars for city; 1 for null at end

char

szState[3];

//

2-char state abbreviation plus null

long

lZip;

//

Use integer. Print as %5.5d for leading 0

int

nRecordNumber;

//

Which record number?

double

dSalesTotal;

// Amount the customer has purchased

} CUSTNAME;

 

 

 

 

typedef CUSTNAME

far *FPCUSTNAME;

typedef CUSTNAME

near *NPCUSTNAME;

typedef CUSTNAME

*PCUSTNAME;

typedef struct _INDEXREC { char szName[61]; long Customer;

} CUSTINDEX;

//60 chars for name; 1 for null at end

//Pointer to customer record in file

typedef

CUSTINDEX

far

*FPCUSTINDEX;

typedef

CUSTINDEX

near

*NPCUSTINDEX;

typedef

CUSTINDEX

 

*PCUSTINDEX;

void

GiveHelp(void);

 

int

compare(const void *, const void *);

continues

369

Part II • Managing Data in C

Listing 10.5. continued

void

main()

{

 

 

FILE

 

*DataFile;

FILE

 

*IndexFile;

PCUSTNAME

FirstCustomer = NULL;

PCUSTNAME

LastCustomer = NULL;

PCUSTNAME

Customer = NULL;

PCUSTNAME

TempCustomer = NULL;

PCUSTINDEX

CustIndex = NULL;

PCUSTINDEX

pTempCustIndex = NULL;

CUSTINDEX

TempCustIndex;

char

 

szIndexFile[257];

char

 

szDataFile[257];

char

 

szBuffer[257];

/* Strings for _splitpath(), which parses a file name */

char

 

szDrive[_MAX_DRIVE];

char

 

szDir[_MAX_DIR];

char

 

szFname[_MAX_FNAME];

char

 

szExt[_MAX_EXT];

int

 

i;

int

 

nDesiredRecord;

int

 

nNotDone = TRUE;

int

 

nRecord = 0;

int

 

nDebug = FALSE;

int

 

nNeedSaving = FALSE;

long

 

lFilePosition;

double

 

dSales = 0.0; /* Forces the loading of floating-point support

 

 

*/

370

Data Management: Sorts, Lists, and Indexes

C C C

 

10C

 

C C C

 

C C

CustIndex = (PCUSTINDEX)calloc(sizeof(CUSTINDEX), INDEX_SIZE);

if (CustIndex == NULL)

{

fprintf(stderr, “Couldn’t allocate necessary index memory!\n”); exit(16);

}

memset(CustIndex, 0, sizeof(CUSTINDEX));

Customer = (PCUSTNAME)calloc(sizeof(CUSTNAME), INCREMENT_AMOUNT);

if (Customer == NULL)

{

fprintf(stderr, “Couldn’t allocate necessary record memory!\n”); exit(16);

}

printf(

“Please enter customer save file name-\n” “Extensions of .DAT and .IND will be used: “);

gets(szBuffer);

_splitpath(szBuffer, szDrive,

szDir,

szFname,

szExt);

strcpy(szIndexFile, szDrive); strcat(szIndexFile, szDir); strcat(szIndexFile, szFname); strcat(szIndexFile, “.IND”);

strcpy(szDataFile, szDrive); strcat(szDataFile, szDir); strcat(szDataFile, szFname); strcat(szDataFile, “.DAT”);

continues

371

Part II • Managing Data in C

Listing 10.5. continued

DataFile = fopen(szDataFile, “wb”);

if (DataFile == NULL)

{/* Test for file open. If file can’t be opened, exit with message. */

printf(“ERROR: Data file ‘%s’ couldn’t be opened.\n”, szDataFile);

exit(4);

}

fclose(DataFile);

IndexFile = fopen(szIndexFile, “wb”);

if (IndexFile == NULL)

{/* Test for file open. If file can’t be opened, exit with message. */

printf(“ERROR: Index file ‘%s’ couldn’t be opened.\n”, szIndexFile);

exit(4);

}

fclose(IndexFile);

printf(“Demo of an indexed file/array.\n” “\n”

“ Commands are:\n”

A

-

Add a customer/supplier record.\n”

D

- Display current list (from file).\n”

X

-

Exit from program.\n”

Z

-

Toggle debug mode.\n”

?

- Display the command list.\n”

H

- Display the command list.\n”

“\n”

 

 

 

);

 

 

 

nRecord = -1;

372

Data Management: Sorts, Lists, and Indexes

C C C

 

10C

 

C C C

 

C C

while (nNotDone)

{

printf(“Enter command?”);

gets(szBuffer);

switch(szBuffer[0])

{

case ‘H’: /* Give some help */ case ‘h’:

case ‘?’:

GiveHelp();

break;

case ‘A’: /* Add a record */ case ‘a’:

memset(Customer, 0, sizeof(CUSTNAME));

printf(“Enter name %d: “, ++nRecord); gets(szBuffer);

 

szBuffer[sizeof(Customer->szName) -

1] = ‘\0’;

 

strcpy(Customer->szName, szBuffer);

 

 

if (strlen(Customer->szName) > 0)

 

 

{/* Insert this

record in the list,

sorted by name. */

 

nNeedSaving

= TRUE;

 

//

Add to file

and index:

 

Customer->nRecordNumber = nRecord;

if (!nDebug)

{

do

{

printf(“Enter 1 for customer, 2 for supplier \ “);

continues

373

Part II • Managing Data in C

Listing 10.5. continued

gets(szBuffer);

sscanf(szBuffer, “%d”, &Customer- >nRecordType);

}

while (Customer->nRecordType != CUSTOMER_RECORD

&&

Customer->nRecordType != SUPPLIER_RECORD);

printf(“Enter address line 1: “); gets(szBuffer); szBuffer[sizeof(Customer->szAddr1) - 1] = ‘\0’; strcpy(Customer->szAddr1, szBuffer);

printf(“Enter address line 2: “); gets(szBuffer); szBuffer[sizeof(Customer->szAddr2) - 1] = ‘\0’; strcpy(Customer->szAddr2, szBuffer);

printf(“Enter City: “); gets(szBuffer);

szBuffer[sizeof(Customer->szCity) - 1] = ‘\0’; strcpy(Customer->szCity, szBuffer);

printf(“Enter state postal abbreviation: “); gets(szBuffer); szBuffer[sizeof(Customer->szState) - 1] = ‘\0’; strcpy(Customer->szState, szBuffer);

printf(“Enter ZIP code: “); gets(szBuffer);

sscanf(szBuffer, “%d”, &Customer->nZip);

printf(“Enter total sales: “); gets(szBuffer);

sscanf(szBuffer, “%f”, &Customer->dSalesTotal);

}

DataFile = fopen(szDataFile, “ab”);

if (DataFile == NULL)

{

374

Data Management: Sorts, Lists, and Indexes

printf(

“ERROR: Data file ‘%s’ couldn’t be “ “opened for update.\n”,

szDataFile);

exit(4);

}

C C C

C10C C

C C C

fseek(DataFile, 0, SEEK_END);

CustIndex[nRecord].Customer = ftell(DataFile); strcpy(CustIndex[nRecord].szName, Customer->szName);

printf(“Index %d ‘%s’ is at ‘%ld’\n”, nRecord,

CustIndex[nRecord].szName,

CustIndex[nRecord].Customer);

fwrite(Customer, sizeof(CUSTNAME), 1, DataFile);

fclose(DataFile);

}

else

{

printf(“\aSorry, name must not be blank!\n”);

}

break;

case ‘Z’: /* Debug mode toggle */ case ‘z’:

nDebug = !nDebug; break;

case ‘D’: /* Display a record */ case ‘d’:

printf(“Display customer (total %d).\n”, nRecord + 1);

qsort(CustIndex, nRecord + 1, sizeof(CUSTINDEX), compare);

continues

375