Input-output error from the text file in C ++ the program

Astralex
Astralex used Ask the Experts™
on
Hello!

I am a beginner in programming by C ++. This is my first question on EE also.

I am learning the book of Bjarne Stroustrup "Programming. Principles and Practice Using C ++".
I'm using MS Visual Studio 2019 for creation of C ++ programs.

I am performing following exercise:
“Define an Order class with (customer) name, address, data, and vector<Purchase> members. Purchase is a class with a (product) name, unit_price, and count members. Define a mechanism for reading and writing Orders to and from a file. Define a mechanism for printing Orders. Create a file of at least ten Orders, read it into a vector<Order>, sort it by name (of customer), and write it back out to file. Create another file of at least ten Orders of which about a third arc the same as in the first file, read it into a list<Order>, sort it by address (of customer), and write it back out to file. Merge the two files into a third using std:: merge().”
I wrote the program consisting of the following files:
1.      std_lib_facilities.h
Header file from B.Stroustrup
2.      Exersize_9.h
This file contains declarations of functions.
3.      Address_definition_file.cpp , Date_definitions_file_.cpp, Order_definitions_file.cpp, Purchase_definition_file.cpp
These files contain definitions of functions
4.      Exersize_9.cpp
This is a main file.
5.      File_input.txt
Input data are in this file. For an example, I took three objects
6.      File_output.txt
 Result data are in this file

And at last problem:
Objects of class Order are stored In the File_input.txt file. As I wrote above, I placed three objects in this file. The code should copy all data from the File_input file into the File_output file. But the program copies the first two objects. The program does not copy the last (third) object.
Please, help to find a program error, and also as to correct it.

P.S: I checked all files before sending. The program is compiled and carried out.
Copy_Project_Cpp.zip
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Most Valuable Expert 2012
Distinguished Expert 2018

Commented:
It has been years since I've done C or C++ o I'm going from really old brain cells.  I think the issue is on the index increment.

See if this magically fixes things:
for (int i = 0; i < v.size(); i++)
NorieAnalyst Assistant

Commented:
I'm not sure if this is the 'correct' solution but it does work.

The code I added is on line 26.
// Exersize_9.cpp : Этот файл содержит функцию "main". Здесь начинается и заканчивается выполнение программы.
//

#include "Exersize_9.h"

template<typename T>
ofstream& operator<< (ofstream& ofs, const vector<T>& v)
{
	for (int i = 0; i < v.size(); i++)
	{
		ofs << v[i] << endl;
	}

	return ofs;
}

template<typename T>
ifstream& operator>> (ifstream& ifs, vector<T>& v)
{
	T d;
	while (ifs >> d)
	{
		v.push_back(d);
	}
	
	v.push_back(d);

	return ifs;
}


int main()
{
	
	try 
	{
		string name = "File_input.txt";
		//string name_1 = "Test.txt";
		ifstream ifs{ name};
		if (!ifs) error("Input file can not open!");
		//vector<Purchase> a;
		vector<Order> a;
		ifs >> a;
		string name_2 = "File_output.txt";
		ofstream ofs{ name_2 };
		if (!ofs) error("Output file can not open!");
		ofs << a;
	}
	catch (exception& e) {
		cout << e.what() << endl;
	}
	catch(...) {
		cout << "Ooops" << endl;
	}
    
}

Open in new window

Author

Commented:
Hi, Norie!

Yes, your changes helped, but for a special case (when the vector <Purchase> is in the Order object). I still don’t understand why my program didn’t work ...In addition, if I delete the vector <Purchase> from the Order class in the original program (without your changes), then my original program will work correctly (I have embedded code that confirms my words). But for some reason, if you do not delete vector <Purchase>, then the original program reads everything from the "File_input" except the last object of the Order class. Please tell me why the program works this way and how to make the program work correctly in the general case (that is, with or without vector <Purchase>).
Copy_Project_Second_Version_Cpp.zip
The Template is fine, you don't have to change it.

       while (ifs >> d)
	{
		v.push_back(d);
	}

Open in new window

As long as there is no error when reading in order, you add (push_back) the order to the vector.

The problem is your reading from the file on the vector<Purchase> in your file "Purchase_definition_file.cpp":
        while (ifs >> d) {
		//cout << "d = " << d << endl;
		v.push_back(d);
		ifs >> ch1;
		//ifs >> ch2;
		//cout << "ch1 = " << ch1 << endl;
		//cout << "ch2 = " << ch2 << endl;
		//ifs.putback(ch2);
		if (ch1 != '{') {
			ifs.putback(ch1);
			return ifs;
		}
	    ifs.putback(ch1);
	}

Open in new window


When you read ahead to see if the next char is '{' or not, to see if there is another purchase, if not you putback the char and return.

Notice, Purchase is the last thing you read in for your Order, so if it's not '{', you assume it is going to be next order to be read in. That's fine for the first 2 Orders.

For the last Order, the next char is end-of-file, so there's an error on the stream when you attempt to read ahead.... Hence, from the template code, your last Order will not be added (push_back) to the Order list.

To fix it, you just have to clear the error before you exit the function.

But Purchase is your last input, if you clear error, then all errors happened before that will be clear also. Therefore, you have to check if there is already an error on the stream, then just return before attempt to read.

And it applies to all your input of all the other things too, like address, date, etc.

Here is the modified code for the Purchase reading:
ifstream& operator>> (ifstream& ifs, vector<Purchase>& v)
{
   // if there is already error on the stream, just return it
   if (!ifs) return ifs;

	Purchase d;
	char ch = '0';

	// There must be at lease ONE purchase
	// Have to look ahead if there is a '{', if not error (no purchase existed)
	ifs >> ch;
	ifs.putback(ch);
   if (ch != '{') {
      ifs.clear(ios::failbit);  // set error
      return ifs;
   }

	while (ifs >> d) {
		v.push_back(d);
		
		ch = '0';  // have to change ch to something else other than '{'
		ifs >> ch;
		ifs.putback(ch);
		if (ch != '{') {
         ifs.clear();  // Have to reset error, assuming there will be an Order next
			return ifs;
		}
	}

	return ifs;
}

Open in new window


Now for the other files, just add:
if (!ifs) return ifs;

Open in new window

to the beginning of each file reading function.

So, in file "Date_definitions_file_.cpp" :
ifstream& operator>>(ifstream& ifs, Date& dd)
{
   // if there is already error on the stream, just return it
   if (!ifs) return ifs;
   ....
}

Open in new window


file "Address_definition_file.cpp" :
ifstream& operator>>(ifstream& ifs, Address& a)
{
   // if there is already error on the stream, just return it
   if (!ifs) return ifs;
   ...
}

Open in new window


File "Order_definitions_file.cpp" is a little bit different... :
ifstream& operator>>(ifstream& ifs, Order& o)
{
	string name, surname;
	Date d;
	Address a;
	vector<Purchase> p;

	ifs >> name;
	ifs >> surname;
	ifs >> d;
	ifs >> a;
	ifs >> p;
	// THIS IS THE NEW LINE
	if (!ifs) return ifs;  // if any error existed from the above, just return
	//cout << "Vector<Purchase> is " << p << endl;
	o = Order(name, surname, d, a, p);
	cout << o << endl;
	return ifs;
}

Open in new window


Hopefully, the explanation is clear enough.

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial