📄 cdbfile.txt
字号:
* SortAllRecords() operates a quicksort algorithm on the
record list. The sorting is done on the criterium Criter1 ,
which points to a field descriptor: the list will be sorted in
increasing order of the Criter1 field of the records.
The public functions are in fact a convenient way to embed the private
functions without requiring the user to manipulate those ugly record lists
or structures (yuck!), let alone the field descriptors (ugh!). He just has
to instantiate a CDBFile object in his program, and mind his own business.
All he has to know about how that object works is that there is a special
pointer CurrentRec (see above) which points at the record he wants
to access or to modify, and that CurrentRec can be moved all along
the list very easily, in an iterative way. Here are the public functions that will
enable the programmer to do so :
class CDBFile
...
public:
unsigned long LoadFileToMemory();
unsigned long WriteModified();
void SortOn(unsigned short Criterium1);
void GetAtRecord(unsigned long RecordNum)
CurrentRec=GetRecord(RecordNum);
unsigned long GetRecordNum()
if (CurrentRec) return CurrentRec->RecordNumber;
else return 0L;
BOOL GetNextRecord()
{ if (CurrentRec) { CurrentRec=CurrentRec->Next; return TRUE;}
else return FALSE; }
BOOL GetPreviousRecord()
{ if (CurrentRec) { CurrentRec=CurrentRec->Previous; return TRUE;}
else return FALSE; }
void LoadRecord(unsigned long RecordNum)
CurrentRec=ReadRecord(RecordNum); Append(CurrentRec);
void DeleteCurrentRec()
CurrentRec=CurrentRec->Previous; DeleteRecord(CurrentRec->Next);
void CreateAndAppend()
CurrentRec=CreateNewRecord(); Append(CurrentRec);
void ClearAllRecords();
...
;
For most of those functions, the souce code is self-explanatory. Some
others are less obvious: SortOn() , for example, launches the
recursive quicksort function SortAllRecords() (see above).
LoadFileToMemory() reads all the records on the file using
ReadRecord() and stores the resulting records in the list.
Wri te Mo di fied() checks all the records in the list and writes some
of them to the file if they have been edited or modified.
ClearAllRecords() empties the list and deletes all the records.
Some more complex functions also involve manipulating the contents of those
records ( SortAllRecords() is one of those), or the DBF file. They
will be explained in the next paragraphs.
3.4.2 Handling the contents of the records
We have seen the structure of the records in the previous section; let's
have a look at the functions that can access the contents of those records.
class CDBFile
...
private:
void* GetFieldValue(Record* Rec, CField* Field);
void SetFieldValue(Record* Rec, CField* Field, void* Value);
BOOL IsBigger(void *v1, void *v2, CField* Criterium);
BOOL IsSmaller(void *v1, void *v2, CField* Criterium);
...
;
The first one is GetFieldValue() . That function takes two
arguments: a pointer to the record that is being accessed and a pointer
to the field descriptor (the field that contains the accessed value).
Knowing the offset and the length of the field within the contents string,
the function first extracts the corresponding substring. Then it checks the
type of the field. If the field is of type 'N' (numeric value),
there are two cases: if its decimal count is null, then the substring will
be converted into a long integer value; if not, it will be
converted into a double floating-point value. If the field is of
type 'L' (logical value), the substring will be converted into a
boolean value (defined as BOOL in that package, and in
Micro$oft's Visual C++). In other cases, if the field is of type
'C' (character string) or 'D' (date), or even
'M' (Memo number for .DBT blocks) , there will be no
conversion, the character substring will be kept `as is'. Maybe some
specific conversions would be useful, particularly for dates or memo
block numbers, but I did not need those functionalities for my own
application. It's up to you to add more conversions, in respect of the GPL,
of course.
In all cases, the result of the conversion will be put in a
dynamically allocated pointer of the corresponding type, and that pointer
will be returned as a void pointer. This is the inelegant aspect
of the function: the programmer has to know exactly what kind of data he is
receiving in that void pointer, and he also has to delete that pointer
(it has to be deleted, since it has been allocated dynamically). If
he is using a fixed type of records (for a specific application, an address
book for example) this will not be a problem. But if the type of records
can vary in that application (a generic spreadsheet application, for
example), the programmer will probably have to verify the type of the data,
using public functions such as GetFieldType() (see below).
The same canvas applies to SetFieldValue() , the other way round:
when calling that function, a void pointer containing
the value to be set is passed, along with a pointer to the field
descriptor. The function then converts the contents of the void
pointer to the right substring, and copies it to the contents of the
record.
The other two functions, IsBigger() and
IsSmaller() , use Get Fi eld Va lue() to compare two records
on the basis of their respective values for a given field. They are called by
SortAllRecords() only so far.
The public functions that can be used by the programmer are the
following:
[1]
class CDBFile
...
public:
void SetFieldValue(char* Field, void* Value);
void SetFieldValue(unsigned short FieldNum, void* Value);
void* GetFieldValue(char* Field);
void* GetFieldValue(unsigned short FieldNum);
char GetFieldType(unsigned short NumField)
return (FirstField->GetField(NumField))->GetType();
void DeleteVoidPointer(void* Pointer, unsigned short Field);
void DeleteVoidPointer(void* Pointer, char* Field);
...
;
The overloaded and public versions of SetFieldValue() and
GetFieldValue() are the equivalent of their private versions,
but they use either the name or the number of the field to refer to it.
GetFieldType() enables the programmer to know the type of the
field referenced by NumField . I also chose to create
DeleteVoid Pointer() so that the programmer can delete
easily the void pointers he receives from calls to
GetFieldValue() .
That's all about handling the contents of the records, let's have
a look now at how the files are handled.
3.4.3 Handling the files
As I said earlier on, I did not implement the possibility to create
a DBF file from scratch, adding fields in the field descriptor ring and the
records or removing them. Therefore, one has to open an existing DBF file
first, then one can only add, edit or remove records from that file, and
save it or save it as a new file (with another pathname).
When referring to Section , the header of the file
contains a few specific parameters that describe the characteristics of the
file. They are implemented in CDBFile as private member variables:
class CDBFile
...
private:
char PathName[256]; // Path name for the dBase III file
FILE *FileHandle; // Handler for the dBase III file
unsigned short HeaderSize; // Length of header structure
unsigned short FieldCount; // Number of fields in each record
unsigned long RecordCount; // Number of records in the file
BOOL ModifiedFlag; // TRUE if the data has been modified
BOOL FullFileInMemory; // TRUE if the entire file is loaded
unsigned short RecordLength; // Length of the record strings
Record* RecordList; // Head of the list of records
Record* CurrentRec; // Current record pointed in the list
CField* FirstField; // Head of the list of fields
...
;
Some of them can be accessed and modified directly by the programmer,
using dedicated public functions; most of them, however, are only accessed
and processed internally by the member functions of the package, since the
programmer should not need to read or modify them in any way. Here are the
few functions that enable the programmer to do so:
class CDBFile
...
public:
BOOL IsOpen()
return (BOOL)(FileHandle!=NULL);
unsigned long GetRecordCount()
return RecordCount;
unsigned short GetFieldCount()
return FieldCount;
...
;
The other functions perform bigger tasks, of a higher level. Here they
are:
class CDBFile
...
public:
CDBFile();
CDBFile(char* Path);
CDBFile();
BOOL Clean();
BOOL OpenFile(char* Path);
BOOL CloseFile();
unsigned long WriteAllToFile(char *Path=NULL);
private:
BOOL WriteHeader(char* Path=NULL);
...
;
The only private function here is used to rewrite the file header when
some of its information has changed. It is also used to write the modified
file under a new pathname.
The public function that uses Write Header() is
Write All To File() . That function is used only when the entire file has
been loaded into memory and has to be rewritten or saved under a new
pathname. Note that it contains non-portable time-encoding functions, which
are specifically DOS-oriented, and will cause trouble when ported to
UNIX / Linux. See the code in "cdbfile.cpp" for more details.
The other public functions perform the following tasks:
* CDBFile() is the default constructor, whereas
CDB File(char* Path creates the object and opens the file known as
Path ;
* CDBFile() is the default destructor;
* Clean() resets (or initializes) the contents of the
CDBFile object, and is cal led both by the constructors and the
destructor;
* OpenFile(char* Path) first resets the object and then opens
a new file under Path ; that function also sets most of the
data members to the values mentioned in the file, and particularly,
it builds the field descriptor ring.
* CloseFile() simply closes the file and resets all the data.
That's all for the description of the code. Again, if you need more
details, RTFC ( Read The F***ing Codefiles! ).
5. An example: the TestDBF file editor
This is an elementary C-style console application, proposing a few
tools to edit DBF files. It does not demonstrate all of the
possibilities of CDBFile , but it shows you most of them. Also,
note that not all the shortkeys are displayed on the top line of the
console when you launch the program. This is a typically user-unfriendly
(but hopefully hacker/programmer-friendly) application.
If you take a look at the source code (
"testdbf.cpp" ), you will have a better idea of how the CDBFile
objects and its methods should be used within a program. The
"case 'e' :" part of the main() function particularly deserves
your attention: it shows how to implement a generic record editor using
that package (which is non-trivial).
I will not comment more on that application: the best thing for you
is definitely to have a look at the source code. Hmmm...
Didn't I say that earlier on? I'm not sure... :-)
6. Conclusion
Well, that's all for the time being. I'm not quite sure that I will have time
enough to support that package in the next few months, but I will do my best.
However, if you have some questions (in spite of all the efforts I did to
make that project understandable :-), comments or bug reports, just e-mail
me at: herve.gourmelon@enssat.fr (until April 98).
As far as the portability is concerned, I only tried that package under
Micro oft's Visual C++. Porting it to Borland's or Watcom's should be no
trouble, but beware of the definition of the 'BOOL type in
'cdbfile.h': you might have to modify it using pre-compilation directives.
If you want ot use it under UNIX/Linux (using 'cc' or 'gcc'), you should be
able to find a special GNU/Linux version that I released lately in the Linux
part of the Sunsite archive (that version is easier to port, by the way), as follows:
ftp://sunsite.unc.edu/pub/Linux/devel/db/cdbfile.tar.gz
If you decide to
improve significantly the code and to add new functionalities, please contact
me: I will provide you with the source of the postscript file you are
reading, so that you can add the description of your new functions or objects
and your name will be added to the contributor's file of the package, and to
this page.
Have fun!
Contributors
Herve GOURMELON
herve.gourmelon@enssat.fr
I hope that several people will add their names to that list...
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -