Writing then reading binary files.

Jan 27, 2011 at 1:08pm
Hi all. I have been set a task for my c++ class. I have to create a program to take input about a companys weekly timesheets (Employee ID, hours worked, hourly rate, that sort of thing). I am then to write this information to a binary file which can then be read. The aim is to create payslips from some of the data while having the original binary as a backup.

My problem comes from reading the binary file back and displaying it. When I try to display it I get the first few records but then the output is incorrect and the console crashes. Any input on my problem would be much appreciated.
(Im running code::blocks on windows XP)

Here is my code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

struct times{
	int empID;
	char lastName[15];
	char firstName[15];
	int dept;
	float hours;
	float overTime;
	float rate;
	string date;
};

times timesheet[20];
void Display();
void readFileByRecord();
void getTimeData();
void displayTimeData();
void writeBinary();
string sheetDate;
string filename;
int rows;
int y;

int main()
{
getTimeData(); //get user input
displayTimeData(); //display the users input
writeBinary(); //write input to binary file
readFileByRecord(); //read binary file
Display(); //display whatever the binary file contained
}

void getTimeData()
{
    cout << "Enter the end of week date (dd.mm.yy: include the full stops)" << endl;
    cout << "This will be used for all data on this timesheet" << endl;
    cin >> sheetDate;

    for (int i = 0; i <= 2; i++){
        timesheet[i].date = sheetDate;
    cout << "Enter employee ID: " << endl;
	cin >> timesheet[i].empID;
	cout << "Enter last name: " << endl;
	cin >> timesheet[i].lastName;
	cout << "Enter first name: " << endl;
	cin >> timesheet[i].firstName;
	cout << "Enter department no.: " << endl;
	cin >> timesheet[i].dept;
	cout << "Enter hours worked: " << endl;
	cin >> timesheet[i].hours;
            if (timesheet[i].hours > 37.5)
        {
            cout << "Enter overtime worked: " << endl;
            cin >> timesheet[i].overTime;
        }
            else
        {
            timesheet[i].overTime = 0;
        }
	cout << "Enter hourly rate: " << endl;
	cin >> timesheet[i].rate;
}
}

void displayTimeData()
{
    for(int x = 0; x <= 2; x++){
    cout << timesheet[x].empID << endl;
    cout << timesheet[x].lastName << endl;
    cout << timesheet[x].firstName << endl;
    cout << timesheet[x].dept << endl;
    cout << timesheet[x].hours << endl;
        if (timesheet[x].overTime != 0)
        {
            cout << timesheet[x].overTime << endl;
        }
    cout << timesheet[x].rate << endl;
    cout << timesheet[x].date << endl;
    }
}

void writeBinary()
{
filename = sheetDate + ".bin";
ofstream outFile(filename.c_str(),ios::binary);
outFile.write((char*)timesheet, sizeof(timesheet));
outFile.close();
}

void readFileByRecord()
{
	rows=0;
	ifstream myfile(filename.c_str(),ios::binary);
	if (!myfile.is_open())
		cout << "Unable to open file: " << filename.c_str() << endl;
	else
	{
		while ( !myfile.eof() )
		{
			myfile.read((char *)&timesheet[rows], sizeof(times));
			rows++;
		}
		myfile.close();
		rows--;
	}
}


void Display()
{
	for ( y=0;y<rows;y++ )
		cout << timesheet[y].empID << " " << timesheet[y].lastName
        << " " << timesheet[y].firstName << " " << timesheet[y].dept << " " << 
        timesheet[y].hours << " ";
        		if (timesheet[y].overTime != 0)
					{
					    cout << timesheet[y].overTime << " ";
					}
        cout << timesheet[y].rate << " " << timesheet[y].date << endl;
}


Some example output (dont ask where I come up with the names, I just hit keys :P)

12 Bobby Bob 4 12 15 Lendon Blik 4 75 12 Grando Polto 14 15 0  0 0 0  0 0 0
  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0 
  0 0 0  0 0 0  0 0 0  0 0 0  0 0 0  0 0 8.40779e-045 0



Thanks for your time!
Jan 27, 2011 at 1:35pm
What do you mean by "binary file". Can you post your exact assignment?

I ask, because this kind of CSV data is usually stored textually. If you want to keep it in a non-textual database, some additional structure must be applied, which typically splits things up.

If you are interested in serialization of an object containing both textual and non-textual data into a non-textual file, that again requires a little bit of extra work and structure. (Well, maybe not so much "extra work" as "different" work.)
Jan 27, 2011 at 1:42pm
You can only do that if you're dealing with Plain Old Data types.
http://www2.research.att.com/~bs/C++0xFAQ.html#PODs

Because times has a string, it's not a POD type.

What's acutally happening is the string in times is an object that has pointers to dynamically allocated memory. These are maintained by the object (constructor/destructor/copy and so on) and so you can't simply bit-blit the thing as you end up changing those pointer values, making the internal state of the string invalid.

To fix it, change the date field to a char array as you've done for the name fields.
Last edited on Jan 27, 2011 at 1:43pm
Jan 28, 2011 at 3:10pm
Hi guys, sorry for late response, my friend 'forced' me to get drunk yesterday.

I have changed the string in times to a char. I am having a problem now trying to use the char date inside my struct to apply to all of the timesheet[i].date. Here is my changed code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
struct times{
	int empID;
	char lastName[15];
	char firstName[15];
	int dept;
	float hours;
	float overTime;
	float rate;
	char date; //changed to char
};

times timesheet[20];
void Display();
void readFileIntoArrayByRecord();
void getTimeData();
void displayTimeData();
void writeBinary();
string sheetDate;
string filename;
int rows;
int y;
const char *SDate; //added to convert the string sheetDate (which I use to name the output file)
//to a char to add into my struct
int main()
{
getTimeData();
displayTimeData();
writeBinary();
readFileIntoArrayByRecord();
Display();
}

void getTimeData()
{
    cout << "Enter the end of week date (dd.mm.yy: include the full stops)" << endl;
    cout << "This will be used for all data on this timesheet" << endl;
    cin >> sheetDate;
SDate = sheetDate.c_str(); //string to char conversion

    for (int i = 0; i <= 2; i++){
                timesheet[i].date = SDate; //problem is here
    cout << "Enter employee ID: " << endl;


I'm not exactly sure how to fix it, any input would be appreciated. Thanks!
Jan 29, 2011 at 4:35am
Just glancing at your code:

1. Bug on Line 40 - date is only one char, as declared on Line 9.
2. Bug on Line 91 of your original file - you are only writing out one record.

BTW, this is actually a very good exercise, so learn to do it well. Learn to do it correctly.

Binary files are extremely useful - I use them to read/write out gigabyte-size files in milliseconds that would actually take minutes using their equivalent ASCII.
Last edited on Jan 29, 2011 at 4:35am
Jan 29, 2011 at 6:12am
2. Bug on Line 91 of your original file - you are only writing out one record.
outFile.write((char*)timesheet, sizeof(timesheet)); timesheet is an array, it is writing all the array
Topic archived. No new replies allowed.