Iostream, conversation and exceptions

Pages: 12
Hi everybody. I am writing a program that will take in values through inFile, all these values will be read as strings and converted to doubles. The range is 0.0 to 10.0, all values that cannot be converted will be written to a certain file "ReadErrors" and all values that are out of range will be written to "RangeErrors". To do this i am trying to implement exceptions.

The program is compiling and running even though it crashes after it has run to its end. Except the crash, the program only reads 700~ of my 20 000 values, and only finds 10 range errors even though there is around 200 of them.

What am I doing wrong?

DataFileReader.H
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
#ifndef DataFileReader_H
#define DataFileReader_H

#include <string>
#include <iostream>
#include <fstream>

using namespace std;

template <typename T>
class DataFileReader {

private:
	string dataFileName;
	string errorFileName;
	ifstream dataFileStream;
	ofstream errorFileStream;

public:
	DataFileReader(string aDataFileName, string aErrorFileName) : dataFileName(aDataFileName), errorFileName(aErrorFileName), dataFileStream(""), errorFileStream("") {};
	
	~DataFileReader() { dataFileStream.close(); errorFileStream.close(); }
	
	void openFiles(); // throw (runtime_error);
				
	bool readNextValue(T &aValue);

};

#endif 


DataFileReader.cpp
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
#include "DataFileReader.h"
#include <iostream>
#include <ostream>
#include <typeinfo>
#include <istream>
#include <sstream>

template <typename T>
void DataFileReader<T>::openFiles() {
	dataFileStream.open(dataFileName);
	errorFileStream.open(errorFileName, ios::app);
	if (!(dataFileStream.is_open() && errorFileStream.is_open()))
		throw(runtime_error("Couldn't open at least one of the files."));
}

template <typename T>
bool DataFileReader<T>::readNextValue(T &aValue) {
	ios_base::iostate mask = ios::eofbit | ios::failbit | ios::badbit;
	dataFileStream.exceptions(mask);
	while (true) {
		string readValue;
		try {
			if (!(dataFileStream >> readValue)) // did I/O fail?
				return false;
			istringstream iss(readValue);
			if (!(iss >> aValue)) // did parsing fail?
				return false;
			return iss.eof(); // was the entire value consumed?
		}
		catch (bad_cast &bc) {
			errorFileStream << readValue << " - " << bc.what() << endl;
		}
		catch (ios_base::failure &eo) {
			if (dataFileStream.eof())
				return false;
		}
	}
}


DataFilter.H
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef DataFilter_H
#define DataFilter_H
#include "DataFileReader.h"

template <typename T>
class DataFilter {

private:
	DataFileReader<T> fileReader;
	T min, max;

public:
	DataFilter(DataFileReader<T> *aReader, T aMin, T aMax) : fileReader(*aReader), min(aMin), max(aMax) {};
	/* pre: aReader points to an instance of DataFileReader<T> for which openFiles() has been succesfully called. */
	bool getNextValue(T &aValue); // throw (range_error); 
		
};

#endif



DataFilter.cpp
1
2
3
4
5
6
7
8
9
10
11
#include "DataFilter.h"

template <typename T>
bool DataFilter<T>::getNextValue(T &aValue) {
	if (fileReader.readNextValue(aValue)) {
		if (aValue > max || aValue < min)
			throw(range_error("Outside of range"));
		return true;
	}
	return false;
}


DataTestClass.cpp
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
#ifndef DataTestClass_H
#define DataTestClass_H

#include "DataFilter.cpp"
#include "DataFileReader.cpp"
#include <iostream>
#include <fstream>
#include <string>
#include <exception>
#include <vector>

//---------------------------------------------------------------------------

vector<double> vec;
int rangeErrorCounter = 0;

void printResults() {
	double total(0), avarage(0);
	vector<double>::iterator first, last, it;
	first = vec.begin();
	last = vec.end();
	for (it = first; it != last; ++it)
		total += *it;
	avarage = total / vec.size();

	cout << "Numeric values read:\t" << vec.size() + rangeErrorCounter << endl;
	cout << "Values outside of range:\t" << rangeErrorCounter << endl;
	cout << "Total:\t\t\t" << total << endl;
	cout << "Avarage:\t\t" << avarage<< endl;
}

int main(int args[])
{
	DataFileReader<double> dr("Values.dat", "ReadErrors.dat");

	try {
		dr.openFiles();
	}
	catch (runtime_error &rt) {
		cout << "Error reading files: " << rt.what() << endl;
		return 0;
	}

	DataFilter<double> df(&dr, 0.0, 10.0);
	ofstream os("RangeErrors.dat");

	if (os.is_open())
		while (true) {
			double value;
			try {
				while (df.getNextValue(value))
					vec.push_back(value);
				printResults();
				os.close();
				return 0;
			}
			catch (range_error) {
				rangeErrorCounter++;
				os << value << endl;
			}
		}
	else
		cout << "Couldn't open RangeErrors.dat" << endl;
}

#endif 
Last edited on
Hello Thesar,

While I load your program these are my first observations:

In the "DataFileReader.H" file lines 4, 5 and 6 should not be in a header file like this. The "#include" file will be picked up when you include this header file in a ".cpp" file. And never put using namespace std; in a header file. this is asking for big trouble. Read this for a better explanation http://www.lonecpluspluscoder.com/2012/09/22/i-dont-want-to-see-another-using-namespace-xxx-in-a-header-file-ever-again/

I am not sure what I will do for an input file yet. Is there any possibility of access to the input file you are using? It would be a big help.

Andy
@Handy Andy.

So you think the includes may cause this problem?

I just uploadet my Values.dat file, https://ufile.io/rioxy. Contains rougly 20000 values.
Hello Thesar,

I will have to admit that for everything that you did wrong it actually compiles. I have not run the program yet to test it, but am about to.

Your link came up as file not found.

I can not say if the placement of the include files is a problem yet, but it is not the proper way of doing you include files.

In my ".cpp" file I start with the usual includes like:
1
2
3
4
5
6
7
8
9
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
#include <limits>
#include <chrono>
#include <thread>

#include "headerFle.hpp" 

This way by the time "headerFile.hpp" is included in the ".cpp" file it is covered by the preceding "#includes". The last three header files are used for:
1
2
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');  // <--- Requires header file <limits>.
std::this_thread::sleep_for(std::chrono::seconds(3));  // Requires header files "chrono" and "thread" 
Both of these come in very handy.

The tips are more for the future than this program because when I tried to change things around I had many errors to try and fix and a couple I am not even sure what to fix. For now the only thing I would change is removing using namespace std; and going through the files to qualify what is needed.

Hope that helps,

Andy
I think the free version only keeps the file for 1 h, here it is again: https://ufile.io/vg3yy

I think what i may be doing wrong is the exceptions at line 33,34,45 in DataReadFile.cpp.

I haven't still got a grasp of how exceptions work.
The program is compiling and running even though it crashes after it has run to its end.

Exactly where does it crash? Hint your debugger should be able to tell you where it detected the problem.

Except the crash, the program only reads 700~ of my 20 000 values,

This is because line 741 has bad data. It starts with the 'f' character which is not a valid digit.

and only finds 10 range errors even though there is around 200 of them.

What are some examples for some of these "range errors"?

Edit: Better yet, what constitutes valid rages?

Just a quick question: Why are the stream instances pointers instead of just "regular" instances? And where are you deleting the memory you newed?



Last edited on
Where is line 741?


It crashes after i get the result

Lõsta numeriska võrden: 741 (number of values read)
Võrden utanf÷r intervallet: 5 (number of out side of range)
Summa: 3799.12
Medelvõrde: 5.16184
---------------------------------------

The errors are many:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
'1>c:\users\user\source\repos\exeptions\exeptions\datafilereader.cpp(33): warning C4101: 'eo': unreferenced local variable
1>c:\users\user\source\repos\exeptions\exeptions\datafilereader.cpp(17): note: while compiling class template member function 'bool DataFileReader<double>::readNextValue(T &)'
1>        with
1>        [
1>            T=double
1>        ]
1>c:\users\user\source\repos\exeptions\exeptions\datafilter.cpp(5): note: see reference to function template instantiation 'bool DataFileReader<double>::readNextValue(T &)' being compiled
1>        with
1>        [
1>            T=double
1>        ]
1>c:\users\user\source\repos\exeptions\exeptions\datafilereader.cpp(9): note: while compiling class template member function 'void DataFileReader<double>::openFiles(void)'
1>c:\users\user\source\repos\exeptions\exeptions\datatestclass.cpp(37): note: see reference to function template instantiation 'void DataFileReader<double>::openFiles(void)' being compiled
1>c:\users\user\source\repos\exeptions\exeptions\datatestclass.cpp(34): note: see reference to class template instantiation 'DataFileReader<double>' being compiled 


I think the try loop in DataReadFile.cpp can be the issue, but i dont know how or why.
Last edited on
Just a quick question: Why are the stream instances pointers instead of just "regular" instances? And where are you deleting the memory you newed?


I tried it as a pointers but seem to forgot to make them regular after i removed the delete ~ from the deconstructor. A bad range is everything under 0.0 and above 10.0
Ahh you mean line 741 in the values.dat

Yes, some of the values are invalid and thats why i am trying to use exceptions that will throw these errors to the Error file. So the issue really seems to be my exception? Am new to exception, never used them
Last edited on
Yes, some of the values are invalid and thats why i am trying to use exceptions that will throw these errors to the Error file.

IMO, this is misusing exceptions since this is really not an exceptional condition. Why not just check the state of the stream and if the stream is in an error state (other than eof() (eof() is a "normal" condition)) just clear the error state, and retrieve the problem value as a string then write that string to your "error" stream.

So the issue really seems to be my exception?

I don't know, where does your debugger indicate that the problem is located?

It crashes after i get the result

That's not specific enough. Run the program with your debugger. The debugger will tell you exactly where the problem is detected, allow you to back trace the problem to somewhere in your code and let you view the values of the variables at the time of the crash.

A bad range is everything under 0.0 and above 10.0

Okay so there is no reason to throw an exception for these values, just write the values to your "error" file instead of your "good" file.

I tried it as a pointers but seem to forgot to make them regular after i removed the delete ~ from the deconstructor.

But why are you using pointers at all. There is no need to use pointers in this program.


Last edited on

[quote]
A bad range is everything under 0.0 and above 10.0

Okay so there is no reason to throw an exception for these values, just write the values to your "error" file instead of your "good" file.
[/quote]

The reason was stated in the original post: the assignment requires it... its just one of those things. I'm guessing the idea is if a student can make it work that way, then they will be able to make it work in other situations.

ETA: Geez, no nested quotes?!? Guess I'm used to PHPbb.
Last edited on
Hello Thesar,

I believe I have finally figured it out.

Starting in main in the second while loop you call "df.getNextValue(value)" which calls "fileReader.readNextValue" in the if statement.When you reach the input "f0.12376" and after I changed the code in "DataFileReader<T>::readNextValue" you return false to "getNextValue()" which returns false back to main.

In the second while loop, when it fails, you continue with:
1
2
3
printResults();
os.close();
return 0;

Which ends the program before you are finished.

I ended up moving these three lines after the else statement where they belong.

I changed and added this in the "DataFileReader<T>::readNextValue" function of the "DataFileReader.cpp" file:
1
2
if (!(iss >> aValue)) // did parsing fail?
	throw std::invalid_argument("invalid arguement conversion failed");

And added this:
1
2
3
4
5
6
catch (const std::invalid_argument& ia)
{
	cout <<ia.what() << " - " << readValue << endl;
	errorFileStream << readValue << " - " << ia.what() << endl;
	return false;
}

You can comment out or remove the "cout" if you do not need it.

This helped, but the biggest change is in main. Doing this the "ReadErrors" file now contains 26 entries and the "RangeError" file contains 201 entries.

Now the only problem I have is when the program ends the line dataFileStream.exceptions(mask); gives me a run time error of Microsoft C++ exception: std::ios_base::failure at memory location 0x0019F840. I expect that the memory location will change, but I am not sure what is causing this error.

Hope that helps,

Andy
Now the only problem I have is when the program ends the line dataFileStream.exceptions(mask); gives me a run time error of Microsoft C++ exception: std::ios_base::failure at memory location 0x0019F840. I expect that the memory location will change, but I am not sure what is causing this error.

Again this is where using a debugger will be of great assistance! The debugger will tell you exactly where it detects the problem and will allow you to view the program stack and values of the variables at the time of the crash.

@jlb,

I was using the IDE in debug mode to run the program as I always do. The full message when the window popped up is:
Unhandled exception at 0x74803EF2 in Iostream, conversation and exceptions EP.exe:
Microsoft C++ exception: std::ios_base::failure at memory location 0x009DF4A8.

And the line it pointed to is the second line of the function "DataFileReader<T>::readNextValue" in the "DataFileReader" file.

I must be really tired today because I am not seeing or understanding the problem.

Earlier it was giving me a run time error on the dtor of the "DataFileReader" class, but that eventually went away and this new one came up.

Andy
Did you use all the files that the OP is using?

Also, IMO, the "exceptions" call should be in the constructor not the Read function.

Also since this involves templates I would put both the template definition and the implementation in the header file (this goes for both classes).

Next have you tried putting a breakpoint at the first line in that function and then check the status of the stream before calling the exception() function (test each "fail" flag).

Also you should be able to backtrace to somewhere in user code (not the std library code) and view the different variables. The problem appears to be that something is throwing outside the catch block or some condition is not being caught.

I would help but the program supplied doesn't even compile on my machine so until the OP provides a small complete program that compiles and illustrates the problem I can't be of much more help.

Thank you Handy Andy, the program now handles the conversion exceptions real fine. But when i moved the three lines in main, it doesnt print the result anymore..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	if (os.is_open())
		while (true) {
			double value;
			try {
				while (df.getNextValue(value))
					vec.push_back(value);
			}
			catch (range_error) {
				rangeErrorCounter++;
				os << value << endl;
			}
		
		}

	else
	{
		cout << "Couldn't open RangeErrors.dat" << endl;
	}

	printResults();
	os.close();
	return 0;
}


I think the problem with the memory is because i still had some pointers left where they shouldnt. One of the constructors where default delete, so i default inizialize them now in line 13


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#ifndef DataFilter_H
#define DataFilter_H
#include "DataFileReader.h"

template <typename T>
class DataFilter {

private:
	DataFileReader<T> fileReader;
	T min, max;

public:
	DataFilter() : fileReader("Values.dat", "ReadErrors.dat"), min(0.0), max(10.0) {}
	DataFilter(DataFileReader<T> &aReader, T aMin, T aMax) : fileReader(aReader), min(aMin), max(aMax) {} 
	~DataFilter() {}
	/* pre: aReader points to an instance of DataFileReader<T> for which openFiles() has been succesfully called. */
	bool getNextValue(T &aValue); // throw (range_error); 
								  /* pre: an earlier call to getNextValue() has not returned false.
								  post: true is returned if aValue holds a value read from aReader. If a value could not be read, false is returned.
								  If a value is read but is not within the interval specified by aMin and aMax parameters to the constructor, a range_error exception is thrown. */
	bool readNextValue(T &value) { return fileReader.readNextValue(value); }

	void openFiles() { fileReader.openFiles(); }
};


And i only need to create a DataFilter object in main now, instead of a DataFileReader which is used as a parameter to create DataFilter object


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
	//DataFileReader<double> dr("Values.dat", "ReadErrors.dat");
	DataFilter<double> df;

	try {
		df.openFiles();
	}
	catch (runtime_error &rt) {
		cout << "Error reading files: " << rt.what() << endl;
		return 0;
	}

	//DataFilter<double> dfs(dr, 0.0, 10.0);
	ofstream os("RangeErrors.dat");

	if (os.is_open())
Hello Thesar,

Moving the three lines to the end of the program is where they should be. The problem is that both of us are not reaching those lines because of other problems.

Like you I do not have much experience beyond the basice of using the try/catch. These two lines have me baffled because I have not seen anything like them before:
1
2
ios_base::iostate mask = ios::eofbit | ios::failbit | ios::badbit;
dataFileStream.exceptions(mask);

I have yet to find anything that tells me how they work or what they are for, but line two is where my IDE stops when it has found all 26 numbers that begin with a letter.

Just to see what would happen I wrote a simple program to read the file and pull out all the problem numbers. I only used one try/catch in the program which worked out great.

For the most part your program works although like others I believe that you over used the try/catch and exceptions, but it does work to a point.

I have been wanting to mention in the "dataFileReader" file in the "readNextValue" function you have catch (bad_cast &bc). This only works with a "dynamic_cast" failure, so unless this come from the string stream you are not using a "dynamic_cast" anywhere and I do not believe that this will ever be reached.

I tried moving the "printResults();" back into the try block and at the end of the outer while loop and neither worked properly. The function was being called to often.

All I have to do is figure out what the problem is.

Hope that helps,

Andy
These two lines have me baffled because I have not seen anything like them before:

The first line is setting "mask" equal to each of the fail modes listed.

The second line is enabling exceptions for the stream using the mask provided.

By the way if you want to catch stream exceptions you should be catching "std::ios_base::failure".

But as I said using exceptions for this is really not the best practice, especially when you throw for eof() which is actually a normal situation.

Lastly as I mentioned earlier these two lines should probably be in the constructor, not every time you try to read the file.



@ jlb,

Thanks for the input. that is what I was thinking, but not sure.

Well its not my program, but there is is a catch for "ios_base::failure".

I tried putting those two lines in the ctor and the only difference is the run time error I get hit with now happens when the object is constructed.

Andy
Hello Thesar,

Perseverance pays off I finally manages to make it run.

In main I added a catch block:
1
2
3
4
catch (ios_base::failure &eo)
{
	break;
}

This is the only way out of the outer while loop. And this is needed to end the program.

In the file "dataFileReader -> readNextValue" I changed the if statement in the try block to:
1
2
3
4
5
if (!(dataFileStream >> readValue)) // did I/O fail?
{
	throw std::ios_base::failure("EOF");
	return false;
}

I do not think the return statement is ever reached, but it did not seem to make any difference.

For the output I came up with this:
 The total number of usable numbers is:       19773
 The total number of unusable numbers is:        26
 The total number of out of range numbers is:   201
                                              -----
 The total numbers processed is:              20000


And your output is:
LΣsta numeriska vΣrden: 19974
VΣrden utanf÷r intervallet:     201
Summa:                  99702.5
MedelvΣrde:             5.04235


I did not do a sum or average, so I do not know if these two numbers are correct.

Hope that helps,

Andy
Pages: 12