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

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.

Related

How does one correctly store data into an array struct with stringstream? [duplicate]

This question already has answers here:
Why does reading a record struct fields from std::istream fail, and how can I fix it?
(9 answers)
Closed 6 years ago.
I was wondering how to store data from a CSV file into a structured array. I realize I need to use getline and such and so far I have come up with this code:
This is my struct:
struct csvData //creating a structure
{
string username; //creating a vector of strings called username
float gpa; //creating a vector of floats called gpa
int age; //creating a vector of ints called age
};
This is my data reader and the part that stores the data:
csvData arrayData[10];
string data;
ifstream infile; //creating object with ifstream
infile.open("datafile.csv"); //opening file
if (infile.is_open()) //error check
int i=0;
while(getline(infile, data));
{
stringstream ss(data);
ss >> arrayData[i].username;
ss >> arrayData[i].gpa;
ss >> arrayData[i].age;
i++;
}
Further, this is how I was attempting to print out the information:
for (int z = 0; z<10; z++)
{
cout<<arrayData[z].username<<arrayData[z].gpa<<arrayData[z].age<<endl;
}
However, when running this command, I get a cout of what seem to be random numbers:
1.83751e-0383 03 4.2039e-0453 1.8368e-0383 07011688
I assume this has to be the array running not storing the variables correctly and thus I am reading out random memory slots, however, I am unsure.
Lastly, here is the CSV file I am attempting to read.
username,gpa,age
Steven,3.2,20
Will,3.4,19
Ryan,3.6,19
Tom,3,19
There's nothing in your parsing code that actually attempts to parse the single line into the individual fields:
while(getline(infile, data));
{
This correctly reads a single line from the input file into the data string.
stringstream ss(data);
ss >> arrayData[i].username;
ss >> arrayData[i].gpa;
ss >> arrayData[i].age;
You need to try to explain to your rubber duck how this is supposed to take a single line of comma-separated values, like the one you showed in your question:
Steven,3.2,20
and separate that string into the individual values, by commas. There's nothing about the >> operator that will do this. operator>> separates input using whitespaces, not commas. Your suspicions were correct, you were not parsing the input correctly.
This is a task that you have to do yourself. I am presuming that you would like, as a learning experience, or as a homework assignment, to do this yourself, manually. Well, then, do it yourself. You have the a single line in data. Use any number of tools that C++ gives you: the std::string's find() method, or std::find() from <algorithm>, to find each comma in the data string, then extract each individual portion of the string that's between each comma. Then, you still need to convert the two numeric fields into the appropriate datatypes. And that's when you put each one of them into a std::istringstream, and use operator>> to convert them to numeric types.
But, having said all that, there's an alternative dirty trick, to solve this problem quickly. Recall that the original line in data contains
Steven,3.2,20
All you have to do is replace the commas with spaces, turning it into:
Steven 3.2 20
Replacing commas with spaces is trivial with std::replace(), or with a small loop. Then, you can stuff the result into a std::istringstream, and use operator>> to extract the individual whitespace-delimited values into the discrete variables, using the code that you've already written.
Just a small word of warning: if this was indeed your homework assignment, to write code to manually parse and extract comma-delimited values, it's not guaranteed that your instructor will give you the full grade for taking the dirty-trick approach...
UNDER CONSTRUCTION
Ton, nice try and nice complete question. Here is the answer:
1) You have a semicolon after the loop:
while(getline(infile, data));
delete it.
How did I figure that out easily? I compiled with all the warnings enabled, like this:
C02QT2UBFVH6-lm:~ gsamaras$ g++ -Wall main.cpp
main.cpp:24:33: warning: while loop has empty body [-Wempty-body]
while(getline(infile, data));
^
main.cpp:24:33: note: put the semicolon on a separate line to silence this warning
1 warning generated.
In fact, you should get that warning without -Wall as well, but get into using it, it will also make good to you! :)
2) Then, you read some elements, but not 10, so why do you print 10? Print as many as the ones you actually read, i.e. i.
When you try to print all 10 elements of your array, you print elements that are not initialized, since you didn't initialize your array of structs.
Moreover, the number of lines in datafile.csv was less than 10. So you started populating your array, but you stopped, when the file didn't have more lines. As a result, some of the elements of your array (the last 6 elements) remained uninitialized.
Printing uninitialized data, causes Undefined Behavior, that's why you see garbage values.
3) Also this:
if (infile.is_open()) //error check
could be written like this:
if (!infile.is_open())
cerr << "Error Message by Mr. Tom\n";
Putting them all together:
WILL STILL NOT WORK, BECAUSE ss >> arrayData[i].username; eats the entire input line and the next two extractions fail, as Pete Becker said, but I leave it here, so that others won't make the same attempt!!!!!!!
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
using namespace std;
struct csvData //creating a structure
{
string username; //creating a vector of strings called username
float gpa; //creating a vector of floats called gpa
int age; //creating a vector of ints called age
};
int main()
{
csvData arrayData[10];
string data;
ifstream infile; //creating object with ifstream
infile.open("datafile.csv"); //opening file
if (!infile.is_open()) { cerr << "File is not opened..\n"; }
int i=0;
while(getline(infile, data))
{
stringstream ss(data);
ss >> arrayData[i].username;
ss >> arrayData[i].gpa;
ss >> arrayData[i].age;
i++;
}
for (int z = 0; z< i; z++)
{
cout<<arrayData[z].username<<arrayData[z].gpa<<arrayData[z].age<<endl;
}
return 0;
}
Output:
C02QT2UBFVH6-lm:~ gsamaras$ g++ -Wall main.cpp
C02QT2UBFVH6-lm:~ gsamaras$ ./a.out
username,gpa,age00
Steven,3.2,2000
Will,3.4,1900
Ryan,3.6,1900
Tom,3,1900
But wait a minute, so now it works, but why this:
while(getline(infile, data));
{
...
}
didn't?
Because, putting a semicolon after a loop is equivalent to this:
while()
{
;
}
because as you probably already know loops with only one line as a body do not require curly brackets.
And what happened to what I thought it was the body of the loop (i.e. the part were you use std::stringstream)?
It got executed! But only once!.
You see, a pair of curly brackets alone means something, it's an anonymous scope/block.
So this:
{
stringstream ss(data);
ss >> arrayData[i].username;
ss >> arrayData[i].gpa;
ss >> arrayData[i].age;
i++;
}
functioned on its one, without being part of the while loop, as you intended too!
Any why did it work?! Because you had declared i before the loop! ;)

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

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.

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
}

ifstream get line change output from char to string

C++ ifstream get line change getline output from char to string
I got a text file.. so i read it and i do something like
char data[50];
readFile.open(filename.c_str());
while(readFile.good())
{
readFile.getline(data,50,',');
cout << data << endl;
}
My question is instead of creating a char with size 50 by the variable name data, can i get the getline to a string instead something like
string myData;
readFile.getline(myData,',');
My text file is something like this
Line2D, [3,2]
Line3D, [7,2,3]
I tried and the compiler say..
no matching function for getline(std::string&,char)
so is it possible to still break by delimiter, assign value to a string instead of a char.
Updates:
Using
while (std::getline(readFile, line))
{
std::cout << line << std::endl;
}
IT read line by line, but i wanna break the string into several delimiter, originally if using char i will specify the delimiter as the 3rd element which is
readFile.getline(data,50,',');
how do i do with string if i break /explode with delimiter comma , the one above. in line by line
Use std::getline():
std::string line;
while (std::getline(readFile, line, ','))
{
std::cout << line << std::endl;
}
Always check the result of read operations immediately otherwise the code will attempt to process the result of a failed read, as is the case with the posted code.
Though it is possible to specify a different delimiter in getline() it could mistakenly process two invalid lines as a single valid line. Recommend retrieving each line in full and then split the line. A useful utility for splitting lines is boost::split().

Very specific parsing in C++

Basically, I'm trying to read in the words from a file and, without punctuation, read each word into a multimap which is then inserted into a vector with each pair being a word and the line of the file that word is found. I've got the function to remove punctuation working perfectly and I'm fairly certain my insert code works properly, but I can't seem to get around the line number part. I've included this section of my code as follows:
ifstream in("textfile.txt");
string line;
string keys;
stringstream keystream;
int line_number = 1;
while (getline(in, line, '\n')) {
alphanum(line);
keystream << line;
while(getline(keystream, keys, ' '))
table.insert(keys, line_number); //this just inserts the pair into my vector (table is an instance of a class I created)
keystream.str("");
line_number++;
}
The problem seems to be related to the stringstream. It doesn't seem to clear when I use keystream.str(""). This particular method only seems to read line 1 in and then exits the loop, whereas some other variations I've tried (I can't remember exactly what I did) read the entire file but don't flush the stringstream so it reads like word 1, word 1, word 2, word 1, word 2, word 3, etc.. Anyway, if anyone could point me in the right direction or perhaps link to a guide specific to parsing input in c++ that would be greatly appreciated! Thanks!
Don't keep the string stream object; just make a new one in each round:
string line;
while (getline(in, line, '\n'))
{
alphanum(line);
istringstream keystream(line);
string keys;
while (getline(keystream, keys, ' ')) // or even "while (keystream >> keys)"
{
}
}
I think the problem is that the second getline() loop sets the EOF flag on the stringstream, and this is not cleared when you call str(). You need to call .clear() also on 'keystream'.