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

Advanced C 1992

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

Part III • Working with Others

Listing 12.2. continued

int main()

{

FILE *DBFile;

DB3HEADER

db3Header;

struct _DBRECORD {

char

sStatus[1]; /* Status does not count as a member */

char

sName[40];

char

sAddr1[40];

char

sAddr2[40];

char

sCity[20];

char

sState[2];

char

sZip[5];

} OurRecord;

char

*pOurRecord;

COLUMNDEF

ColumnDef[6]; /* Six members in OurRecord */

char

szBuffer[200]; /* Work buffer */

char

szTime[26];

char

szFileName[25];

int

i;

int

nColumnCount = 0;

int

nResult;

long

lCurrentRecord = 0;

double

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

struct tm

*NewTime;

time_t

aClock;

/* Step 1. Determine the layout of the columns (fields). In this * example, they are predefined. In other programs, you

486

C and Databases

C C C

 

12C

 

C C C

 

C C

*might determine the column layout by prompting the user

*or by examining the user’s data.

*/

printf(“Please enter new database name: “);

gets(szFileName);

DBFile = fopen(szFileName, “wb”);

if (DBFile == NULL)

{

fprintf(stderr,

“ERROR: File ‘%s’ couldn’t be opened.\n”, szFileName);

exit(4);

}

/* Step 2. Initialize and write the header record. */

time( &aClock );

NewTime = localtime(&aClock);

memset(&db3Header, 0, sizeof(db3Header));

/* Make it dBASE III-compatible */ db3Header.bfVersion = 3;

/* Make it a database with no memo fields */ db3Header.bfHasMemo = 0;

/* Set the date to

now, but UPDATE when closing */

/* because date may have changed */

db3Header.bYear = NewTime->tm_year;

db3Header.bMonth

= (unsigned char)(NewTime->tm_mon + 1);

db3Header.bDay

= (unsigned char)NewTime->tm_mday;

/* No records in the database yet */ db3Header.lNumberRecords = 0;

continues

487

Part III • Working with Others

Listing 12.2. continued

/* File header, plus column headers, plus a byte for the carriage return */

db3Header.nFirstRecordOffset = sizeof(DB3HEADER) + sizeof(ColumnDef) + 2;

/* Make it the size of a record in the database */ db3Header.nRecordLength = sizeof(OurRecord);

nResult = fwrite((char *)&db3Header, sizeof(DB3HEADER),

1,

DBFile);

if (nResult != 1)

{

fprintf(stderr, “ERROR: File ‘%s’, write error (Database \ header).\n”,

szFileName);

fclose(DBFile);

exit(4);

}

/* Step 3. Initialize the column headers using the information from step 1.*/

memset(ColumnDef, 0, sizeof(ColumnDef));

/* Do the following for each column, either inline or using a loop */

strcpy(ColumnDef[0].szColumnName, “Name”); ColumnDef[0].chType = CHARACTER_FIELD; ColumnDef[0].byLength = sizeof(OurRecord.sName); ColumnDef[0].byDecimalPlace = 0;

strcpy(ColumnDef[1].szColumnName, “Address1”); ColumnDef[1].chType = CHARACTER_FIELD; ColumnDef[1].byLength = sizeof(OurRecord.sAddr1);

488

C and Databases

C C C

 

12C

 

C C C

 

C C

ColumnDef[1].byDecimalPlace = 0;

strcpy(ColumnDef[2].szColumnName, “Address2”); ColumnDef[2].chType = CHARACTER_FIELD; ColumnDef[2].byLength = sizeof(OurRecord.sAddr2); ColumnDef[2].byDecimalPlace = 0;

strcpy(ColumnDef[3].szColumnName, “City”); ColumnDef[3].chType = CHARACTER_FIELD; ColumnDef[3].byLength = sizeof(OurRecord.sCity); ColumnDef[3].byDecimalPlace = 0;

strcpy(ColumnDef[4].szColumnName, “State”); ColumnDef[4].chType = CHARACTER_FIELD; ColumnDef[4].byLength = sizeof(OurRecord.sState); ColumnDef[4].byDecimalPlace = 0;

strcpy(ColumnDef[5].szColumnName, “Zipcode”); ColumnDef[5].chType = CHARACTER_FIELD; ColumnDef[5].byLength = sizeof(OurRecord.sZip); ColumnDef[5].byDecimalPlace = 0;

nResult = fwrite((char *)ColumnDef, sizeof(ColumnDef),

1,

DBFile);

if (nResult != 1)

{

fprintf(stderr, “ERROR: File ‘%s’, write error (Column \ headers).\n”,

szFileName);

fclose(DBFile);

exit(4);

}

/* Step 4. Write a carriage return (and a NULL) to meet dBASE standards */

continues

489

Part III • Working with Others

Listing 12.2. continued

nResult = fwrite((char *)”\x0D\0", sizeof(char) * 2,

1,

DBFile);

if (nResult != 1)

{

fprintf(stderr, “ERROR: File ‘%s’, write error (Column \ headers).\n”,

szFileName);

fclose(DBFile);

exit(4);

}

db3Header.nFirstRecordOffset = (int)ftell(DBFile);

/* Step 5. Get some records for the database. */

memset(&OurRecord, 0, sizeof(OurRecord));

printf(“Enter the name: (or no name to end)”); gets(szBuffer);

strncpy(OurRecord.sName, szBuffer, sizeof(OurRecord.sName));

while (strlen(szBuffer) > 0)

{

OurRecord.sStatus[0] = USABLE_RECORD;

printf(“Enter address line 1: “); gets(szBuffer);

strncpy(OurRecord.sAddr1, szBuffer, sizeof(OurRecord.sAddr1));

printf(“Enter address line 2:”); gets(szBuffer);

strncpy(OurRecord.sAddr2, szBuffer, sizeof(OurRecord.sAddr2));

printf(“Enter city:”);

490

 

C and Databases

C C C

 

 

12C

 

 

C C C

 

gets(szBuffer);

C C

 

 

 

strncpy(OurRecord.sCity, szBuffer, sizeof(OurRecord.sCity));

 

 

printf(“Enter state (2 characters only):”);

 

 

gets(szBuffer);

 

 

strncpy(OurRecord.sState, szBuffer, sizeof(OurRecord.sState));

 

printf(“Enter Zipcode:”);

 

 

gets(szBuffer);

 

 

strncpy(OurRecord.sZip, szBuffer, sizeof(OurRecord.sZip));

 

/*

dBASE records do not contain NULLs, but are padded with

 

*

blanks instead, so we convert the NULLs to spaces

 

*/

pOurRecord = (char *)&OurRecord;

for (i = 0; i < sizeof(OurRecord); i++)

{

if (pOurRecord[i] == ‘\0’)

{

pOurRecord[i] = ‘ ‘;

}

}

nResult = fwrite((char *)&OurRecord, sizeof(OurRecord),

1,

DBFile);

if (nResult != 1)

{

fprintf(stderr, “ERROR: File ‘%s’, write error (Column \ headers).\n”,

szFileName);

fclose(DBFile);

exit(4);

}

else

{

continues

491

Part III • Working with Others

Listing 12.2. continued

++db3Header.lNumberRecords;

}

memset(&OurRecord, 0, sizeof(OurRecord));

printf(“Enter the name: (or no name to end)”); gets(szBuffer);

strncpy(OurRecord.sName, szBuffer, sizeof(OurRecord.sName));

}

/* Step 6. Update the file header with the current time and * the number of records.

*/

time( &aClock );

NewTime = localtime(&aClock);

/* Set the date to

now */

db3Header.bYear = NewTime->tm_year;

db3Header.bMonth

= (unsigned char)(NewTime->tm_mon + 1);

db3Header.bDay

= (unsigned char)NewTime->tm_mday;

/* The number of

records is already set */

nResult = fseek(DBFile, (long)0, SEEK_SET);

if (nResult != 0)

{

fprintf(stderr, “ERROR: File ‘%s’, seek error (rewrite of \ header).\n”,

szFileName);

fclose(DBFile);

exit(4);

}

nResult = fwrite((char *)&db3Header, sizeof(DB3HEADER),

492

C and Databases

C C C

 

12C

 

C C C

 

C C

1,

DBFile);

if (nResult != 1)

{

fprintf(stderr, “ERROR: File ‘%s’, write error (Database header \ rewrite).\n”,

szFileName);

fclose(DBFile);

exit(4);

}

/* Finished. Close the file and end the program. */

fclose(DBFile);

return(0);

}

Your program that creates a dBASE file must do the following, in order:

1.Determine the record layout. This may have been already defined by the application’s requirements, or you may be using information supplied by the user. Each field must have a type and a length.

2.Initialize and write a dBASE file header (our DB3READ structure). You must initialize the record size (sum of fields plus the leading record deleted byte), the pointer to the first record, usually the byte after the end of header byte (see step 4), the date, and the time.

3.Initialize and write the field headers (our COLUMNDEF structure). Be sure you specify the field’s name, length, and type.

4.Write the end of the header byte (0x0D); following the header byte with a NULL byte (0x00) is optional.

5.Write the records placed in the file at creation time. Be sure the deleted flag is set to blank so that the initial records are not deleted.

493

Part III • Working with Others

6.Seek to the beginning of the file and reset the file header’s number of records count, date, and time. (The date and time are reset because they have changed since they were last written.) Rewrite the file header.

Each of these six steps were followed in DBWRITE.C and are indicated by comments in the program.

Updating dBASE and dBASE-Compatible Files

When you update a dBASE file, I suggest that you update a copy of the original file. If you update the original file, you risk damaging the original file if your program crashes. I never work on the only copy of a file. Instead, I copy the original .DBF file to a .BAK file or a temporary work file.

The steps for updating a dBASE file follow:

1.Read the file header and the column (field) header records.

2.Read the file’s data records if necessary. Append to the file any new records that the user creates. If the user is deleting a record, mark it with the * delete flag in the first column (the deleted field) of the data record. Modify records as necessary. When modifying a record, many programs mark the original record as deleted, then make the changed record a new record. Although this technique is acceptable, the database file may grow excessively if many records are changed.

3.Write the updated file header record with a new date and record count.

Using a dBASE-compatible file structure increases your program’s flexibility because the user can use other utilities (including a database program) to manipulate the data files.

Summary

In this chapter, you learned about interfacing C with database programs and with files created by dBASE-compatible programs.

Database programs are one of the most common computer applications.

The most common database file format is the dBASE III format. It is simple and easy to use.

494

C and Databases

C C C

 

12C

 

C C C

 

C C

A dBASE III .DBF file can be read using two structures, as shown in the chapter.

A dBASE III file can be created using simple C functions.

A .DBF file can contain deleted records. It is possible to write a program that recreates the database and undeletes the deleted records—if the database has not been packed and the space that the deleted records occupied has not been lost.

495