Link to home
Start Free TrialLog in
Avatar of claracruz
claracruz

asked on

Problems reading and writing char and int type.

Hello experts,

I need to understand what I am missing here please.
I have a program that uses code to auto generates customer IDs on account creation. Here is how I achieve this;

I have a file called account.dat which contains a figure, for example 99.
I get this figure 99, and add 1 to it to get 100. 100 is the new account number generated, and 100 is returned to account.dat.

To save data, I am writing to a binary file like so;

                         dataIn.write((char*) (&account),sizeof (account));

In order to be able to write the new account number to file along with other customer details, i convert the accountnumber to char using the following code;
   
                         char AccountNumberE[20];//global variable
                         itoa (AccountNumber, AccountNumberE, 10);/*convert int AccountNumber to char.


see full code below;

char* Customer::returnAccountNumber()
{
//generate new account number
            inAccountNum.open("account.dat", ios::in);/*assign file to input stream from
                                                                          account number.*/


            if (inAccountNum.is_open())//if file is open
            {
                  inAccountNum >> AccountNumber; //get account number from account.dat
                  inAccountNum.close();
            }
            else //otherwise, if file is closed
            {
                  cout << "the file has not been open"; //display information
            }

            AccountNumber = AccountNumber + 1;//add one to account number
            itoa (AccountNumber, AccountNumberE, 10);/*convert int AccountNumber to char,
                                                                         to prevent memory errors when reading data.*/

            outAccountNum.open("account.dat"); //open account.dat for output
            outAccountNum << AccountNumber;//replace account number in account.dat with new figure for future use.
            outAccountNum.close();//close account.dat
            return AccountNumberE;



This function is then called elsewhere in my program like so;
void Customer::add(int flag)
{

      while (!cin.get()) {};

      if (flag==1)
      {

                      Customer accountnumber;
            do
            {      
            cout << "\n\n NEW CUSTOMER ACCOUNT NUMBER GENERATED:"
                                << accountnumber.returnAccountNumber() << "\n";
            }while (!stringCheck(accountnumber.returnAccountNumber(),0));//checks that acceptable input is received.

                 }


Now the problem I am having is that the following line;

                          cout << "\n\n NEW CUSTOMER ACCOUNT NUMBER GENERATED:"

 outputs the account number correctly, but when I read from file like so,

void Customer::display(void)
{
      cout << setiosflags(ios::left)<<setw(5)<<AccountNumberE<< setw(20)<<name;
      cout << setw(25)<<address<<setw(15)<<telephone<<setw(10)<<lastMeterReading<<setw(10)<<type<<"\n";
}

 the is no data for account number and if I do the following;

                         int i = atoi(AccountNumberE);//convert customer ID from char to int


to convert back to int before displaying, I get 0 for account number.

I don't know where the problem is occuring, it appears the ID isn't being written to the file. Because if I then switch the add(intflag) function for account number so the the user inputs it manually, then there is not problem, the figure is written to and read from file.

However the requirement for the program ia that it autogenerates account number.

Any help is appreciated.
Avatar of itsmeandnobodyelse
itsmeandnobodyelse
Flag of Germany image

>> dataIn.write((char*) (&account),sizeof (account));

That is wrong, as your are writing a pointer value  to a file and not the account value. Also a cast to char* will not convert an integer value to a character string. Last error is that if you write a pointer you also have to take the size of a pointer, i. e. sizeof(char*) or sizeof(&account). But you are using sizeof(account) == sizeof(int), that luckily is the same on most compilers (== 4 bytes) but wrong.

Do that if you want to write account as ASCII string

    char buf[20];
    itoa(account, buf, 10);
    dataIn.write(buf, strlen(buf) + 1);  // add one byte for terminating zero character.

However, you said, dataIn is a binary file. Then, you should write an integer as binary, so you have

    dataIn.write(account, sizeof (account));

Regards, Alex

Avatar of grg99
grg99

Change:

       outAccountNum << AccountNumber;    //replace account number

to:
      outAccountNum << AccountNumberE;   //replace account number


--------------------------
It's also a good idea to not use itoa(), as it's kinda unsafe,
better to use the more flexible snprintf( );

I checked the rest of your code and it isn't clear why - or if at all - you are using a binary file. I am assuming that the only contents of the file is the account number, For that purpose it is much more convenient to have a text file or an inifile.

On Windows platforms the default opens a file in text mode if ios::binary flag isn't given. Yo could open your file using a text editor. if you don't see any cryptic chars it's a text file.

Back to your problem. You said  

      ...
      << accountnumber.returnAccountNumber() << "\n";
      ...

correctly outputs the account number. Then you correctly read the file as the function returns a value that you read from file, incremented and converted to a char string.

Also you said in Customer::display you got wrong output and couldn't convert AccountNumberE to int (result is 0). Then, most likely AccountNumberE wasn't filled correctly to that time. You know, that AccountNumberE must be either a data member of class Customer

    class Customer
    {
          ...
    private:
          ...
          char AccountNumberE[20];
          ...
    };

or must be defined as a global variable in your main cpp file. If you have more than one cpp files you have to define the variable in one cpp file and declare it in all other cpp files as extern:


// main.cpp


// global variables:

char AccountNumberE[20] = "";  // define the global variable

   ....



// customer.cpp

// declare the variable as extern because it is defined in another cpp file
extern char AccountNumberE[20];


>> It's also a good idea to not use itoa(), as it's kinda unsafe,
>> better to use the more flexible snprintf( );

snprintf is much more unsafe than itoa as it has a variable argument list that will not be checked by the compiler.

When using  istream/ostream classes, there isn't any need to convert data from int to char* or vice versa, as these classes have a save conversion. So, if you have a text file

    outFile << account;

writes a string value to the file nomatter if 'account' is an int, long, or char* type. Same applies for istreams. If for any reason you need both representations in your prog, you may use stringstreams:

 #include <sstream>
 #include <string>

 void f()
 {
      string strAccount;
      int     iAccount;
       
      iAccount = 99;
      ++iAccount;
 
      stringstream ss1;
      ss1 << iAccount;
      strAccount = ss1.str();

      const char* pszAccount = strAccount.c_str();

      strAccount = "100";
      istringstream ss2(strAccount);
      ss2 >> iAccount;
}      
     
Regards, Alex
 
 }




Avatar of claracruz

ASKER

The account.dat contains only one value, the accout number, which is incremented by one each time an account is created. This is not the problem....!!!!!!!!!


I have to save a set of data which include the following;-

               char type; // type of customer (individual=I, business=B)
      char name[20];
      char address[25];
      char telephone[7];
      char AccountNumberE[20];   <--- of which accountNumber is pnly one of them!!!
      char lastMeterReading[20];
      
I am succesfully saving all other data (EXCEPT AUTO GENERATED ACCOUNT NUMBER) to the file using;-

                             dataIn.write((char*) (&account),sizeof (account));

Please note that if the account number is manually inputted by user, this is written to file, however, when I try to use the auto-generated number like so;-
void Customer::add(int flag)
{

     while (!cin.get()) {};

     if (flag==1)
     {

                     Customer accountnumber;
          do
          {    
          cout << "\n\n NEW CUSTOMER ACCOUNT NUMBER GENERATED:"
                                << accountnumber.returnAccountNumber() << "\n";
          }while (!stringCheck(accountnumber.returnAccountNumber(),0));//checks that acceptable input is received.

                 }

<------------------------------THIS WON'T WORK------------------------------->

HOWEVER if I did this instead


void Customer::add(int flag)
{

     while (!cin.get()) {};

     if (flag==1)
     {

                     Customer accountnumber;
          do
          {    
          cout << "\n\n ENTER CUSTOMER ACCOUNT NUMBER:" <-------------------------A/C NUMBER INPUT MANUALLY
                          cin  >> accountnumberE;
          }while (!stringCheck(accountnumberE ,0));//checks that acceptable input is received.

                 }

<---------------------------------THIS WORKS------------------------------------------>

So the question is why does it work for manual inputs and not for the autogenerated number????, what have I missed!!
OH I FORGOT TO MENTION THIS LINE OF CODE;

 dataIn.write((char*) (&account),sizeof (account));

writes to customer.dat (not account.dat)
You're doing things a bit covolutedly.... As far as I can tell, you're calling returnAccountNumber, which both stuffs a new string value into AccountNumberE in the calling object, AND returns a pointer to it.

First I'd rename "returnAccountNumber" into something more evocative of it's real function, generating a NEW accountNumber.   Then I'd have it return NOTHING as a function, the better to prevent you from calling it willy-nilly to get the account number, while it quielty increments it.  Side-effects from evaluating function are a BAD thing.

Then I'd investigate exactly what is in the array, and why you think it's "bad".  I havent seen an explanation yet of why you think it's bad.







ASKER CERTIFIED SOLUTION
Avatar of itsmeandnobodyelse
itsmeandnobodyelse
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Alex,

the bit you keep showing me i do not have a problem with. that bit is fine. the prblem is that I need to save the account number along with a bunch of other data in the format I wrote before.

------------------------------------------>                                      <--------------------------------------------

grg99,

you seem to understand my problem, but for some reason you keep describing the problem for me. I am aware that i am implementing it incorrectly one way or another. if you understand the problem, which you obviously do, could you please provide the best way to implement this and explain each step for me. This is probably the oly way I will understnad anything.

thank you.
The problem is very simple;

1)  I need to generate an account number.

2)  I need to save it as char* along with other customer data.

SO, if the way I have approached it is an absolute no brainer, then please forgive the nuisance and suggest a different approach (please show code and steps while explaining).

please! please!! please!!!

everything so far is not helping!!!

thank you
>> everything so far is not helping!!!

The problem is that you seem to be resistent to any advice ;-)

But take that:

class Customer
{
private:
     char type; // type of customer (individual=I, business=B)
     char name[20];
     char address[25];
     char telephone[7];
     char AccountNumberE[20];   // <--- of which accountNumber is pnly one of them!!!
     char lastMeterReading[20];
     

public:

    Customer() {}
    Customer(char typ, const char* nam, const char* addr = "", const char* phone = "" )
        : type(typ)
    {
        strcpy(name, nam);
        strcpy(address, addr);
        strcpy(telephone, phone);
        AccountNumberE[0] = '\0';        
        lastMeterReading[0] = '\0';
    }
       

    char* returnIncrementedAccountNumber();
    void  add(int flag);
};


char* Customer::returnIncrementedAccountNumber()
{
    int AccountNumber = 0;

    ifstream ifs("account.dat");
    if (ifs.fail())
    {
        // we assume the file doesn't exist
        AccountNumber  = 0;
        strcpy(AccountNumberE, "0");
    }
    else
    {
        ifs >> AccountNumber;
        if (ifs.fail())
        {
            ifs.close();
            cout << "Failed to read account number" << endl;
            return NULL;
        }
        ifs.close();
    }
    itoa(++AccountNumber, AccountNumberE, 10);
    ofstream ofs("account.dat");
    ofs << AccountNumber;
    ofs.close();

    return AccountNumberE;
}

void Customer::add(int flag)
{

     while (!cin.get()) {};

     if (flag==1)
     {

         Customer customer;
         
         char* pszAccount =  customer.returnIncrementedAccountNumber();
         if (pszAccount != NULL)
         {
              cout << "\n\n NEW CUSTOMER ACCOUNT NUMBER GENERATED:" << pszAccount << endl;
         }
         else
         {
             cout << "\n\n NEW CUSTOMER ACCOUNT NUMBER GENERATION FAILED:" << endl;
         }
     }
}


It is used like that:

void main()
{
    Customer c1('B', "John");
    c1.add(1);
    Customer c2('I', "Mary");
    c2.add(1);
}


All that compiles and runs and i won't see any do while loops in the next version you are posting ;-)

Regards, Alex