File I/O

May 24, 2013 at 11:11pm
Hello all,
I'm working on a file I/O at the moment and I need some guidance. I have a fraction structure, and I'm dynamically allocating the structure through the pointer as the user selects the option to add a fraction to the structure. After allocating the memory, and receiving the user input I have to store the fraction into a binary file. Here's my code so far as I've worked out.

My questions are :
1) Do I allocate memory then destroy the pointer after writing to the file ?
2) I'm not sure if I'm storing them correctly, as I have a run-time error when I try to display the fractions from the file. I tried using pointers to access it (did not work), and then after created a variable of type Fraction to try and access it. Both do not seem to work. It compiles and runs but when I try and display all the fractions using the read function, it goes into an infinite loop.

Any help would be greatly appreciated !

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
#include <iostream>
#include <fstream>
#include <conio.h>
using namespace std;

struct Fraction
{
	int num, den;
};


int main()
{		
	Fraction *fracPtr;											// A pointer of type Fraction.	
	int selection;												// Stores the user selection for the menu. 
	char slash;													// A variable to store the / in the fraction.
	Fraction fracP;												// A variable of type fracP

	// Create file object
	fstream fwrite("fractions.dat", ios::out | ios::binary);
	fstream fread("fractions.dat", ios::in | ios::binary);

	do
	{
		cout << "1. Enter new fraction\n\n";
		cout << "2. Display/update a fraction\n\n";
		cout << "3. Display all fractions.\n\n";
		cout << "4. Add fractions\n\n";
		cout << "5. Sort fractions\n\n";
		cout << "6. Quit\n\n";
		cout << "Enter selection: ";
		cin >> selection;
		cin.ignore();

		switch(selection)
		{
		case 1:	fwrite.open("fractions.dat", ios::out | ios::binary | ios::app);
				fracPtr = new Fraction;
				cout << "Enter fraction to add: ";
				cin >> fracPtr->num >> slash >> fracPtr->den;
				cin.ignore();
				// Write the fractions to the file fractions.dat using the write function. 
				fwrite.write(reinterpret_cast<char *>(fracPtr), sizeof(fracPtr));
				delete fracPtr;
				fracPtr = 0;
				fwrite.close();
				break;

		case 3: fread.open("fractions.dat", ios::in | ios::binary | ios::beg);
				fread.read(reinterpret_cast<char *>(&fracP),sizeof(fracP));
				while(!fread.eof())
					{
						cout << fracP.num << "/" << fracP.den << " ";
						fread.read(reinterpret_cast<char *>(&fracP),sizeof(fracP));
						fread.close();
					}
				
				break;

		//default : break;
		}
	}while(selection != 6);

	_getch();
	return 0;
}
Last edited on May 25, 2013 at 3:38am
May 25, 2013 at 3:38am
Updated code after a little more trial and error. Any ideas ?
May 25, 2013 at 4:22am
Prefer formatted input/output over binary io.

> 1) Do I allocate memory then destroy the pointer after writing to the file ?

No. For this, do not use dynamic memory allocation at all.


> 2) I'm not sure if I'm storing them correctly

No, you are not.
1
2
// fwrite.write(reinterpret_cast<char *>(fracPtr), sizeof(fracPtr));
fwrite.write(reinterpret_cast<char *>(fracPtr), sizeof(Fraction))

Size of object, not size of pointer.


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
#include <iostream>
#include <fstream>
using namespace std;

struct Fraction
{
    int num, den;
};


int main()
{
    int selection ;

    do
    {
        cout << "1. Enter new fraction\n\n";
        cout << "3. Display all fractions.\n\n";
        cout << "6. Quit\n\n";
        cout << "Enter selection: ";
        cin >> selection;

        switch(selection)
        {
            case 1:
            {
                cout << "Enter fraction to add: ";
                Fraction f ;
                char slash ;
                cin >> f.num >> slash >> f.den ;
                std::ofstream file( "fractions.dat", ios::app ) ;
                file << f.num << '/' << f.den << '\n' ;

                // or, if you must do binary i/o
                // std::ofstream file( "fractions.dat", ios::binary|ios::app ) ;
                // file.write( reinterpret_cast<const char*>(&f), sizeof(f) );
            }
            break ;

            case 3:
            {
                Fraction f ;
                char slash ;
                std::ifstream file( "fractions.dat" ) ;
                while( file >> f.num >> slash >> f.den )
                    std::cout << f.num << slash << f.den << '\n' ;

                // or, if you must do binary i/o
                // std::ifstream file( "fractions.dat", ios::binary ) ;
                // while( file.read( reinterpret_cast<char*>(&f), sizeof(f) ) )
                // std::cout << f.num << '/' << f.den << '\n' ;
            }
            break ;
        }

    }
    while(selection != 6);
}
May 25, 2013 at 4:25am
hi, i'm a bit of a beginner.
i've read your code and found some flaws that might be the cause:
1- inside the switch statement, you shouldn't open the file again before closing it, you've already opened the file in the declaration of your streams.
2-i think you should test if the file actually opened before using the stream, maybe your system is denying the program from creating the file from being created or accessed from the program, if this is the case, then i can't help convincing your system to let the program do its job.
these might be the cause of your problem, if you fix them and find other problems, i'll try to help.
May 25, 2013 at 6:50am
Thank you very much @JLBorges. I actually had the sizeof(object) but, I had tried the ptr and probably copied that code over. Good to know.
I know this problem is much easier with formatted i/o but, our given problem states that we use binary files.
The really mind boggling fact is that we are required to use dynamic memory allocation. That is the sole reason I use a pointer instead of a variable of type Fraction. And, that is where I think where my main problem resides.

@Rechard3 Thank you for your suggestion. I'm a newbie myself and I'm trying to learn the ins and outs of file i/o. Now, that you mention it I do realize I shouldn't open the file again (and instead use one file stream for both input and output).
The system is creating the file as I can see it inside the executing folder. However, I'm not sure if the output is being written to the file in binary format.

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
#include <iostream>
#include <fstream>
using namespace std;

struct Fraction
{
    int num, den;
};


int main()
{
    int selection ;

    do
    {
        cout << "1. Enter new fraction\n\n";
        cout << "3. Display all fractions.\n\n";
        cout << "6. Quit\n\n";
        cout << "Enter selection: ";
        cin >> selection;

        switch(selection)
        {
            case 1:
            {
                cout << "Enter fraction to add: ";
                Fraction f ;
                char slash ;
                cin >> f.num >> slash >> f.den ;
                std::ofstream file( "fractions.dat", ios::app ) ;
                file << f.num << '/' << f.den << '\n' ;

                // or, if you must do binary i/o
                // std::ofstream file( "fractions.dat", ios::binary|ios::app ) ;
                // file.write( reinterpret_cast<const char*>(&f), sizeof(f) );
            }
            break ;

            case 3:
            {
                Fraction f ;
                char slash ;
                std::ifstream file( "fractions.dat" ) ;
                while( file >> f.num >> slash >> f.den )
                    std::cout << f.num << slash << f.den << '\n' ;

                // or, if you must do binary i/o
                // std::ifstream file( "fractions.dat", ios::binary ) ;
                // while( file.read( reinterpret_cast<char*>(&f), sizeof(f) ) )
                // std::cout << f.num << '/' << f.den << '\n' ;
            }
            break ;
        }

    }
    while(selection != 6);
}
Last edited on May 25, 2013 at 6:51am
May 25, 2013 at 7:41am
> However, I'm not sure if the output is being written to the file in binary format.

No. It is being written and read in text format (formatted i/o).


> our given problem states that we use binary files.
> The really mind boggling fact is that we are required to use dynamic memory allocation.

I presume that you are also required to use delete instead of letting a std::unique_ptr<> do the job.

This is terrible code, but it will meet the stated requirements (new, delete, and binary i/o).

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
#include <iostream>
#include <fstream>
#include <memory>
using namespace std;

struct Fraction
{
    int num, den;
};

int main()
{
    int selection ;

    do
    {
        cout << "1. Enter new fraction\n\n";
        cout << "3. Display all fractions.\n\n";
        cout << "6. Quit\n\n";
        cout << "Enter selection: ";
        cin >> selection;

        switch(selection)
        {
            case 1:
            {
                cout << "Enter fraction to add: ";
                Fraction* f = new Fraction ;
                char slash ;
                cin >> f->num >> slash >> f->den ;
                std::ofstream file( "fractions.dat", ios::binary|ios::app ) ;
                file.write( reinterpret_cast<const char*>(f), sizeof(Fraction) );
                delete f ;
            }
            break ;

            case 3:
            {
                Fraction* f = new Fraction ;
                std::ifstream file( "fractions.dat", ios::binary ) ;
                while( file.read( reinterpret_cast<char*>(f), sizeof(Fraction) ) )
                   std::cout << f->num << '/' << f->den << '\n' ;
                delete f ;
            }
            break ;
        }

    }
    while(selection != 6);
}


Note: for the 5. Sort fractions option, you would require dynamic memory.
Hopefully, std::vector<> is not blacklisted.
May 25, 2013 at 8:04am
@JLBorges Thank you very much. I never had thought of using a pointer to read the file stream ! This is the code I had been working on in the past 1 hour.
I'm sure it's rather bad coding. In the last three weeks we have been working on structures, pointers and finally files.

In the display statement, I was using the original Fraction type variable to access and it would just keep on showing random numbers.
We haven't covered vectors so, I'll have to figure out a different approach to solving it. I'll try out the code you have posted.
One last question :
I see you do
1
2
while( file.read( reinterpret_cast<char*>(f), sizeof(Fraction) ) )
                   std::cout << f->num << '/' << f->den << '\n' ;

Instead of :
1
2
3
4
5
file.read(reinterpret_cast<char *>(&fracP),sizeof(fracP));
				while(!file.eof())
				{
					cout << fracP.num << "/" << fracP.den << " ";
					file.read(reinterpret_cast<char *>(&fracP),sizeof(fracP));

Is there a reason why ? Again, thank you very much. You really helped me out in figuring this out.
This is the code I had written:
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
	switch(selection)
		{
		case 1:	{
				fracPtr = new Fraction;
				cout << "Enter fraction to add: ";
				cin >> fracPtr->num >> slash >> fracPtr->den;
				cin.ignore();
				// Write the fractions to the file fractions.dat using the write function. 
				file.write(reinterpret_cast<char *>(fracPtr), sizeof(fracP));
				delete fracPtr;
				fracPtr = 0;
				}
				break;

		case 2:	{
				if(file.is_open())
					cout << "File is open!" << endl;
				file.seekg(0, ios::beg);
				file.read(reinterpret_cast<char *>(&fracP),sizeof(fracP));
				while(!file.eof())
				{
					cout << fracP.num << "/" << fracP.den << " ";
					file.read(reinterpret_cast<char *>(&fracP),sizeof(fracP));
				}
				}
				break;

		}
	}while(selection != 6);

	_getch();
	return 0;
}
May 25, 2013 at 8:39am
In general, avoid operating on the same file, keeping the same file simultaneously open via two different stream objects.

If you want to read and write into the same file without closing the stream, open it for both input and output as a std::fstream

This
1
2
3
4
5
6
file.read(reinterpret_cast<char *>(&fracP),sizeof(fracP));
while(file)
{
    cout << fracP.num << "/" << fracP.den << " ";
    file.read(reinterpret_cast<char *>(&fracP),sizeof(fracP)); 
}

is equivalent to
1
2
while( file.read( reinterpret_cast<char *>(&fracP), sizeof(fracP) ) )
    cout << fracP.num << "/" << fracP.den << " ";

The second canonical version is shorter.

Topic archived. No new replies allowed.