I am currently working on an accounting program for my project. I am struggling with reading from a file into nested structures. Any guidance on where I should go with this? I know I want it to stop reading into account data and move on to the next customer when I reach a blank line (null terminator). Basically, some customers have a couple of accounts, others have more than 2 (but no more than 5, struct array only holds 5). The project itself is much more in-depth (structures contain more variables). I am currently just using a practice file to try and figure out the concept..
Here is my code so far:
struct ACCOUNT
{
char acct_num[7];
char balance[8];
};
struct CUSTOMER
{
char cust_name[20];
char number[5];
ACCOUNT acct[5];
};
int main()
{
CUSTOMER person[3];
fstream fin;
fin.open("accounts.dat", ios::in);
int i, j;
i = 0, j = 0;
char buff[20];
char line[20];
while (fin.getline(buff, 20))
{
strncpys(person[i].cust_name, buff, 10);
fin.getline(line, 10);
strncpy(person[i].number, line, 10);
do {
fin.getline(line, 20, ' ');
strncpy(person[i].acct[j].acct_num, line, 10);
fin.getline(line, 20);
strncpy(person[i].acct[j].balance, line, 10);
j++;
cin.getline(line, 20);
} while (*line != 0);
i++;
}
return 0;
}
Data file I am trying to read into:
Jane Smith
FD12
SSDFSS 64.51
SD5545 88.51
John Smith
FD45
SFG789 77.21
NM4521 21.223
MM7888 33.33
John Doe
FSS4
SFGSGG 77.65
HN5555 22.31
Problems I noticed:
strncpys(person[i].cust_name, buff, 10);
I assume you meant to use strncpy
, not strncpys
. Even then, 10 is too small. You needed to use 20.
strncpy(person[i].number, line, 10);
Here, 10 is too large. You need to use 5.
j
needs to be reinitialized to 0
inside the outer loop.
The logic for checking for empty lines in the inner while
loop is flawed. You have used
cin.getline(line, 20);
as the last line in the loop but I assume that's error in posting to SO. I think you have
fin.getline(line, 20);
That is a problem since you consume the next line from the file but the data contained in it just tossed.
However, the more important suggestions I have are:
Re-think the strategy for parsing the input file. I find it easiest to read the contents of a file line by line and then process each line separately.
Create smaller functions to read different pieces of the input and use them from main
.
Write to stdout what you read so you can clearly see where the data was not read like you hoped it would.
I would suggest the following update to main
.
int main()
{
CUSTOMER person[3];
fstream fin;
fin.open("socc.in", ios::in);
int i = 0;
std::string line;
while ( getline(fin, line) )
{
read_customer_name(line, person[i]);
std::cout << "cust_name: " << person[i].cust_name << std::endl;
if ( getline(fin, line) )
{
read_customer_number(line, person[i]);
std::cout << "number: " << person[i].number << std::endl;
}
else
{
// Read was not successful.
// Break the loop.
break;
}
// Define it in this scope only.
int j = 0;
while ( getline(fin, line) )
{
if ( line.empty() )
{
break;
}
read_customer_account(line, person[i].acct[j]);
std::cout << "acct[j].acct_num: " << person[i].acct[j].acct_num << std::endl;
std::cout << "acct[j].balance: " << person[i].acct[j].balance << std::endl;
j++;
}
}
return 0;
}
where the helper functions are:
void read_customer_name(std::string const& line, CUSTOMER& person)
{
strncpy(person.cust_name, line.c_str(), 20);
}
void read_customer_number(std::string const& line, CUSTOMER& person)
{
strncpy(person.number, line.c_str(), 5);
}
void read_customer_account(std::string const& line, ACCOUNT& acct)
{
std::istringstream str(line);
str.getline(acct.acct_num, 10, ' ');
str.getline(acct.balance, 10);
}