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

Advanced C 1992

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

Part II • Managing Data in C

Listing 10.5. continued

for (i = 0; nDebug && i <= nRecord; i++)

{/* In debug mode, display the sorted index list. */ printf(“Record %2d szName ‘%s’\n”,

i,

CustIndex[i].szName);

}

memset(Customer, 0, sizeof(CUSTNAME)); memset(&TempCustIndex, 0, sizeof(CUSTINDEX));

printf(“Enter name”); gets(TempCustIndex.szName);

printf(“Searching with a linear search\n”);

nDesiredRecord = -1;

for (i = 0; i <= nRecord; i++)

{/* Linear search; could be bsearch() */ if (stricmp(TempCustIndex.szName,

CustIndex[i].szName) == 0)

{

nDesiredRecord = i; break;

}

}

if (nDesiredRecord >= 0)

{

DataFile = fopen(szDataFile, “rb”);

if (DataFile == NULL)

{

printf(

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

szDataFile);

376

Data Management: Sorts, Lists, and Indexes

C C C

 

10C

 

C C C

 

C C

exit(4);

}

fseek(DataFile, CustIndex[nDesiredRecord].Customer, SEEK_SET);

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

printf(

“Name ‘%10s’ City ‘%10s’ State ‘%2s’ “ “ZIP ‘%5.5d’\n”,

Customer->szName,

Customer->szCity,

Customer->szState, Customer->nZip);

fclose(DataFile);

}

else

{

printf(“LINEAR SEARCH: Sorry, the name ‘%s’ couldn’t \ be found\n”,

TempCustIndex.szName);

}

printf(“Searching with a binary search\n”);

if ((pTempCustIndex = (PCUSTINDEX)bsearch(&TempCustIndex, CustIndex,

nRecord + 1, sizeof(CUSTINDEX), compare)) != NULL)

{

DataFile = fopen(szDataFile, “rb”);

if (DataFile == NULL)

{

printf(

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

szDataFile);

continues

377

Part II • Managing Data in C

Listing 10.5. continued

exit(4);

}

fseek(DataFile, pTempCustIndex->Customer, SEEK_SET);

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

printf(

“Name ‘%10s’ City ‘%10s’ State ‘%2s’ “ “ZIP ‘%5.5d’\n”,

Customer->szName,

Customer->szCity,

Customer->szState, Customer->nZip);

fclose(DataFile);

}

else

{

printf(“BSEARCH: Sorry, the name ‘%s’ couldn’t be \ found\n”,

TempCustIndex.szName);

}

break;

case ‘X’: /* Exit; prompt for save if needed. */ case ‘x’:

nNotDone = FALSE;

szBuffer[0] = ‘\0’;

while (nNeedSaving && szBuffer[0] == ‘\0’)

{

printf(“\nSave the data? (y|n)”);

378

Data Management: Sorts, Lists, and Indexes

gets(szBuffer);

if (szBuffer[0] == ‘n’ || szBuffer[0] == ‘N’)

{

nNeedSaving = FALSE;

}

else

{

if (szBuffer[0] != ‘y’ && szBuffer[0] != ‘Y’)

{

printf(“\nWrong answer, “

“please respond with ‘y’ or ‘n’”);

szBuffer[0] = ‘\0’;

}

}

}

C C C

C10C C

C C C

if (!nNeedSaving)

{/* Don’t need to save, so just exit */ break;

}

/*

Else fall through to the save routines */

case ‘S’: /* Save all records */ case ‘s’:

printf(“Saving customer index file.\n”);

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

continues

379

Part II • Managing Data in C

Listing 10.5. continued

}

else

{

fwrite(CustIndex,

sizeof(CUSTINDEX) * (nRecord + 1), 1,

IndexFile);

fclose(IndexFile);

nNeedSaving = FALSE;

}

break;

default:

printf(“\aUnknown operation ‘%c’\n”, szBuffer[0]);

break;

}

}

}

int compare( PCUSTINDEX CustIndex1, PCUSTINDEX CustIndex2)

{

//Uncomment the following printf() to see how qsort and qsearch work.

//printf(“Comparing %s and %s\n”,

//CustIndex1->szName,

//CustIndex2->szName);

return(stricmp(

((PCustIndex) CustIndex1)->szName, ((PCustIndex) CustIndex2)->szName));

}

380

Data Management: Sorts, Lists, and Indexes

C C C

 

10C

 

C C C

 

C C

void GiveHelp()

{

printf(

“\n”

“This program shows how an indexed file list is created and\n” “used. It enables you to add records, display a specified\n” “record, and save the list of records to the disk file.\n” “\n”

“INDEX supports the following commands:\n”);

printf(

 

“\n”

 

A - Add a customer/supplier record.\n”

Adds a record. Each added record is placed\n”

in the list in the correct order.\n”);

printf(

“\n”

D - Display current list.\n”

Prints the user-specified record. This\n”

command lists the name and address\n”

information, assuming the name has been found.\n”);

printf(

“\n”

X - Exit from program.\n”

Ends the program. If records or the index have\n”

not been saved, will prompt for save. All saves \ are\n”

made to the file specified when the program was \ started.\n”);

printf(

 

“\n”

 

Z - Toggle debug mode.\n”

Changes the information displayed for the\n”

user. When on, debug mode shows the sorted\n”

index list.\n”);

continues

381

Part II • Managing Data in C

Listing 10.5. continued

printf(

 

 

 

“\n”

 

 

 

?

-

Display the command list.\n”

H

-

Display the command list.\n”

Displays this help information.\n”);

printf(

“\n”

S - Save the list.\n”

If records and the index have not been saved, this \ option\n”

saves the records the user has entered. All saves \ are made\n”

to the file specified when the program was \ started.\n”);

printf(

“Additional feature: In this program includes a prompt\n” “to save when the exit command is given. (This prompt\n”

“is given only when the records have not been saved since\n” “the last added record).\n”);

printf(

“Additional feature: This program has a debug mode so that\n” “the user can see how the program works. This debug mode \

allows\n”

“the user to print the linked list and its pointers.\n”);

}

First the CUSTNAME structure (which is identical to the structure used in many of the other example programs) is defined. Then an index on the customer’s name is defined.

In general, the field you are indexing on should be unique (although this is not a requirement). When you use the index to retrieve records, a unique field ensures that only one name is returned for each requested search, which can make your program simpler because is does not have to process multiple matches.

382

Data Management: Sorts, Lists, and Indexes

C C C

 

10C

 

C C C

 

C C

The definition of the index structure follows:

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

} CUSTINDEX;

//60 chars for name, 1 for null at end.

//Pointer to actual customer record in file.

typedef CUSTINDEX

far

*FPCUSTINDEX;

typedef

CUSTINDEX

near

*NPCUSTINDEX;

typedef

CUSTINDEX

 

*PCUSTINDEX;

Pointers are defined for the structure, like any other typedef’d structure. A compare() function is also defined for use when sorting (and searching) the index. The advantage of an indexed file is that it is always sorted. However, you can avoid resorting the entire index when a record is added or an index field is changed. A typical trick is to retain the existing records in the sorted index, and when a record is added or changed, add it to a special area at the end of the index. If a binary search of the sorted portion of the index does not find the record, a linear search is used on the special section of nonsorted records. When the count of records in the unsorted section exceeds a predetermined value, the program re-sorts the index.

Linear Search Versus Binary Search

A linear search starts with the first record in the list or file, and reads each record until it finds a match to the key or the file ends. In a linear search, the list or file does not need to be sorted, and the records in the list do not have to be in any particular order.

A binary search proceeds as follows:

1.The binary search starts with the middle item in the list. If the key is less than the selected item, the binary search takes the item halfway between the current item and the beginning of the list. If the key is greater than the selected item, the binary search takes the item halfway between the current item and the end of the list. With one comparison, the binary search eliminates half of the file.

2.If the key is less than the item found in step 1, the half less than the item is selected. If the key is greater than the item found in step 1, the half that is

383

Part II • Managing Data in C

greater than the item is selected. Of this half, the middle item is then again selected.

3.This process is repeated until a match is found or it is shown that the key is not part of the list.

For example, suppose the key (the item you want to find) is 5. Your list contains the following numbers: 1, 2, 5, 12, 23, 24, 34, 35, 38, 45, 47, 50, 60, 65, 66, 76, 78, and 80. The first selection is 38 (the middle item in the list). Because 5 is less than 38, the next selection is 23, (halfway between 1 and 38). Because 5 is smaller than 23, the next selection is 5 (halfway between 1 and 23). This is a match, so the search stops.

The maximum number of comparisons with a binary search is small—in a file of 65,000 items, at most only 16 comparisons must be made. With a linear search, an average of 32,000 comparisons are required.

The winner? A binary search is always the winner when the list can be (or is) sorted. If the list cannot be sorted, a linear search must be performed.

Fortunately, the C compiler provides a binary search function called bsearch(). This function requires a sorted list and the address of a compare function. The bsearch() and bsort() functions use the same compare function, so that only one compare function needs to be written when using either bsearch() or bsort(). In our sorting and searching, we are working with an array of index records, and these index records are what must be dealt with by the compare function. Because with bsort() and bsearch() the compare is passed the address of the array members, the compare function is defined as accepting two pointers to CUSTINDEX structures.

int

compare(const void *, const void*);

When the user wants to add a record to the customer database, the program first uses memset() to clear the Customer structure. It then prompts for the name. If the user enters a name, the program processes it.

The code for adding a record follows:

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

384

Data Management: Sorts, Lists, and Indexes

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

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

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

C C C

C10C C

C C C

 

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

 

{/* Insert this

record in the list, sorted by name. */

 

nNeedSaving

= TRUE;

//

Add to file

and index:

Customer->nRecordNumber = nRecord;

To add the record to the database, the program first opens the database file. The file is closed when it is not in use so that the database is as safe as possible if the computer fails. If you do not close files in your program, you should at least call fflush() after every write to the file.

The file is opened in the append mode so that existing records are not lost. If the file were opened in the write mode, the operating system would delete the contents of the file.

DataFile = fopen(szDataFile, “ab”);

if (DataFile == NULL)

{

printf(

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

szDataFile);

exit(4);

}

After the file is opened, the program goes to the end of the file:

fseek(DataFile, 0, SEEK_END);

The ftell() function returns the current file pointer for the record that will be added. This value is assigned to the index array’s pointer to this record. Next, strcpy() copies the key into the index array:

385