Initializing a Vector of Objects from a .txt file - c++

#include<iostream>
#include<vector>
#include<fstream>
#include "stock.h"
int main(){
double balance =0, tempPrice=0;
string tempStr;
vector < Stock > portfolio;
typedef vector<Stock>::iterator StockIt;
ifstream fileIn( "Results.txt" );
for(StockIt i = portfolio.begin(); i != portfolio.end(); i++)
{
while ( !fileIn.eof( ))
{
getline(fileIn,tempStr);
i->setSymbol(tempStr);
fileIn >> tempPrice;
i->setPrice(tempPrice);
getline(fileIn,tempStr);
i->setDate(tempStr);
}
fileIn.close();
}
for(StockIt i = portfolio.begin(); i != portfolio.end(); i++){
cout<<i->getSymbol() <<endl;
cout<<i->getPrice() <<endl;
cout<<i->getDate() <<endl;
}
return 0;
}
Sample text file, Results.txt:
GOOG 569.964 11/17/2010
MSFT 29.62 11/17/2010
YHOO 15.38 11/17/2010
AAPL 199.92 11/17/2010
Now obviously, I want this program to create a vector of Stock Objects which has the appropriate set/get functionality for object: Stock(string, double, string).
Once that is done, I want to print out each individual member of each Object in the vector.
One thing that boggles my mind about fstream, is how can it decipher spaces and end of lines, and intelligently read strings/ints/doubles and place them into the appropriate data type? Maybe it can't...and I have to add an entirely new functionality?
now it would seem that I'm not actually creating a new object for each iteration of the loop? I think would need to do something along the lines of:
portfolio.push_back(new Stock(string, double, string));? I'm just not entirely sure how to get to that point.
Also, this code should be interchangeable with std::list as well as std::vector. This program compiles without error, however, there is zero output.

First of all, iterating over the vector only makes sense when it isn't empty. So remove the line:
for(StockIt i = portfolio.begin(); i != portfolio.end(); i++)
because otherwise the contents of this loop will never be executed.
Second, you have problems with your input reading: you use getline for the first field, which would read the values of all 3 fields on the line into the tempStr variable.
Third, you shouldn't use while(!fileIn.eof()) - the eof function only returns true after you tried to read past the end of the file. Instead, use:
while (fileIn >> symbol >> price >> date) {
//here you should create a Stock object and call push_back on the vector.
}
This will read the three fields, which are separated by spaces.

Few issues in your code.
the first for loop runs on an empty portfolio vector, as the vector is not initialized (no objects are being pushed to it) the begin() and end() are the same.
you should read line by line from the fstream until EOF, then push objects to the vector.
each line you read, you should split (tokenize) into the 3 parts, and create a new Stock object to be pushed to the vector.
Another side feedback, whenever using an stl iterator, use ++itr on for loops, it will run much more fast

Related

trouble reading file into two objects repeatedly without an infinite loop

c++ beginner and first time poster here.
I'm reading a file consisting of a series of large numbers separated by semicolons.
I am trying to create a loop where the file is read into two objects (first number up to semicolon in first object, second number into second object), then print the two objects containing each number. After printing the first two, it should repeat with the next two numbers until the end of the file is reached.
I have been playing around with it a lot and can't get it to work. Two problems keep occurring:
Infinite loop
failure to read the number into an object
I currently have the operator >> overloaded as follows:
std::istream& operator>>(std::istream& fin, big& obj){
char temp[size];
char stop = ';';
fin.get(temp, size, stop);
//constructor converts temp to big
obj = big(temp);
return in;
}
I have the file opening correctly, and when I just use it on one object( in>>big1) it works properly and reads the first number.
But if I try to read into two objects (in>>big1>>big2) then only the first gets a number and second gets nothing (big1==1234 big2==0).
If I try to do this in a loop which is the end goal, then I just get an infinite loop of zero. This is the basic code I have right now of what I'm trying to do:
//file opens correctly
big big1, big2;
while (!fin.eof()){
//read two numbers from file seperated by semicolon
fin >> big1 >> big2;
std::cout << big1 << "\n";
std::cout << big2 << "\n";
//repeat until end of file reached
}
fin.close();
return 0;
How can I write this so it will work? I've been trying many ways of writing it and nothing works, but it has to be possible.
Please help me if you can!
also note I can't use predefined classes like std::vector or std::string

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! ;)

Reading a CSV to vectors in objects

I'm trying to write code that will, on a line-by-line basis, pass numerical data from a CSV to an object's vector. The object's structure is as follows: the object itself (let's call it CS) is an enclosed space, within which resides a vector of objects (called Points) which each have a vector of objects (Features) with 3 variables. The first two variables in these Features are descriptors of the feature and the third is the actual value taken by a specific Point[i].Feature[j]. Each point has the same set of Features, and aside from third value being different, the descriptors are likewise identical. (edit: Sadly I can't change this structure as it's part of a larger framework which is out of my hands)
Currently, my CSV has one column per feature, the first two rows being the descriptors which apply for all points and the rest of the rows being each individual point's third feature value. It's been a while since my introductory C++ course and I'm finding it hard to think of a fast way to implement this, as my CSVs could become fairly large (my current upper limit is 50000 points having 2000 features, this will probably grow) and I wouldn't want to do something silly like rereading the first two lines every time for each point. I've looked around and most CSV solutions involve string CSVs, which I don't have to deal with, and simpler array objects in which the CSV is stored. The problem for me is simply going up a level each time I reach the end of the line and restarting the procedure for the next point, and I can't think of anything. Any tips?
You could just create a temporary array of Descriptor objects which holds the two descriptors for each column and then read in your first row and create your Point objects from that. Afterwards you can just copy the descriptors from the Point a row above, e.g. Point[i-csvWidth], and deallocate the Descriptor array.
I guess I was nearly there, just used the wrong kind of variable to read in.
fstream myFile;
myFile.open(filePath.c_str());
if(!myFile){
cout << "File \"" << filePath << "\" doesn't exist, exiting program." << endl;
exit(EXIT_FAILURE);
}
string line,line2,line3;
Points.clear();
//gets the range row
getline(myFile,line);
istringstream lineStream(line);
//gets the nomin row
getline(myFile,line2);
istringstream lineStream2(line2);
//gets the first person's traits
getline(myFile,line3);
istringstream lineStream3(line3);
CultVec originalCultVec = CultVec(RNG);
int val,val2,val3,val4;
while (lineStream >> val && lineStream2 >> val2 && lineStream3 >> val3) {
Feature feature;
feature.Range = (char)val;
feature.Nomin = (bool)val2;
feature.Trait = (char)val3;
originalCultVec.addFeature(feature);
} // while
Points.push_back(originalCultVec);
while (getline(myFile,line)) {
int i = 0;
CultVec newVec = CultVec(RNG);
istringstream lineStream4(line);
while ( lineStream4 >> val4 ) {
Feature newFeat = originalCultVec.getFeature(i);
newFeat.Trait = (char)val4;
newVec.addFeature(newFeat);
i++;
}
Points.push_back(newVec);
}

Need to write specific lines of a text into a new text

I have numerical text data lines ranging between 1mb - 150 mb in size, i need to write lines of numbers related to heights, for example: heights=4 , new text must include lines: 1,5,9,13,17,21.... consequentially.
i have been trying to find a way to do this for a while now, tried using a list instead of vector which ended up with compilation errors.
I have cleaned up the code as advised. It now writes all lines sample2 text, all done here. Thank you all
I am open to method change as long as it delivers what i need, Thank you for you time and help.
following is what i have so far:
#include <iostream>
#include <fstream>
#include <string>
#include <list>
#include <vector>
using namespace std;
int h,n,m;
int c=1;
int main () {
cout<< "Enter Number Of Heights: ";
cin>>h;
ifstream myfile_in ("C:\\sample.txt");
ofstream myfile_out ("C:\\sample2.txt");
string line;
std::string str;
vector <string> v;
if (myfile_in.is_open()) {
myfile_in >> noskipws;
int i=0;
int j=0;
while (std::getline(myfile_in, line)) {
v.push_back( line );
++n;
if (n-1==i) {
myfile_out<<v[i]<<endl;
i=i+h;
++j;
}
}
cout<<"Number of lines in text file: "<<n<<endl;
}
else cout << "Unable to open file(s) ";
cout<< "Reaching here, Writing one line"<<endl;
system("PAUSE");
return 0;
}
You need to use seekg to set the position at the beginning of the file, once you have read it (you have read it once, to count the lines (which I don't think you actually need, as this size is never used, at least in this piece of code)
And what is the point if the inner while? On each loop, you have
int i=1;
myfile_out<<v[i]; //Not writing to text
i=i+h;
So on each loop, i gets 1, so you output the element with index 1 all the time. Which is not the first element, as indices start from 0. So, once you put seekg or remove the first while, your program will start to crash.
So, make i start from 0. And get it out of the two while loops, right at the beginning of the if-statement.
Ah, the second while is also unnecessary. Leave just the first one.
EDIT:
Add
myfile_in.clear();
before seekg to clear the flags.
Also, your algorithm is wrong. You'll get seg fault, if h > 1, because you'll get out of range (of the vector). I'd advise to do it like this: read the file in the while, that counts the lines. And store each line in the vector. This way you'll be able to remove the second reading, seekg, clear, etc. Also, as you already store the content of the file into a vector, you'll NOT lose anything. Then just use for loop with step h.
Again edit, regarding your edit: no, it has nothing to do with any flags. The if, where you compare i==j is outside the while. Add it inside. Also, increment j outside the if. Or just remove j and use n-1 instead. Like
if ( n-1 == i )
Several things.
First you read the file completely, just to count the number of lines,
then you read it a second time to process it, building up an in memory
image in v. Why not just read it in the first time, and do everything
else on the in memory image? (v.size() will then give you the number
of lines, so you don't have to count them.)
And you never actually use the count anyway.
Second, once you've reached the end of file the first time, the
failbit is set; all further operations are no-ops, until it is reset.
If you have to read the file twice (say because you do away with v
completely), then you have to do myfile_in.clear() after the first
loop, but before seeking to the beginning.
You only test for is_open after having read the file once. This test
should be immediately after the open.
You also set noskipws, although you don't do any formatted input
which would be affected by it.
The final while is highly suspect. Because you haven't done the
clear, you probably never enter the loop, but if you did, you'd very
quickly start accessing out of bounds: after reading n lines, the size
of v will be n, but you read it with index i, which will be n * h.
Finally, you should explicitly close the output file and check for
errors after the close, just in case.
It's not clear to me what you're trying to do. If all you want to do is
insert h empty lines between each existing line, something like:
std::string separ( h + 1, '\n' );
std::string line;
while ( std::getline( myfile_in, line ) ) {
myfile_out << line << separ;
}
should do the trick. No need to store the complete input in memory.
(For that matter, you don't even have to write a program for this.
Something as simple a sed 's:$:\n\n\n\n:' < infile > outfile would do
the trick.)
EDIT:
Reading other responses, I gather that I may have misunderstood the
problem, and that he only wants to output every h-th line. If this is
the case:
std::string line;
while ( std::getline( myfile_in, line ) ) {
myfile_out << line << '\n';
for ( int count = h - 1; h > 0; -- h ) {
std::getline( myfile_in, line );
// or myfile_in.ignore( INT_MAX, '\n' );
}
}
But again, other tools seem more appropriate. (I'd follow thiton's
suggestion and use AWK.) Why write a program in a language you don't
know well when tools are already available to do the job.
If there is no absolutely compelling reason to do this in C++, you are using the wrong programming language for this. In awk, your whole program is:
{ if ( FNR % 4 == 1 ) print; }
Or, giving the whole command line e.g. in sh to filter lines 1,5,9,13,...:
awk '{ if ( FNR % 4 == 1 ) print; }' a.txt > b.txt

Initializing array of objects with data from text file

I’m getting system error when I try to compile the code below on Visual C++ 2008 Express. What I’m trying to do is to initialize array of objects with data read from file. I think there is something wrong inside the while loop, because when I initialize these objects manually without the while loop it seems to work. Here is the code and text file:
#include <iostream>
#include <string>
#include "Book.h"
using namespace std;
int main()
{
const int arraySize = 3;
int indexOfArray = 0;
Book bookList[arraySize];
double tempPrice;//temporary stores price
string tempStr;//temporary stores author, title
fstream fileIn( "books.txt" );
while ( !fileIn.eof( ))
{
getline(fileIn,tempStr);
bookList[indexOfArray].setAuthor(tempStr);
getline(fileIn,tempStr);
bookList[indexOfArray].setTitle(tempStr);
fileIn >> tempPrice;
bookList[indexOfArray].setPrice(tempPrice);
if ( indexOfArray < arraySize ) //shifting array index while not exceeding array size
indexOfArray++;
}
fileIn.close();
return 0;
}
and the text file:
Author1
Book1
23.99
Author2
Book2
10.99
Autho3
Book3
14.56
It looks like you are trying to write to bookList[3] in the loop. You will loop through three times filling your array incrementing indexOfArray each time. This will leave indexOfArray at 3 -- your condition as it is written will allow indexOfAray to be incremented to 3. Then if you have a newline after the "14.56" in your data file you will loop one more time and attempt to pass an empty string to bookList[indexOfArray].setAuthor() leading to a segfault since indexOfArray is past the end of the array.
I would suggest ditching the hard-coded array and using a std::vector instead. At the start of each loop just use push_back() to add a new book to the end of the vector and then use back() to access the new element in the array.
There's another run-time error in your code: You don't read an entire line with the call to fileIn >> tempPrice;. The next call to getline() will read to the end of the line, so you'll get an empty string when you're expecting an author.
You're then off by one line in your text file, and you try to convert a title into a double. That make the fstream signal an error, and after that, you're in trouble.
Brett's right, a vector with push_back is a better solution here.
Brett also correctly pointed out that you could run into errors if your file has extra lines. You can fix that by checking if you successfully read from the file:
if(fileIn >> tempPrice)
{
bookList[indexOfArray].setPrice(tempPrice);
}
else
{
break;
}
if(!getline(fileIn,tempStr))
{
break;
}
The key must be in the contents of
#include "Book.h"
I copy-pasted your code, and replaced the #include with my assumption of what class Book might look like:
class Book
{
std::string auth;
std::string title;
double price;
public:
void setAuthor(std::string& str)
{
auth = str;
}
void setTitle(std::string& t)
{
title = t;
}
void setPrice(double d)
{
d = price;
}
};
and it compiled. Perhaps you could share your Book.h, or look there for any problems? Start with some simple definition from Book (like above) and begin readding code until you've found the lines that cause the problem. Its a crude method of figuring out the issue, but sometimes its the most direct way.