using getline() for objects?

Hello.

So I decided to make a new topic since I now know what my problem is. Here's 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
ofstream dataOut; //output stream for the data file
	ifstream dataIn; //input stream for data file
	string FILE = "contracts.dat"; //filename

	//file creation, will fail if it already exists (I think)
	dataOut.open(FILE,ios::out);
	dataOut.flush();
	dataOut.close();

	dataIn.open(FILE);
	//if it can't be opened, there's a problem and the program won't work. It will most likely be a permission issue.
	if(!dataIn) {
		cout << "File could not be opened/created. Make sure you have the correct permissions. \n This program will now close \n";
		system("pause");
		exit(0);
	}

	//import the data
	vector<Contract> contracts; //initialize a vector to hold all the contracts
	Contract temp; //initialize a temporary var to store the read data into
	cout << "Importing contracts from data file...";
	while(!dataIn.eof()){ //read until end of file
		dataIn.getline(temp, 5000);
		contracts.push_back(temp);
	}
	dataIn.close();
	cout << "done!" << endl;


So my object here is a contract. It just holds a couple of vectors, ints, strings, all that good stuff. I'm trying to write a program that will allow users to create contracts and save them to a text file that the program creates. When the program is opened, it then reads the text file and retrieves the contracts they created so they can pick up where they left off. Problem is, I don't know how to overload the getline() function to work with my contract objects. I also don't know if there's a different, better way to do this. Any insights are greatly appreciated!
Google around "c++ object serialization".

Good luck!
> So my object here is a contract. It just holds a couple of vectors, ints, strings, all that good stuff.
> I'm trying to write a program that will allow users to create contracts and save them to a text file

So, overload the stream insertion operator to write out an object of type contract to an ostream.
A simple way to do this would be to write out one member per line of text.

For example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct contract
{
    std::vector<int> v ;
    int n ;
    std::string s ;
};

std::ostream& operator<< ( std::ostream& stm, const contract& c )
{
    // on the first line, write out the contents of the vector 
    if( c.v.empty() ) stm << "<empty>" ;
    else for( int i : c.v ) stm << i << ' ' ; // with a space between each int

    // on the second line, write out the int member 'n'
    // and on the third line, write out the string member 's'
    return stm << '\n' << c.n << '\n' << c.s << '\n';
}


> When the program is opened, it then reads the text file and retrieves the contracts

Overload the stream insertion operator to read in an object of type contract from an istream.

For example (error handling elided for brevity):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
std::istream& operator>> ( std::istream& stm, contract& c )
{
    {
        std::string line ;
        // read the first line, skipping over empty lines
        while( std::getline( stm, line ) && line.empty() ) ;

        // assign the sequence of int in the line to the vector member 'v'
        std::istringstream strstm(line) ;
        c.v.assign( std::istream_iterator<int>(strstm), std::istream_iterator<int>() ) ;
    }

    // read the int member 'n' from the second line
    stm >> c.n ;

    // move over to the third line
    stm.ignore(1024,'\n') ;
    // read the string member 's' from the third line
    return std::getline( stm, c.s ) ;
}


Now, put them together to write out or read in a sequence of contracts:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int main()
{
    constexpr const char* path2file = "contracts.txt" ;
    {
        std::vector<contract> seq = { { {1,2,3,4,5,6,7}, 25, "hello world" },
                                      { {}, 3, "hello again" },
                                      { {6,22,17}, 9, "bye" } } ;

        // write out the contracts in the sequence to a file
        std::ofstream fout( path2file ) ;
        for( const auto& c : seq ) fout << c << '\n' ;
    }

    {
        // read the contracts in the file into the sequence of contracts
        std::ifstream fin( path2file ) ;
        std::vector<contract> seq( ( std::istream_iterator<contract>(fin) ),
                                   ( std::istream_iterator<contract>() ) ) ;

        // just to verify that things have been read back correctly
        for( const auto& c : seq ) std::cout << c << "\n--------\n\n" ;
    }
}
Oooooooookay. So I have to store each value individually as its primitive type. What a pain. Thanks so much to you both!
Alrighty. So I wrote the function for the << operator:
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
//overloaded function to output contract objects
ostream& operator<< (ostream& stm, const Contract& c){
	stm << c.name << '\n'; //1st line
	stm << c.totalPrice << '\n'; //2nd line
	//line three is item names vector
	//one item followed by delimiter character '$'
	if(c.items.empty())
		stm << "<empty>";
	else 
		for each (string s in c.items)
			stm << s << '$';
	stm << endl; //end of items vector
	//line four is the amount vector
	//same delimiter
	if(c.amount.empty())
		stm << "<empty>";
	else
		for each (int i in c.amount)
			stm << i << '$';
	stm << endl; //end of amount vector
	//line five is amountObtained vector
	//same delimiter
	if(c.amountObtained.empty())
		stm << "<empty>";
	else
		for each (int i in c.amountObtained)
			stm << i << '$';
	return stm << endl; //end of amountObtained vector
}


and I think it's okay (sorry, JLB, your formatting was a bit difficult for me to understand!). So what I want each contract to look like in the file is:

name of person contract is for (string)
price to be paid (int)
names of items separated by delimiter '$' (strings)
amount of each item desired separated by same delimiter (ints)
amount of each item we have obtained, same delimiter (ints)

So something like:
Generic User
500
resources$more resources$even more resources$
5$7$12$
0$6$2$


The delimiters for the ints probably aren't needed, I put them there just because...

Anyway, I really don't understand how to write the operator>> function. On top of that, I don't know what it's supposed to return! Thank you very much for your help and especially your patience. I've only ever done this with Java where all you need is a scanner object!
Last edited on
bump for help, please?
i noticed you did not convert it to a c string. this is needed and exceptions are another way to check if the file already exists.

string FILE = "contracts.dat"; //filename

//file creation, will fail if it already exists (I think)
dataOut.open(FILE,ios::out);

file needs to be converted as a c string in order for this to work so do
dataOut.open(file.c_str())

for file checking that it exists you should include exceptions here is one i use for basic file creation/allocation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
   std::string filename = "stuff.txt";  
    std::ifstream file;
    file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
    //open file
    try{
        file.open(filename.c_str());
    }
    //catch error
    catch (std::ofstream::failure &e){
        std::cout << "An error has occured";
        file.close(); 
        return 0; 
    }
Last edited on
> I really don't understand how to write the operator>> function.
> On top of that, I don't know what it's supposed to return!

There are several tutorials on the topic scattered all around the web.
For instance, the first result returned by google for 'overloading+stream+operators+tutorial' :
http://www.learncpp.com/cpp-tutorial/93-overloading-the-io-operators/
Topic archived. No new replies allowed.