Solved

Problems reading and writing char and int type.

Posted on 2004-08-04
10
225 Views
Last Modified: 2010-04-01
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.
0
Comment
Question by:claracruz
  • 4
  • 4
  • 2
10 Comments
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 11714055
>> 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

0
 
LVL 22

Expert Comment

by:grg99
ID: 11714058
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( );

0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 11715049
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
 
 }




0
 
LVL 4

Author Comment

by:claracruz
ID: 11715830
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!!
0
 
LVL 4

Author Comment

by:claracruz
ID: 11716934
OH I FORGOT TO MENTION THIS LINE OF CODE;

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

writes to customer.dat (not account.dat)
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 22

Expert Comment

by:grg99
ID: 11717309
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.







0
 
LVL 39

Accepted Solution

by:
itsmeandnobodyelse earned 500 total points
ID: 11718085
First, as i already told you,

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

is wrong.

If account is of type int or long, you are writing the address of that variable  to your data file. And because of the cast to char* it writes non-printable characters, what you can see when opening the file in any text editor.


Second, you still haven't answered where AccountNumber and AccountNumberE were defined. As you are calling member function

   Customer::returnAccountNumber()

both variables should be data members of that class like that:

class Customer
{
private:
      int AccountNumber;
      char AccountNumberE[20];

public:
       .....
       all other stuff          
};

Last, the loop

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

is most likely wrong as you call accountnumber.returnAccountNumber() at least  twice (once in cout and once in stringCheck).

Look at that code:

void g()
{
    int account = 99;
    ofstream ofs("xxx.txt");
    ofs << account;
    ofs.close();
    ifstream ifs("xxx.txt");
    ifs >> account;
    char accountE[20];
    itoa(++account, accountE, 10);
    ifs.close();
    ofstream ofs1("xxx.txt");
    ofs1 << accountE;
    ofs1.close();

}

That works fine and you can look at xxx.txt using any text editor.

Regards, Alex


 


0
 
LVL 4

Author Comment

by:claracruz
ID: 11725752
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.
0
 
LVL 4

Author Comment

by:claracruz
ID: 11726320
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
0
 
LVL 39

Expert Comment

by:itsmeandnobodyelse
ID: 11727065
>> 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
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Suggested Solutions

This article will show you some of the more useful Standard Template Library (STL) algorithms through the use of working examples.  You will learn about how these algorithms fit into the STL architecture, how they work with STL containers, and why t…
C++ Properties One feature missing from standard C++ that you will find in many other Object Oriented Programming languages is something called a Property (http://www.experts-exchange.com/Programming/Languages/CPP/A_3912-Object-Properties-in-C.ht…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.
The viewer will be introduced to the member functions push_back and pop_back of the vector class. The video will teach the difference between the two as well as how to use each one along with its functionality.

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now