How to read a separate line from textfile for different objects? - c++

I have two rectangles objects which I'm trying to read different lines in a textfile with readcood() however when I run this I get the same line from the text file.
#include<iostream>
#include<fstream>
using namespace std;
class rect{
int xcood[3],ycood[3],x1,x2,x3,x4,y1,y2,y3,y4;
public:
void readcood()
{
char dummy;
ifstream myfile;
myfile.open("coordinates.txt");
for(int i=0;i<4;i++)
{
myfile>>xcood[i]>>dummy>>ycood[i];
}
}
void printcood()
{
for(int i=0;i<4;i++)
{
cout<<xcood[i]<<"\t"<<ycood[i]<<endl;
}
}
};
int main()
{
char dummy;
string coordinates;
rect rectangle1,rectangle2;
rectangle1.readcood();
rectangle2.readcood();
rectangle1.printcood();
rectangle2.printcood();
}

It's because you open the file in the function. When the function returns the file-stream object will be destructed and all you've done will be lost.
You could open it in the main function and pass it (by reference) to the read function.
Another solution is to add an open function to your rect class, that opens the file, and have the file stream being a member of the class.
However the solution I would recommend is to make your own overloaded input function, that takes a rect object and reads into that. Something like
class rect
{
public:
...
friend std::istream& operator>>(std::istream& is, rect& r)
{
char dummy;
for (int i = 0; i < 3; ++i)
is >> r.xcood[i] >> dummy >> r.ycood[i];
return is;
}
...
};
Then you can simply do e.g.
std::ifstream file("some/path/file");
rect r1, r2;
file >> r1 >> r2;

Related

Passing an Array built off of a Struct to a Function that Stores Data into Array from a File

In a structs lab assignment that I am doing, the question asks to read statistics about 10 different dinosaurs from a text file and store that information into a struct. I get no errors in the code, however the console is just totally blank.I think i am definitely referencing the array wrong and I have no idea how to fix this.
My code is as follows:
using namespace std;
const int LIST_SIZE = 10;
struct dinosaurInfo {
string dinosaurName;
string dinosaurClass;
string dinosaurHabitat;
double dinosaurSize;
double dinosaurWeight;
int battleRating;
};
void loadData(ifstream& getData, dinosaurInfo *data);
int main()
{
dinosaurInfo data[LIST_SIZE];
ifstream getData;
ofstream giveData;
getData.open("dinosaurRecords.txt");
if (!getData)
{
cout << "Error loading in data." << endl;
}
loadData(getData, data);
getData.close();
system("pause");
return 0;
}
void loadData(ifstream& getData, dinosaurInfo *data)
{
while (!getData.eof())
{
for (int i = 0; i < 10; i++)
{
getline(getData, data[i].dinosaurName);
getline(getData, data[i].dinosaurClass);
getline(getData, data[i].dinosaurHabitat);
cin.ignore();
getData >> data[i].dinosaurSize;
getData >> data[i].dinosaurWeight;
getData >> data[i].battleRating;
}
}
The text file is formatted as follows: (dinosaurname class habitat height weight battle rating).
screenshot of the file below
May someone please help me fix this?
The root problem is use of getline to read the string elements.
getline(getData, data[i].dinosaurName);
getline(getData, data[i].dinosaurClass);
getline(getData, data[i].dinosaurHabitat);
Since these are tokens separated by space, use the >> operator instead to read them.
getData >> data[i].dinosaurName;
getData >> data[i].dinosaurClass;
getData >> data[i].dinosaurHabitat;
Also, avoid use of
while (!getData.eof()) { ... }
See Why is iostream::eof inside a loop condition considered wrong? for details.
A cleaned up version of you function would be:
void loadData(ifstream& getData, dinosaurInfo *data)
{
// Stop when 10 records are read.
// Also, stop reading when getData is an error state.
for (int i = 0; i < 10 && getData; i++)
{
getData >> data[i].dinosaurName;
getData >> data[i].dinosaurClass;
getData >> data[i].dinosaurHabitat;
getData >> data[i].dinosaurSize;
getData >> data[i].dinosaurWeight;
getData >> data[i].battleRating;
}
}

How to read pieces of string into a class array C++

I have an array of dvd from a Video class I created
Video dvd[10];
each video has the property,
class Video {
string _title;
string _genre;
int _available;
int _holds;
public:
Video(string title, string genre, int available, int holds);
Video();
void print();
void read(istream & is, Video dvd);
int holds();
void restock(int num);
string getTitle();
~Video();
};
I'm trying to fill up this array with data from my text file where each info such as the title and genre is separated by a comma
Legend of the seeker, Fantasy/Adventure, 3, 2
Mindy Project, Comedy, 10, 3
Orange is the new black, Drama/Comedy, 10, 9
I've tried using getline(in, line, ',') but my brain halts when its time to insert each line into the dvd array.
I also created a read method to read each word separated by a whitespace but I figured thats not what I really want.
I also tried to read a line with getline, store the line in a string and split it from there but I get confused along the line.
**I can get the strings I need from each line, my confusion is in how to insert it into my class array in the while loop especially when I can only read one word at a time.
I need help on what approach I should follow to tackle this problem.
**My code
#include <iostream>
#include <fstream>
#include <cassert>
#include <vector>
#define MAX 10
using namespace std;
class Video {
string _title;
string _genre;
int _available;
int _holds;
public:
Video(string title, string genre, int available, int holds);
Video();
void print();
void read(istream & is, Video dvd);
int holds();
void restock(int num);
string getTitle();
~Video();
};
Video::Video(string title, string genre, int available, int holds){
_title = title;
_genre = genre;
_available = available;
_holds = holds;
}
void Video::read (istream & is, Video dvd)
{
is >> _title >> _genre >> _available>>_holds;
dvd = Video(_title,_genre,_available,_holds);
}
int Video::holds(){
return _holds;
}
void Video::restock(int num){
_available += 5;
}
string Video::getTitle(){
return _title;
}
Video::Video(){
}
void Video::print(){
cout<<"Video title: " <<_title<<"\n"<<
"Genre: "<<_genre<<"\n"<<
"Available: " <<_available<<"\n"<<
"Holds: " <<_holds<<endl;
}
Video::~Video(){
cout<<"DESTRUCTOR ACTIVATED"<<endl;
}
int main(int params, char **argv){
string line;
int index = 0;
vector<string> tokens;
//Video dvd = Video("23 Jump Street", "comedy", 10, 3);
//dvd.print();
Video dvd[MAX];
dvd[0].holds();
ifstream in("input.txt");
/*while (getline(in, line, ',')) {
tokens.push_back(line);
}
for (int i = 0; i < 40; ++i)
{
cout<<tokens[i]<<endl;
}*/
if(!in.fail()){
while (getline(in, line)) {
dvd[index].read(in, dvd[index]);
/*cout<<line<<endl;
token = line;
while (getline(line, token, ',')){
}
cout<<"LINE CUT#####"<<endl;
cout<<line<<endl;
cout<<"TOKEN CUT#####"<<endl;*/
//dvd[index] =
index++;
}
}else{
cout<<"Invalid file"<<endl;
}
for (int i = 0; i < MAX; ++i)
{
dvd[i].print();
}
}
First, I would change the Video::read function into an overload of operator >>. This will allow the Video class to be used as simply as any other type when an input stream is being used.
Also, the way you implemented read as a non-static member function returning a void is not intuitive and very clunky to use. How would you write the loop, and at the same time detect that you've reached the end of file (imagine if there are only 3 items to read -- how would you know to not try to read a fourth item)? The better, intuitive, and frankly, de-facto way to do this in C++ is to overload the >> operator.
(At the end, I show how to write a read function that uses the overloaded >>)
class Video
{
//...
public:
friend std::istream& operator >> (std::istream& is, Video& vid);
//..
};
I won't go over why this should be a friend function, as that can be easily researched here on how to overload >>.
So we need to implement this function. Here is an implementation that reads in a single line, and copies the information to the passed-in vid:
std::istream& operator >> (std::istream& is, Video& vid)
{
std::string line;
std::string theTitle, theGenre, theAvail, theHolds;
// First, we read the entire line
if (std::getline(is, line))
{
// Now we copy the line into a string stream and break
// down the individual items
std::istringstream iss(line);
// first item is the title, genre, available, and holds
std::getline(iss, theTitle, ',');
std::getline(iss, theGenre, ',');
std::getline(iss, theAvail, ',');
std::getline(iss, theHolds, ',');
// now we can create a Video and copy it to vid
vid = Video(theTitle, theGenre,
std::stoi(theAvail), // need to change to integer
std::stoi(theHolds)); // same here
}
return is; // return the input stream
}
Note how vid is a reference parameter, not passed by value. Your read function, if you were to keep it, would need to make the same change.
What we did above is that we read the entire line in first using the "outer" call to std::getline. Once we have the line as a string, we break down that string by using an std::istringstream and delimiting each item on the comma using an "inner" set of getline calls that works on the istringstream. Then we simply create a temporary Video from the information we retrieved from the istringstream and copy it to vid.
Here is a main function that now reads into a maximum of 10 items:
int main()
{
Video dvd[10];
int i = 0;
while (i < 10 && std::cin >> dvd[i])
{
dvd[i].print();
++i;
}
}
So if you look at the loop, all we did is 1) make sure we don't go over 10 items, and 2) just use cin >> dvd[i], which looks just like your everyday usage of >> when inputting an item. This is the magic of the overloaded >> for Video.
Here is a live example, using your data.
If you plan to keep the read function, then it would be easier if you changed the return type to bool that returns true if the item was read or false otherwise, and just calls the operator >>.
Here is an example:
bool Video::read(std::istream & is, Video& dvd)
{
if (is.good())
{
is >> dvd;
return true;
}
return false;
}
And here is the main function:
int main()
{
Video dvd[10];
int i = 0;
while (i < 10 && dvd[i].read(std::cin, dvd[i]))
{
dvd[i].print();
++i;
}
}
Live Example #2
However, I still say that the making of Video::read a non-static member makes the code in main clunky.

Reading file into object array C++

I have to read from a data file which is formatted like this
abcd(string) 1(int) 2(int) 3(int)
abcde(string) 4(int) 3(int) 2(int)
.
.
.
I want to perform some functions which use the variables in the same line only. But here's my code. I'm a beginner so please correct me thank you.
in the .h file
#include <string>
using namespace std;
#ifndef CALC_H
#define CALC_H
class Calc
{
public:
void readFile(string file);
private:
string name;
int a;
int b;
int c;
};
#endif
in the implementation file
#include "Vehicle.h"
#include iostream>
#include fstream>
#include string>
#include cstdlib>
#include cmath>
using namespace std;
void Vehicle::readFile(string filename)
{
ifstream myIn;
int totalNum=0;
myIn.open(filename.c_str());
if (!myIn)
{
cerr<<"Data file failed to open!\n";
exit (0);
}
for (int i=0; i<MAX; i++)
{
while (myIn.peek() != EOF)
{
myIn>>calc[i].name;
myIn>>calc[i].a;
myIn>>calc[i].b;
myIn>>calc[i].c;
totalNum++;
}
}
myIN.close();
and then I want to display what i just read from the file
for (int i = 0; i < MAX; i++)
cout << calc[i].name << calc[i].a << calc[i].b << calc[i].c << endl;
sorry I left out alot of stuff I just want to know if I on the right path. Thanks
The proper way to do this is to overload the >> operator for your class Calc.
class Calc {
public:
friend istream& operator >>(istream& myIn, Calc& calc);
};
istream& operator >>(istream& myIn, Calc& calc) {
myIn >> calc.name;
myIn >> calc.a;
myIn >> calc.b;
myIn >> calc.c;
return myIn;
}
Now you can just do:
while (myIn >> calc[i]) {
++totalNum;
}
You should consider designing it a bit differently.
create a class that holds one line i.e. string int int int - like you have it in "Calc" but without making it dependent on how you create a line (readfile). Lets call it "Line"
class Line
{
public:
std::string name;
int a;
int b;
int c;
};
Now since you need to read several lines you will need some kind of container to hold them, create a vector of Line (or some other container)
std::vector<Line> contents;
then override the stream operator as Tushar suggested so when you read from a file (or from e.g. stdin) you can create instances of Line for each line you read, these instances you use to fill the 'contents' array
now you can start doing whatever it is you want to do with the lines i.e. the actual operation calc

Reading from file into a vector

I'm using C++ and I'm reading from a file lines like this:
D x1 x2 x3 y1
My code has:
struct gate {
char name;
vector <string> inputs;
string output;
};
In the main function:
vector <gate> eco;
int c=0;
int n=0;
int x = line.length();
while(netlist[c][0])
{
eco.push_back(gate());
eco[n].name = netlist[c][0];
eco[n].output[0] = netlist[c][x-2];
eco[n].output[1] = netlist[c][x-1];
}
where netlist is a 2D array I have copied the file into.
I need help to loop over the inputs and save them in the vector eco.
I don’t fully understand the sense of the 2D array but I suspect it’s redundant. You should use this code:
ifstream somefile(path);
vector<gate> eco;
gate g;
while (somefile >> g)
eco.push_back(g);
// or, simpler, requiring #include <iterator>
vector<gate> eco(std::istream_iterator<gate>(somefile),
std::istream_iterator<gate>());
And overload operator >> appropriately for your type gate:
std::istream& operator >>(std::istream& in, gate& value) {
// Error checking … return as soon as a failure is encountered.
if (not (in >> gate.name))
return in;
gate.inputs.resize(3);
return in >> gate.inputs[0] >>
gate.inputs[1] >>
gate.inputs[2] >>
gate.output;
}

Inputting a file into a structure

I am trying to read the lines from a file called 'weapon.txt' and input them into a structure something a long the lines of this
struct weapon
{
char name[20]; //Edited
int strength;
}
The file to be read looks like this:
Excalibur
150
Throwing Stars
15
Rapier
200
Bow and Arrow
100
Axe
200
Crossbow
100
Scimitar
250
Rusted Sword
10
Soul Slayer
500
The code I have right now is
#include<fstream>
#include<iostream>
#include<cstring>
using namespace std;
struct WeaponInfo
{
char name[16];
int strength;
};
const int MaxWeap = 10;
void openfile(ifstream&); //Opening the file
void displayfile(ifstream&, WeaponInfo&);//Display file
int main ()
{
WeaponInfo weapon[MaxWeap];
ifstream fin;
openfile(fin);
displayfile(fin, weapon[MaxWeap]);
}
void openfile(ifstream& fin)
{
fin.open("weapon.txt");
}
void displayfile(ifstream& fin, WeaponInfo& weapon[MaxWeap])
{
char nm;
int str;
while (fin.eof() == 0)
{
for(int i = 0; i <= MaxWeap; i++);
{
fin.getline(nm);
fin.getline(str);
strcpy(weapon[i].name, nm);
strcpy(weapon[i].strength, str);
i++;
cout << weapon[i].name << "\n" << weapon[i].strength << endl;
}
}
fin.close();
}
EDIT: This is what I have right now after re-doing it, I am getting compile errors of : declaration of 'weapon' as array of references; In function 'void displayfile(...) 'fin' was not declared in this scope; 'weapon' is not declared in this scope; ma,e lookup of 'i' changed for ISO 'for' scoping [-fpermissive].
I'd firstly tend to use std::string rather than char arrays - they're just easier to work with. So the structure noww looks like this:
struct weapon
{
string name;
int strength;
};
Next you need something that will read the structure from an input stream:
bool getWeapon( ifstream& is, weapon& w )
{
getline(is, w.name) ;
string strengthStr;
getline(is, strengthStr) ;
w.strength = strtol( strengthStr.c_str(), NULL, 0 );
return !is.eof();
}
Two things here, I've used strtol as a conversion function from string to int. atoi is used but strtol gives you slightly more flexibility and crucially, better error cchecking, alkthough I've not bothered to implement it here. A stringstream might have been another alternative here.
Secondly, I return a boolean indicating whether the name was empty. The reason for this is that when, later in the code, I check for eof() on the ifstream, it isn't actually set until you read past the end of the file. So the last good read will not set it but the first attempt to reead past it will. Returning false here then will indicate to the caller that the 'get' failed due to the ifstream being at end of file.
Lastly, we need something to read all of the weappons in:
ifstream input;
input.open("weapons.txt");
vector<weapon> ws;
if ( input )
{
while (! (input.eof()))
{
weapon w;
if ( ! getWeapon( input, w ) )
break;
ws.push_back( w );
}
}
input.close();
This wwill place all the weapons into a vector. Note the call to getWeapon breaks if it failed to prrevent adding on an 'empty' weapon. Not the most glamorous solution but it should work.
Pseudo-code goes something like this, (and like Martol1ni has coded for you):
open the file
while (!end-of file)
{
create instance of struct weapon
read a line and strcpy into weapon.name
read a line and set weapon.strength = atoi(line)
do something with the instance, eg. add to list, call a member function, etc.
}
loop
close file.
Assuming you control the weapons.txt, don't bother checking for errors in the file, you can do this. Next time, do a little research... :)
#include <fstream>
#include <vector>
#include <string>
#include <iostream>
#include <cstdlib>
using namespace std;
struct weapon
{
string name;
int strength;
weapon(string n, int s) : name(n), strength(s) {}
};
void readFileToVec(vector<weapon> &myVec) {
ifstream in("weapon.txt");
while (!in.eof()) {
string name;
getline(in,name);
string strength;
getline(in,strength);
weapon myWep(name,atoi(strength.c_str()));
myVec.push_back(myWep);
}
in.close();
}