C++ Read in file with only numbers (doubles) - c++

I'm trying to read in a file that should contain only numbers in it. I can successfully read in the entire file if it meets that criteria, but if it so happened to have a letter in it, I need to return false with an error statement.
The problem is I'm finding it hard for my program to error when it finds this character. It can find it no problem, but when it does, it decides to just skip over it.
My code to read in the file and attempt to read in only numbers:
bool compute::Read (ifstream& stream)
{
double value;
string line;
int lineNumber = 1;
if (stream)
{
while (getline(stream, line))
{
lineNumber++;
istringstream strStream(line);
while (strStream >> value)
{
cout << value << endl;
}
}
}
return true;
}
The input file which I use for this is
70.5 61.2 A8 10.2
2
Notice that there is a non-number character in my input file. It should fail and return false at that point.
Currently, all it does is once it hits the "A", it simply returns to the next line, continuing the getline while loop.
Any help with this would be much appreciated.

The stringstream does catch those errors, but you're doing nothing to stop the enclosing loop from continuing when an error is found. You need to tailor your main loop so that it stops when the stringstream finds an error, which you can't do if the stringstream is being reconstructed on each iteration. You should create a for() loop instead and construct the stringstream in the declaration part. And the condition to the loop should be "as long as the stringstream and stream do not catch an error". For example:
for (std::istringstream iss; iss && std::getline(stream, line);)
{
iss.clear();
iss.str(line);
while (iss >> value)
{
std::cout << value << '\n';
}
}
Futhermore, it doesn't look like you need to use std::getline() or std::istringstream if you just want to print each value. Just do:
while (stream >> value) {
std::cout << value << '\n';
}
The above will stop when it finds an invalid character for a double.

You need the code to stop streaming but return false if it hasn't yet reached the end of the "input".
One way, possibly not the most efficient but still one way, to do that is parse a word at a time.
If you read first into a std::string and if it works (so the string is not empty) create an istringstream from that string, or reuse an existing one, and try streaming that into a double value.
If that fails, you have an invalid character.
Of course you can read a line at a time from the file, then split that into words, so that you can output a meaningful error message showing what line the bad text was found.
The issue of reading straight into doubles is that the stream will fail when it reaches end of file.
However it is possible to workaround that too because the reason for failing has an error status which you can check, i.e. you can check if it eofbit is set. Although the f in eofbit stands for "file" it applies to any stream not just files.
Although this method may sound better than reading words into a string first, I prefer that method in normal circumstances because you want to be able to report the error so you'll want to print in the error what was read.

Related

Ifstream in c++

I need some help with a code.
I need to take this information to my c++ code from another file, the last one is just like this:
Human:3137161264 46
This is what I wrote for it, it takes the word "Human" correctly but then it takes random numbers, not the ones written on the file I just wrote:
struct TSpecie {
string id;
int sizeGen;
int numCs; };
__
TSpecie readFile(string file){
TSpecie a;
ifstream in(file);
if (in){
getline(in,a.id,':');
in >> a.sizeGen;
in >> a.numCs;
}
else
cout << "File not found";
return a; }
Hope you can solve it and thanks for your help
3137161264 causes integer overflow leading to Undefined Behaviour.
So unsigned int sizeGen would be enough for this case, but consider long long (unsigned) int sizeGen too.
Edit 1: As pointed out by #nwp in comments to your question, you can also check your stream if any error has occured:
//read something and then
if (!in) {
// error occured during last reading
}
Always test whether input was successful after reading from the stream:
if (std::getline(in, a.id, ':') >> a.sizeGen >> a.NumCs) {
// ...
}
Most likely the input just failed. For example, the first number probably can't be read successful. Also note that std::getline() is an unformatted input function, i.e., it won't skip leading whitespace. For example the newline after the last number read is still in the stream (at least, since your use of std::getline() finishes on a colon, it will only create an odd ID).

Reading the input and checking if it ends with two newlines

I am writing a program in which the message is complete if the user presses the return key twice. And the way to check (as prescribed) is by checking to see if two consecutive '\n' occurrences have been read. I am confused as how to do this. Studying this thread at: How do I store a previous iteration in a while loop in C++?
I got some idea and did this:
for(new_advice; getline(cin, new_advice);) {
if(new_advice.substr(new_advice.length()-2,2).compare("\n\n") == 0) {
outstream<<endl;
outstream<<advice;
}
}
I got one error and a warning.
The error is:
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: basic_string
And the warning is
expression result unused (The expression is new_advice)
What should I do? Files and streams are a bit confusing (I am new to C++)
Thanks in advance :)
getline reads a line from the given stream and stores it in the variable given in the second parameter. It overwrites the contents of the given variable. Furthermore, it does not put the newline character into the variable. That means, you don't find two newline characters in the string, not even a single one.
When the user hits the return key twice, two lines are read by your C++ application, of which the second is empty.
You should detect that case by checking if the read line is empty. If this is the case, that means the user just hit the return key for the second time, and the previously read string was his actual input. That means that you need to store the input in a different string variable such that getline doesn't overwrite your variable.
Something like this:
string new_advice;
string input;
while (getline(cin, input)) {
if (input.empty()) {
// Do something with new_advice
}
else {
// Remember input for the case where the user hits return key again
new_advice = input;
}
}
for (std::string new_advice; std::getline(std::cin, new_advice);)
{
if ('\n' == std::cin.peek()) {
// Two consecutive new line characters. Do something with new_advice.
std::cin.ignore();
}
}
How about using e.g. std::string::rfind?
if (new_advice.rfind("\n\n") == new_advice.size() - 2)
{
// Last two characters were newlines
}

C++ istream operator>> bad-data handling

Every time I ask a question here on SO, it turns out to be some very dumb mistake (check my history if you don't believe me), so bear with me if you can here.
It feels like my question should be very popular, but I couldn't find anything about it and I've run out of ideas to try.
Anyway, without further ado:
I'm trying to overload the input operator>>. It's supposed to read one integer at a time from a file, skipping invalid data such as chars, floats, etc.
Naturally, I'm checking if(in >> inNum) to both get() the next token and check for successful get().
If successful, not much to say there.
If it fails, however, I assume that one of two things happened:
It stumbled upon a non-integer
It reached the eof
Here's how I tried to deal with it:
istream& operator>> (istream& in, SortSetArray& setB) {
bool eof = false;
int inNum = -1;
while(!eof) {
if(in >> inNum) {
cout << "DEBUG SUCCESS: inNum = " << inNum << endl;
setB.insert(inNum);
}
else {
// check eof, using peek()
// 1. clear all flags since peek() returns eof regardless of what
// flag is raised, even if it's not `eof`
in.clear();
cout << "DEBUG FAIL: inNum = " << inNum << endl;
// 2. then check eof with peek()
eof = (in.peek() == std::char_traits<char>::eof());
}
}
return in;
}
The file contains [1 2 3 4 a 5 6 7], and the program naturally goes into infinite loop.
Okay, easy guess, peek() doesn't consume the char 'a', and maybe in >> inNum also failed to consume it somehow. No biggie, I'll just try something that does.
And that's pretty much where I've been for the last 2 hours. I tried istream::ignore(), istream::get(), ios::rdstate to check eof, double and string instead of char in the file, just in case char is read numerically.
Nothing works and I'm desperate.
Weirdly enough, the approach above worked for a previous program where I had to read a triplet of data entries on a line of the format: string int int
The only difference is I used an ifstream object for that one, and an istream object for this one.
Bonus Question: inNum has the value of 0 when the hiccup occurs. I'm guessing it's something that istream::operator>> does?
Implementation description
try to read an int
if successful;
insert the read value to setB
next iteration
else;
clear error flags
check so that we haven't reached the end of the file
still more data? next iteration.
The above is the logic description of your function, but there's something missing...
In case we try to read a value, but fail, std::istream's handle these cases by setting the approriate error flags, but it will not discard any data.
The problem with your implementation is that upon trying to read invalid data, you will just try to read the same invalid data again.. over, and over, and over, inf.
Solution
After clearing the error flags you can use std::istream::ignore to discard any data from the stream.
The function's 1st argument is the max number of potential chars to ignore, and the 2nd is the "if you hit this char, don't ignore any more*.
Let's ignore the maximum amount of characters, or until we hit ' ' (space):
#include <limits> // std::numeric_limits
in.ignore (std::numeric_limits<std::streamsize>::max(), ' ');

Getting an out_of_range: vector error for c++ but can't figure out why

This is my code:
StockAccount::StockAccount() {
vector<string> temp;
string line;
std::ifstream stockfile("Results.txt");
if (stockfile.is_open()) {
while (stockfile.good()) {
getline(stockfile, line);
istringstream ss(line);
string token;
while (std::getline(ss, token, ',')) {
temp.push_back(token);
}
addStock(temp.at(0), temp.at(1), temp.at(2));
temp.clear();
}
stockfile.close();
} else {
cout << "Unable to open file" << std::endl << std::endl;
}
}
I know it isn't THAT efficient, that is what I am trying to fix. What it is supposed to be doing is:
Read that file line by line.
Parse each line and split it by comma.
Take those 3 values and use it in a method.
I am using that vector temp to store the values, add them to the function and then clear it so that it can be empty and used again to store the next ones ...etc.
I tried printing out each value BEFORE the temp.clear() and they all print out and THEN I get the error. So I know that temp.clear() is the problem. Perhaps I am using the wrong method, or there is a much better way.
I want to try and NOT use boost if possible.
This is the error I'm getting:
libc++abi.dylib: terminating with uncaught exception of type std::out_of_range: vector
Results.txt is a file that looks like.
goog,525,0
msft,34,10
and so on.
while (stockfile.good()) is wrong, and leads to your reading one extra, non-existent line.
That's because you're checking for stream validity before attempting to read a new line; if there's no new line to read, it's only after the call to getline that this condition would evaluate to false but by then it's too late and you're off trying to handle this non-existent line.
That non-existent line does not have three tokens on it, but you are performing no error checking on the tokenization, nor are you ever verifying the size of the vector temp.
So, when you come to try to access those three vector elements, which don't exist, an exception is thrown.
Your loop should look like this:
while (getline(stockfile, line)) {
istringstream ss(line);
string token;
// ...
}
Notice how I directly check for success in the loop condition, which will prevent the loop body from being executed if the actual getline fails.

c++: istringstream

I am creating a simple command parser using c++, and I am trying to use istream >> to check whether I am inputting a number or a character.
input:
a = 10
b = a
parser.cpp:
string inputLine, varLeft, equal, varRight;
double varValue
// please see update below
while(getline(cin, inputLine)){
istringstream stringSplitter(inputLine);
stringSplitter >> varLeft >> equal;
if(!(stringSplitter >> varValue)){
stringSplitter >> varRight;
}
}
The goal is, later in the code, if varRight is empty, I assume that the line of input was a double literal, otherwise, it's a string that denotes a variable. I understand there might be logical errors associated with mixed input starting with digits, but I am assuming that all input is correctly formatted for now. Why is the a in the second line of input discarded? What fix do you propose?
Update
The problem is not with the while loop statement, it is with the if statement at the end of the while code block.
In the actual code, the loop is not actually a while loop; I am using a vector object holding string commands, and iterating through them using a for loop which goes through the vector using iterators. But, in order to please the commenters, I have fixed it above.
If an input function fails, the stream will not allow any more extractions until you clear the failure state. You'll need to do that, after you've checked that the input to varValue failed, with std::basic_ios::clear:
if(!(stringSplitter >> varValue)){
stringSplitter.clear();
stringSplitter >> varRight;
}
I don't know how you're doing /* not end of input */ at the moment (hopefully you're not checking eof()!), but it's recommended that you do:
while (getline(cin, inputLine)) {
// ...
}
This checks that the line input was successful before diving into the loop.