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

Advanced C 1992

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

Part III • Working with Others

Listing 12.1. continued

long

lCurrentRecord = 0;

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

printf(“sizeof(DB3HEADER) = %d\n”, sizeof(DB3HEADER)); printf(“sizeof(COLUMNDEF) = %d\n”, sizeof(COLUMNDEF));

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

gets(szFileName);

DBFile = fopen(szFileName, “rb”);

if (DBFile == NULL)

{

fprintf(stderr,

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

exit(4);

}

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

1,

DBFile);

if (nResult != 1)

{

if (!feof(DBFile))

{

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

szFileName);

fclose(DBFile);

exit(4);

}

476

C and Databases

C C C

 

12C

 

C C C

 

C C

else

{

fprintf(stderr, “Unexpected end of database file ‘%s’.\n”, szFileName);

fclose(DBFile);

exit(4);

}

}

if (db3Header.bfHasMemo)

{

printf(“There is a .DBT memo\n”);

}

else

{

printf(“There is no a .DBT memo\n”);

}

printf(“Created with version %d of dBASE. \n”, db3Header.bfVersion);

if (db3Header.bfVersion != 3 && db3Header.bfVersion != 4)

{

printf(“The version of dBASE that created this file “ “may not be compatible.\n”);

}

printf(“Updated last on: %d/”, db3Header.bMonth); printf(“%d”, db3Header.bDay);

printf(“ 19%d\n”, db3Header.bYear);

printf(“There are %ld records in the database. \n”, db3Header.lNumberRecords);

printf(“The first record starts at byte %d. \n”, db3Header.nFirstRecordOffset);

printf(“Each record is %d bytes long. \n”, db3Header.nRecordLength);

continues

477

Part III • Working with Others

Listing 12.1. continued

printf(“The reserved field contains ‘%s’ \n”, db3Header.szReserved);

nColumnCount =

(db3Header.nFirstRecordOffset - sizeof(DB3HEADER)) / sizeof(COLUMNDEF);

printf(“There are %d columns in each record. \n”, nColumnCount);

/* Now allocate memory for each of the column definitions: */

ColumnDef = (COLUMNDEF *)calloc(sizeof(COLUMNDEF), nColumnCount);

if (ColumnDef == (COLUMNDEF *)NULL)

{

fprintf(stderr,

“Couldn’t allocate memory for the column definitions \n”);

fclose(DBFile);

exit(4);

}

nResult = fread((char *)ColumnDef, sizeof(COLUMNDEF), nColumnCount,

DBFile);

if (nResult != nColumnCount)

{

if (!feof(DBFile))

{

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

szFileName);

fclose(DBFile);

exit(4);

}

478

C and Databases

else

{

fprintf(stderr, “Unexpected end of database file ‘%s’” “ while reading column definitions.\n”, szFileName);

fclose(DBFile);

exit(4);

}

}

printf(“Column definitions: \n”);

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

{

printf(“Name: ‘%10.10s’ “, ColumnDef[i].szColumnName);

switch(ColumnDef[i].chType)

{

case NUMERIC_FIELD: /* Number field */ printf(“ (Numeric)\n”);

break;

case CHARACTER_FIELD: /* Character field */ printf(“ (Character)\n”);

break;

case LOGICAL_FIELD: /* Logical (using ‘Y’, ‘N’, etc */ printf(“ (Logical)\n”);

break;

case MEMO_FIELD: /* Memo Index field */ printf(“ (Memo file .DBT Index)\n”); break;

case DATE_FIELD: /* Date in YYYYMMDD format */ printf(“ (Date in YYYYMMDD)\n”);

break;

C C C

C12C C

C C C

continues

479

Part III • Working with Others

Listing 12.1. continued

case FLOAT_FIELD: /* Floating point field */ printf(“ (Floating point)\n”);

break;

case PICTURE_FIELD: /* Date in YYYYMMDD format */ printf(“ (Picture format)\n”);

break;

default: /* Unknown type of field */ printf(“ (Field type unknown)\n”); break;

}

printf(“Length: %d\n”, ColumnDef[i].byLength); printf(“DecimalPoint: %d\n”, ColumnDef[i].byDecimalPlace); printf(“Reserved ‘%s’\n”, ColumnDef[i].szReserved);

}

/* Next allocate the buffer to hold a database record

*We add a byte for the terminating \0, which is not supplied by

*dBASE as part of the record.

*/

pBuffer = (unsigned char *)calloc(sizeof(char), db3Header.nRecordLength + 1);

if (pBuffer == (unsigned char *)NULL)

{

fprintf(stderr,

“Couldn’t allocate memory for the column buffer\n”);

fclose(DBFile);

exit(4);

}

nResult = fseek(DBFile, (long)db3Header.nFirstRecordOffset, SEEK_SET);

if (nResult != 0)

480

C and Databases

{

if (!feof(DBFile))

{

fprintf(stderr, “ERROR: File ‘%s’, seek error.\n”, szFileName);

fclose(DBFile);

exit(4);

}

else

{

fprintf(stderr, “Unexpected end of database file ‘%s’” “ while reading record.\n”,

szFileName);

fclose(DBFile);

exit(4);

}

}

C C C

C12C C

C C C

for (lCurrentRecord = 0;

lCurrentRecord < db3Header.lNumberRecords; ++lCurrentRecord)

{

nResult = fread((char *)pBuffer, db3Header.nRecordLength, 1, DBFile);

if (nResult != 1)

{

if (!feof(DBFile))

{

fprintf(stderr, “ERROR: File ‘%s’, read error \ (records).\n”,

szFileName);

fclose(DBFile);

exit(4);

}

else

continues

481

Part III • Working with Others

Listing 12.1. continued

{

fprintf(stderr, “Unexpected end of database file ‘%s’” “ while reading records.\n”,

szFileName);

 

fclose(DBFile);

 

exit(4);

 

}

 

}

 

pBuffer[db3Header.nRecordLength] = ‘\0’;

/*

Where we print inside the switch, a program that would use the

*database records would parse, and read each field, probably

*using a built format string and a call to sscanf().

*/

switch(pBuffer[0])

{

case USABLE_RECORD: /* Valid, undeleted record */ printf(“Record ‘%s’\n”, &pBuffer[1]);

break;

case DELETED_RECORD: /* Record has been deleted. Usually, you’d ignore it. */

printf(“Deleted ‘%s’\n”, &pBuffer[1]); break;

default: /* The record’s status is unknown */ printf(“Unknown ‘%s’\n”, &pBuffer[1]); break;

}

}

return(0);

}

482

C and Databases

C C C

 

12C

 

C C C

 

C C

In DBREAD, first the file is opened, then the file header is read into the DB3HEADER structure. The information placed in this structure is used to read the column definitions and the data records.

Next, the program allocates the memory for the column definitions by computing the number of columns:

nColumnCount =

(db3Header.nFirstRecordOffset - sizeof(DB3HEADER)) / sizeof(COLUMNDEF);

The calloc() function uses the nColumnCount variable to allocate the required memory. The column definitions are saved for later use.

ColumnDef = (COLUMNDEF *)calloc(sizeof(COLUMNDEF), nColumnCount);

After the memory is allocated, the column definitions are read. A loop is not necessary; the program uses one read in which the number of bytes is computed from the size of the structure and the number of columns in the database:

nResult = fread((char *)ColumnDef, sizeof(COLUMNDEF), nColumnCount,

DBFile);

After all the columns have been read (determined by the return value from a call to the fread() function), the program can process them as required. In this simple example, the information is printed to the screen. A for() loop is an easy and effective way to process the columns:

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

{

printf(“Name: ‘%10.10s’ “, ColumnDef[i].szColumnName);

When the format of the records has been determined, the program can process the records in the dBASE file. First, a buffer must be allocated to hold the records. The buffer’s size is known (or can be computed from the size of each record, not forgetting the byte for the record’s status):

pBuffer = (unsigned char *)calloc(sizeof(char), db3Header.nRecordLength + 1);

Because we do not use dBASE’s index files, we accept the records in the order that they are stored in the file, using a simple for() loop. Before reading the records, I recommend that you do a seek to the known point where the first record can be found.

483

Part III • Working with Others

This is necessary because dBASE adds a carriage return (0x0D) after the column definitions and may well add another extra byte. This point is found in the file header,

in the nFirstRecordOffset member of the DB3HEADER structure.

nResult = fseek(DBFile, (long)db3Header.nFirstRecordOffset, SEEK_SET);

The program is now at the position of the first record in the dBASE file, so it can read each record:

for (lCurrentRecord = 0;

lCurrentRecord < db3Header.lNumberRecords; ++lCurrentRecord)

{

nResult = fread((char *)pBuffer, db3Header.nRecordLength, 1, DBFile);

As each record is read, the first byte tells you the status of the record. If the first byte is a blank, the record is not deleted. If the byte is *, the record has been deleted and should not be processed (unless you are writing a deleted record processor!).

Creating dBASE and dBASE-Compatible Files

With a little care, you can read a dBASE file or create a dBASE-compatible file without difficulty. The process of creating a dBASE-compatible file is basically the reverse of reading a file.

Listing 12.2, DBWRITE.C, is a simple program that creates a simple dBASE IIIcompatible .DBF file. This file can be read by any program that can read dBASE files.

Listing 12.2. DBWRITE.C.

/* DBWRITE, written 1992 by Peter D. Hipson */

/* This program creates a dBASE-compatible file. */ /* Derived from DBREAD.C */

#include <stdio.h> #include <stddef.h> #include <stdlib.h> #include <string.h> #include <time.h>

484

C and Databases

/* Some defines that are useful for dBASE files: */

/* Record status defines (first byte of each record) */ #define DELETED_RECORD ‘*’

#define USABLE_RECORD ‘ ‘

/* Field (column) definitions (capital letters, please) */

#define NUMERIC_FIELD

‘N’

#define CHARACTER_FIELD

‘C’

#define LOGICAL_FIELD

‘L’

#define MEMO_FIELD

‘M’

#define DATE_FIELD

‘D’

#define FLOAT_FIELD

‘F’

#define PICTURE_FIELD

‘P’

/* End of dBASE defines */

#pragma pack(1) /* Pack to byte boundaries */

C C C

C12C C

C C C

typedef struct {

/* Bit fields are

arranged from least significant to most significant */

unsigned

int

bfVersion:7;

unsigned

int

bfHasMemo:1;

unsigned

int

bYear:8;

unsigned

char

bMonth;

unsigned

char

bDay;

long int

 

lNumberRecords;

short int

 

nFirstRecordOffset;

short int

 

nRecordLength;

unsigned

char

szReserved[20];

} DB3HEADER;

 

typedef struct {

 

char

 

szColumnName[11];

char

 

chType;

long

 

lFieldPointer;

unsigned

char

byLength;

unsigned

char

byDecimalPlace;

char

 

szReserved[14];

} COLUMNDEF;

 

continues

485