#include <iostream>
#include <fstream>
#include <string>
#include <getcoeff.h>
usingnamespace std;
long getCoeff(string file, long nbrCells, long* a)
{
long rc;
ifstream s(file.c_str());
if (s)
{
for (int i=0; i < nbrCells; i++)
s >> a[i];
rc = 0;
}
else
{
cout << "Cannot open " << file << "." << endl;
rc = 1;
}
s.close();
return rc;
}
This works fine...when there's nothing but valid integer values in the file. Suppose I want to "harden" this routine, to accommodate comments and invalid data in the file.
The reason this is a C++ question is: I don't know how much I can rely (if at all) on the "smarts" of the >> operator in this routine. Will this do anything for me in terms of rejecting non-integer information? If so, is this documented anywhere?
Thanks...I'll look at those references. What I meant by "harden" was just to make the routine more robust, so that it would only process valid data, would ignore invalid data, and so on. For example, if lines 1 through 99 were valid integers, and line 100 said something like "line 100" it would skip "line" and use the number 100. Or, if it said "100 line" it would take the 100 and skip line.
I suppose I could modify it to allow for comments, C++ style, and skip everything after a "//". I'm willing to do the work myself, but I was hoping for some insight into what (if anything) the operator does for me. I remember from my C++ class that C++ was a lot "smarter" than C when it came to type-sensitive data processing on reads and writes.
The single-line C++ comment is terminated with the new-line character. So all you've got to do is ignore everything until a new-line is reached, then start reading the file as normal.
mzimmers wrote:
I was hoping for some insight into what (if anything) the operator does for me. (sic)
// Comments are just like C++ comments... but that isn't necessarily the case.
// You could use "#" or ";" if you like.
// You can also make commentary more complex if you like, but the method to
// handle it becomes more complex as well...
//
// This routine does not assume that whitespace leads commentary. That means that you
// can have comments starting (without intervening whitespace) after both valid and invalid
// input. This works because valid input will stop at the first non-integer character, and invalid
// input will have a "//" somewhere in it.
//
bool is_comment( const string& s )
{
return s.find( "//" ) != string::npos;
}
// This thing lets us overload the extraction operator to read our ints with our special method
struct my_int
{
int& n;
my_int( int& n ): n( n ) { }
};
// This is our special method to read ints -- skipping bad integers and C++ commentary
istream& operator >> ( istream& ins, my_int& n )
{
// only continue if all OK
if (ins && !ins.eof())
// repeat while necessary to get an integer
while (true)
{
// get an integer -- if OK then we're done
if (ins >> n.n)
break;
// otherwise, get it as a string
string s;
ins.clear();
ins >> s;
// special handling for comments
if (is_comment( s ))
ins.ignore( numeric_limits <streamsize> ::max(), '\n' );
}
// return the stream
return ins;
}
Now, change line 17 of your code to:
16 17
for (int i=0; i < nbrCells; i++)
s >> my_int( a[i] );
This code assumes that your integer data is surrounded by whitespace. If that is not the case, we can make some modifications, but it is slightly more involved to read the string.
BTW... I haven't had time to test this code. (I could have made a mistake!) If it doesn't work right let us know and we'll fix it.
By looking at the error message I can say that the instantiation, s, doesn't have an overloaded >> operator that takes the operand that you gave it. Here's an example:
I'm sorry, but I don't understand what you're trying to tell me here.
I'm beginning to think that an overloaded operator is probably overkill, since I truly only use this operator in one place. But, I'm not sure that inline coding this will fix the issue.
What I'm basically saying is that if an operator doesn't support the type of the passed operand, the compiler will either attempt to convert the type, or throw an error at you. This is what's happening in my previous post.
In Simple, I overloaded the >> operator to accept an instantiation of std::ifstream. However, I passed it a std::string instantiation, which I didn't provide, so the compiler cried. Instead, I have to overload the >> operator again so it accepts a std::string instantiation. If I overload an operator to accept a given type, I'm giving the operator knowledge on how to handle a specific type.
#include <iostream>
#include <fstream>
#include <string>
#include <limits>
usingnamespace std;
// Comments are just like C++ comments... but that isn't necessarily the case.
// You could use "#" or ";" if you like.
// You can also make commentary more complex if you like, but the method to
// handle it becomes more complex as well...
//
// This routine does not assume that whitespace leads commentary. That means that you
// can have comments starting (without intervening whitespace) after both valid and invalid
// input. This works because valid input will stop at the first non-integer character, and invalid
// input will have a "//" somewhere in it.
//
bool is_comment( const string& s )
{
return s.find( "//" ) != string::npos;
}
// This thing lets us overload the extraction operator to read our ints with our special method
struct my_int
{
mutablelong& n;
my_int( long& n ): n( n ) { }
};
// This is our special method to read ints -- skipping bad integers and C++ commentary
istream& operator >> ( istream& ins, const my_int& n )
{
// repeat while necessary to get an integer
while (ins && !ins.eof())
{
// get an integer -- if OK then we're done
if (ins >> n.n)
break;
// otherwise, get it as a string
string s;
ins.clear();
ins >> s;
// special handling for comments
if (is_comment( s ))
ins.ignore( numeric_limits <streamsize> ::max(), '\n' );
}
// return the stream
return ins;
}
long getCoeff(string file, long nbrCells, long* a)
{
long rc;
ifstream s(file.c_str());
if (s)
{
for (int i=0; i < nbrCells; i++)
s >> my_int( a[i] );
rc = 0;
}
else
{
cout << "Cannot open " << file << "." << endl;
rc = 1;
}
s.close();
return rc;
}
int main( int argc, char** argv )
{
if (argc != 2)
{
cout << "usage:\n " << argv[ 0 ] << " FILENAME\n";
return 1;
}
long ns[ 12 ];
long rc = getCoeff( argv[ 1 ], 12, ns );
for (unsigned n = 0; n < 12; n++)
cout << n << ":\t" << ns[ n ] << endl;
cout << rc << endl;
return 0;
}
Tests:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// This is a test
1 2 3//hello
four 4 five//world 5
6 7 8
9
10 12 13
The extraction operator is convenient because it requires minimal changes to your existing code... However, you will notice that it really is just a function call. Instead of the fancy struct and extraction operator, you really only need to call a function, say:
That does compile now. Storage modifiers aren't exactly my strong point in C++, so would you mind telling me what we've done with the const/mutable keywords?
Temporary object references must be passed to functions as const -- meaning that you cannot modify them (a safety feature in the language to prevent unwanted side-effects). Except, in this case, we do want the side-effects (to modify the object it references), so we tell the compiler as much using the mutable keyword.
The if (f >> foo) is a common C++ idiom that means, "if the item (foo) was successfully obtained from the stream (f) [then do something]." You'll often find it in loops like:
1 2 3 4 5 6 7 8 9
// Get all the integers we can from the standard input
// (until there is no more input, or until something other
// than an integer is there).
int n;
vector <int> ns;
while (cin >> n)
{
ns.push_back( n );
}
Again, you can dump all the fancy C++ stuff and just write it as a simple (normal) function, like:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
void extract( istream& ins, long& n )
{
// repeat while necessary to get an integer
while (ins && !ins.eof())
{
// get an integer -- if OK then we're done
if (ins >> n)
break;
// otherwise, get it as a string
string s;
ins.clear();
ins >> s;
// special handling for comments
if (is_comment( s ))
ins.ignore( numeric_limits <streamsize> ::max(), '\n' );
}
}
//loop through stream
{
stream >> var;
if( stream.fail() ) {
//whoops, non-conforming data
stream.clear(); //ignore the problem or possibly do something else
}
}
What's wrong with a simple solution like that?
(Or you could even use exception handling if you don't like the if-statment...)
How is that different than the solution I suggested?
Oh wait... your solution doesn't remove the invalid input from the stream...
And it doesn't address the OPs distinction between invalid data and comments.
Duoas, if you're still reading this, I need to make a change to this routine.
It turns out that the array I'm reading into isn't an array of longs, but an array of objects. I guess the right way to do this is to overload the >> operator for the object, but...how do I distinguish between this usage of ">>" and the bitshift usage? I've overloaded the bitshift operator as follows:
Extraction operators don't take const references. Also, they work on input streams.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
istream& operator >> ( istream& ins, MyClass& obj )
{
// This is where you extract the pieces of your object and assemble them
// into the argument obj.
// Coding extraction stuff is not too simple. Remember that you must leave the stream
// in a reasonable state. If something goes wrong, make sure to set the failbit
// http://www.cplusplus.com/reference/iostream/ios/setstate/
...
// Don't forget to return the input stream reference...
return ins;
}