MSCUFlatFile class
MSCUFlatFile is the class that deals with fixed-length flat files, that are made up of the objects derived from MSCUObject. Before starting the class, please make sure you have finished the MSCUObject and it's derived classes:
Class Definition
class MSCUFlatFile : private fstream
{
public:
MSCUFlatFile();
MSCUFlatFile( char *File );
~MSCUFlatFile();
void Open( char *File=0, int Mode=in|out|binary, bool CreateIt=true);
void Close();
void AddField( MSCUObject*Field, char *Fieldname );
void RemoveField( int Index=-1 );
void Write();
void Append();
void Read();
void MoveNext();
void MovePrev();
void MoveFirst();
void MoveLast();
void Dump();
protected:
// Remove 'allocator' if it causes problems
deque<MSCUObject*,allocator> Fields;
deque<string,allocator> Fieldnames;
string Filename;
unsigned long RecCount, RecNum, RecSize;
};
Function Specifications
MSCUFlatFile()
Initializes data members, except for Filename.
MSCUFlatFile( char *File )
Initializes data members, including Filename. Does not open
file.
~MSCUFlatFile();
Calls RemoveField repeatedly, to remove all fields.
void Open( char *File=0, int Mode=in|out|binary, bool CreateIt=true);
Open the datafile. If File is not 0, then it should store
the value into the Filename datamember. CreateIt determines
if the file should be created if it doesn't exist (see handout on how to
test for existance, and create). Eventually calls the fstream::open function,
passing it the Filename data member, and the Mode parameter.
Throw an exception if can't open or create the file.
void Close();
Calls the fstream::close() function, if the file is open.
void AddField( MSCUObject*Field, char *Fieldname );
Adds Field to the Fields data member, and Fieldname
to the FieldNames data member using the push_back member function
of the deque. Add Field->GetStorageSize() to the RecSize data
member.
void RemoveField( int Index=-1 );
Remove the field indicated by Index. You must first get
the MSCUObject* out of the Fields data member. Then you must call
the erase function for the deque class (the Fields and FieldNames
data members). Then you subtract the fields size (GetStorageSize)
from the RecSize data member, and finally 'delete' the MSCUObject*.
void Write();
First, position to the correct record, using seekp, and
a position of RecNum*RecSize. Then, using the size member
function to determine how many fields are in the Fields data member,
iterate through each field object, and call it's SaveToFile function
( Fields[i]->SaveToFile( *this ); ). If the RecNum data member
equals the RecCount data member, then add 1 to RecCount (that
means there was an 'append').
void Append();
Simply calls RecNum data member to RecCount, and
then calls Write().
void Read();
Like Write(), only instead of calling SaveToFile(),
call LoadFromFile(). Also, use seekg to position to the right
record.
void MoveNext();
Adds 1 to RecNum. If RecNum is >= RecCount,
then through an exception. Call Read() after changing RecNum.
void MovePrev();
Subtracts 1 from RecNum, unless RecNum is 0, in which case
it throws an exception. Call Read() after changing RecNum.
void MoveFirst();
Sets RecNum to 0, and calls Read().
void MoveLast();
Sets RecNum to RecCount, and calls Read().
void Dump();
Iterates through all Fields elements, and calls the operator<<
to output the Fields element, and also the Fieldname element.
Yet More Code To Add
The following functions should be added to your MSCUFlatFile class, in addition to what is above after you get the above working. These functions implement the field access and record copying abilities. At the end of this file, you will find a test program that should test this new version of MSCUFlatFile. You should already have an example program that tests the above version. Note: these are all member functions of MSCUFlatFile.
int GetFieldCount() { return( Fields.size() ); }
Returns number of fields in the MSCUFlatFile object.
bool IsEOF() { return( RecNum==RecCount ); }
Returns true if at end of file, false otherwise.
const string& GetFieldName( int Index ) const
{ if( Index<0||Index>=Fields.size()) throw "Bad Index"; return( Fieldnames[Index] ); }
Returns string for fieldname, based on integer index.
MSCUObject* Field( int Index ) const { if( Index<0||Index>=Fields.size())
throw "Bad Index"; return( Fields[Index] ); }
Returns MSCUObject* for a field, based on integer index position.
MSCUObject* Field( const char* Name ) const
{
for(int Index=0; Index<Fieldnames.size(); Index++ )
if( Fieldnames[Index]==Name )
return( Fields[Index] );
return( 0 );
}
Returns MSCUObject* for a field, based on character string (fieldname, case
sensitive).
enum HM { MakeBlank, Skip, Error };
Used by CopyData
void CopyData( const MSCUFlatFile& Src, HM HandleMissing=MakeBlank
)
{
MSCUObject * Fld;
for( int i=0; i < Fields.size(); i++ )
{
Fld = Src.Field( Fieldnames[i].c_str() );
if( Fld==NULL )
{
if( HandleMissing==Error )
throw "missing field";
if( HandleMissing==MakeBlank )
Fields[i]->SetFromString( string("") );
else
continue;
}
else
Fields[i]->SetFromString( Fld->GetAsString() );
}
}
Copies fields from one record to another, regardless of position. Src identifies the source flat file object to copy data from. HandleMissing describes how fields that are missing in the source object should be handled:
Error - Throws an exception
MakeBlank- Initializes the field to an empty string value
Skip - Don't change the target field in anyway.
Examle program for new MSCUFlatFile:
This program should be run three times.
The first time you run it, it will create a dummy data file.
The second time you run it, it will convert the dummy data file into a second type of file, with a different format (and one less field).
The third time you run it (and everytime there after, unless you delete it's test files), it will list the new dummy data file created on the second run.
class FileType1 : public MSCUFlatFile
{
public:
FileType1()
{
Filename="test.1";
AddField( new MSCUDate, "BDay" );
AddField( new MSCUSSN, "SSN" );
AddField( new MSCUString(35), "String" );
}
};
class FileType2 : public MSCUFlatFile
{
public:
FileType2()
{
Filename="test.2";
AddField( new MSCUSSN, "SSN" );
AddField( new MSCUDate, "BDay" );
}
};
void main()
{
FileType1 One;
FileType2 Two;
try
{
if( !ifstream( "test.1" ) )
{
cout << "Create dummy test data file" << endl;
One.Open();
char Tmp[32];
for( int i=0; i < 20; i++ )
{
sprintf( Tmp, "%ld", rand()*rand() );
One.Field( "SSN" )->SetFromString( string(Tmp) );
sprintf( Tmp, "12-%02d-1997", i );
One.Field("BDay" )->SetFromString( string(Tmp) );
for( int j=0; j < 10; j++ )
One.Field( "String"
One.Append();
}
}
else
{
if( !ifstream( "test.2" ) )
{
cout << "Converting format 1 to format 2"<<endl;
One.Open();
Two.Open();
while( One.IsEOF() == false )
{
Two.CopyData( One );
Two.Dump();
Two.Append();
One.MoveNext();
}
}
else
{
cout << "Listing converted file" << endl;
Two.Open();
while( Two.IsEOF() == false)
{
for( int i=0; i < Two.GetFieldCount(); i++ )
cout << Two.Field(i)->GetAsString() << " ";
Two.MoveNext();
cout <<endl;
}
}
}
}
catch( char* E )
{
cout << "Error: " << E << endl;
}
catch( MSCUExcept * E )
{
MSCUExcept::Dump( cout );
}
}