• Welcome to Powerbasic Museum 2020-B.
 

ProgEx34 -- Return To ProgEx20 And Problems We Had With Reading Strings

Started by Frederick J. Harris, November 26, 2009, 09:16:57 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris

Strings.cpp & Strings.h are available in the attached zip file (as is the file below - Main.cpp).


/*
 ProgEx34   -- Back To ProgEx20 And The Problem We Had Re-Constituting comma
               delimited file data Into The struct It Came From.
               
 The program below is a 'doctored up' version of ProgEx20 where we ran into
 some ugly C string issues which I hope we've finally solved by delving into
 the domain of creating a String Class.  See my narrative below in the code.                  
*/

#include <stdio.h>
#include <string.h>
#include "Strings.h"

struct Employee          //Same as Type Employee
{
char szName[32];  
char szAddress[32];
char szCity[16];
char szState[16];
char szCountry[16];
char szZipCode[16];
unsigned int iAge;
unsigned int iEmpNum;
double dblSalary;  
};                       //End Type

void Initialize(Employee* pEmp)
{
strcpy(pEmp[0].szName,"Martin Veneski");             //1st employee
strcpy(pEmp[0].szAddress,"1016 Wabash Street");
strcpy(pEmp[0].szCity,"Shamokin");
strcpy(pEmp[0].szState,"PA");
strcpy(pEmp[0].szCountry,"USA");
strcpy(pEmp[0].szZipCode,"17872");
pEmp[0].iAge=23;
pEmp[0].iEmpNum=123456;
pEmp[0].dblSalary=45000;

strcpy(pEmp[1].szName,"Raymond Latchkoski");         //2nd employee
strcpy(pEmp[1].szAddress,"1624 Tioga Street");
strcpy(pEmp[1].szCity,"Shamokin");
strcpy(pEmp[1].szState,"PA");
strcpy(pEmp[1].szCountry,"USA");
strcpy(pEmp[1].szZipCode,"17872");
pEmp[1].iAge=25;
pEmp[1].iEmpNum=789123;
pEmp[1].dblSalary=50000;
}

int main()
{
char szBuffer[256];
String strEmp(256);
String* pEmp=NULL;
Employee emp[2];
int iCnt=0;
FILE* fp;

/*
   This 1st block of code simply loads the Employee struct array and then
   writes the data to a comma delimited text file using fprintf.
*/
Initialize(emp);
fp=fopen("Data.dat","w");  //Same as Open "Data.dat" For Output As #fp
for(unsigned int i=0; i<sizeof(emp)/sizeof(emp[0]); i++)  //For i=0 To 1
{
    fprintf                      //Print #fp, ...
    (
     fp,
     "%s,%s,%s,%s,%s,%s,%u,%u,%6.2f\n",
     emp[i].szName,emp[i].szAddress,emp[i].szCity,emp[i].szState,
     emp[i].szCountry,emp[i].szZipCode,emp[i].iAge,emp[i].iEmpNum,
     emp[i].dblSalary
    );
}    
fclose(fp);

/*
  In this block of code we first zero out our Employee array emp[].  I usually
  use memset() for that, although there is also a ZeroMemory function, but its
  not in any of the C Runtime Libraries.
 
  Next we re-open Data.dat and read each line out into our szBuffer char array
  using gets (Get String).  Note that we declared a simple String object at the
  top of main() like so...
 
  String strEmp(256);
 
  You have not seen me do that yet with our String Class which we so laborously
  developed in the past several Program Examples.  What that is is an example
  of constructing a String object with a pre-set buffer size.  If you examine
  the various String Class Constructor overloads you'll see one that instead of
  taking a char or char* or String as the basis for creating (constructing) a
  new String, there is one for creating a String with a specific buffer size.
  This could come in handy if as the writer of a program you know in advance
  the approximate sizes of the String data you'll be dealing with, and rather
  than allowing the default String Class behavior of re-allocating and
  releasing memory as the Strings encountered shrink or grow, you decide in
  advance to just allocate a buffer big enough to handle the largest String you
  expect to encounter.  So the above String declaration will cause a call to
  the String Constructor that takes an integer parameter (size of requested
  buffer to allocate).
 
  Next we simply parse the String read out of the file in exactly the same
  manner as we did in ProgEx33 where we introduced Parse.  Note that I created
  a String* (String Pointer) variable named pEmp...
 
  String* pEmp=NULL;
 
  to hold the parsed out Strings.  Since the Employee struct contains only
  primitive asci character strings of various sizes, we unfortunately need to
  use strcpy() to move the bytes in the parsed out Strings into the struct.
  That is what most of the code bulk below is doing.  You might also note I had
  to add another member to our String Class in this example because it was
  missing a way to create a floating point double out of a String, and we
  needed that for the Salary member of the struct.  After the loop is finished
  reading the file data back into the array of structs, delete [] is called on
  pEmp.
*/
memset(emp,0,sizeof(emp)*2);     //zero out 2 emp structs
fp=fopen("Data.dat","r");        //Same as Open "Data.dat" For Input As #fp
for(unsigned int i=0; i<2; i++)  //Same as For i=0 To 1
{
    fgets(szBuffer,256,fp);      //Same As Line Input #fp, szBuffer
    strEmp=szBuffer;             //Assign szBuffer To strEmp.  Will trigger operator=
    strEmp.Print(false);         //Print data read from file through String.  Already has \n in it
    iCnt=strEmp.ParseCount(','); //Find out how many comma delimited Strings
    pEmp=new String[iCnt];       //Create a String on the heap for each one
    strEmp.Parse(pEmp,',');      //Parse the Strings into pEmp[]
    strcpy(emp[i].szName,pEmp[0].lpStr());     //This is awkward but the Employee struct does not
    strcpy(emp[i].szAddress,pEmp[1].lpStr());  //contain dynamic strings or String objects, so
    strcpy(emp[i].szCity,pEmp[2].lpStr());     //strcpy (byte copy type operation) is necessary
    strcpy(emp[i].szState,pEmp[3].lpStr());    //to copy bytes into struct.
    strcpy(emp[i].szCountry,pEmp[4].lpStr());
    strcpy(emp[i].szZipCode,pEmp[5].lpStr());
    emp[i].iAge=pEmp[6].iVal();
    emp[i].iEmpNum=pEmp[7].iVal();
    emp[i].dblSalary=pEmp[8].dblVal();
     delete [] pEmp;
}
fclose(fp);                      //Same as Close #fp

/*
  In this block of code we simply iterate through our array of Employee structs
  and output the data to the screen with printf.  This data was originally put
  in the Employee structs in Initialize(), but remember we wrote the structs
  out to a comma delimited text file, zeroed out the Employee structs, then
  read the data back in.  It is that data read back in to the Employee structs
  that is being output here and as seen below in the output.
*/
for(unsigned int i=0; i<sizeof(emp)/sizeof(emp[0]); i++)  //For i=0 To 1
{
    printf                      //Print #fp, ...
    (
     "\n%s\n%s\n%s\n%s\n%s\n%s\n%u\n%u\n%6.2f\n",
     emp[i].szName,emp[i].szAddress,emp[i].szCity,emp[i].szState,
     emp[i].szCountry,emp[i].szZipCode,emp[i].iAge,emp[i].iEmpNum,
     emp[i].dblSalary
    );
}
getchar();

return 0;
}

/*
Martin Veneski,1016 Wabash Street,Shamokin,PA,USA,17872,23,123456,45000.00
Raymond Latchkoski,1624 Tioga Street,Shamokin,PA,USA,17872,25,789123,50000.00

Martin Veneski
1016 Wabash Street
Shamokin
PA
USA
17872
23
123456
45000.00

Raymond Latchkoski
1624 Tioga Street
Shamokin
PA
USA
17872
25
789123
50000.00
*/