How to istream struct that contains vector of integer as member, I tried the following code but it failed to read the file
struct Ss{
std::vector<int> a;
double b;
double c; };
std::istream& operator>>(std::istream &is, Ss &d)
{
int x;
while (is >> x)
d.a.push_back(x);
is >> d.b;
is >> d.c;
return is;
}
std::vector <std::vector<Ss >> Aarr;
void scanData()
{
ifstream in;
in.open(FileInp);
std::string line;
while (std::getline(in, line))
{
std::stringstream V(line);
Ss S1;
std::vector<Ss > inner;
while (V >> S1)
inner.push_back(std::move(S1));
Aarr.push_back(std::move(inner));
}
}
I did search for similar problems but i could not find one.
The immediate problem here is that the same condition that terminates your while loop prevents the successive reads from working. The stream is in an error state. So your double values are never read.
Actually, the integer part of the first double is read as an integer, leaving the fractional part in the stream. You can't recover from this.
One way to solve this might be to read in your values as strings, converting them to integers using stoi and putting them in the vector. But before you do the integer conversion, check for a decimal point in the string. If it contains one, break out of the loop. Convert the double values using stod.
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Im trying to read from a file called stock.txt, which contains the following values:
ID, Item, Colour, Size, Quantity, Price
11,T-shirt,blue,XL,2,10.500000
12,Supreme,red,M,10,20.500000
13,BANG,red,M,10,20.500000
I wanted to store each item in a list, how can I do that?
int main() {
ifstream infile;
infile.open("Stock.txt");
string id; string title; string colour; string size; string quantity; string cost;
//If file open is successful
while(infile.good()){
getline(infile,id,',');
getline(infile,title,',');
getline(infile,colour,',');
getline(infile,size,',');
getline(infile,quantity,',');
getline(infile,cost,'\n');
}
infile.close();
}
You should use a more moden C++ approach.
I would be happy, if you could study this solution and try to use some features in the future.
In the object orient world, we use classes (or structs) and put data and functions, operating on this data, in one (encapsulated) object.
Only the class should know, how to read and write its data. Not some outside global functions. Therefor I added 2 member functions to your struct. I have overwritten the inserter and the extractor operator.
And in the extractor, we will use modern C++ algorithms, to split a string into tokens. For this purpose, we have the std::sregex_token_iterator. And because there is a specialized function for this purpose, we should use it. And besides, it is ultra simple.
With the below one-liner, we split the complete string into tokens and put the resulting tokens in a std::vector
std::vector token(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {});
Then we copy the resulting data in our member variables.
For demo output I have also overwritten the inserter operator. Now you can use the exteractor and inserter operators (">>" and "<<") for variables of type Stock, as for any other C++ integral variable.
In main, we use also an ultrasimple approach. First, we open the file and check, if this was OK.
Then we define a variable "stocks" (A std::vector of Stock) and use its range constructor and the std::istream_operator to read the complete file. And, since the App has an overwritten extractor operator, it knows, how to read and will parse the complete CSV file for us.
Again, the very simple and short one-liner
std::vector stocks(std::istream_iterator<Stock>(inFile), {});
will read the complete source file, all lines, parse the lines and store the member variables in the single stock elements of the resulting std::vector.
#include <string>
#include <iostream>
#include <vector>
#include <fstream>
#include <regex>
#include <iterator>
#include <algorithm>
std::regex delimiter{ "," };
struct Stock {
// The data. Member variables
std::string id{};
std::string title{};
std::string colour{};
std::string size{};
std::string quantity{};
std::string cost{};
// Overwrite extractor operator
friend std::istream& operator >> (std::istream& is, Stock& s) {
// Read a complete line
if (std::string line{}; std::getline(is, line)) {
// Tokenize it
std::vector token(std::sregex_token_iterator(line.begin(), line.end(), delimiter, -1), {});
// If we read at least 6 tokens then assign the values to our struct
if (6U <= token.size()) {
// Now copy the data from the vector to our members
s.id = token[0];
s.title = token[1];
s.colour = token[2];
s.size = token[3];
s.quantity = token[4];
s.cost = token[5];
}
}
return is;
}
// Overwrite inserter operator
friend std::ostream& operator << (std::ostream& os, const Stock& s) {
return os << "ID: " << s.id << "\nTitle: " << s.colour
<< "\nSize: " << s.size << "\nQuantity: " << s.quantity << "\nCost: " << s.cost;
}
};
int main() {
// Open file and check, if it could be opened
if (std::ifstream inFile("stock.txt"); inFile) {
// Define the variable and use range constructor to read and parse the complete file
std::vector stocks(std::istream_iterator<Stock>(inFile), {});
// Show result to the user
std::copy(stocks.begin(), stocks.end(), std::ostream_iterator<Stock>(std::cout, "\n"));
}
return 0;
}
Please note: I am using C++17 and can define the std::vector without template argument. The compiler can deduce the argument from the given function parameters. This feature is called CTAD ("class template argument deduction").
Additionally, you can see that I do not use the "end()"-iterator explicitely.
This iterator will be constructed from the empty brace-enclosed initializer list with the correct type, because it will be deduced to be the same as the type of the first argument due to the std::vector constructor requiring that.
You should model each row with a class or struct:
struct Record
{
int id;
std::string title;
std::string colour;
std::string size;
int quantity;
double price;
friend std::istream& operator>>(std::istream& input, Record & r);
};
std::istream& operator>>(std::istream& input, Record & r)
{
char comma;
input >> r.id;
input >> comma;
std::getline(input, r.title, ',');
std::getline(input, r.colour, ',');
std::getline(input, r.size, ',');
input >> r.quantity;
input >> comma;
input >> r.price;
input.ignore(100000, '\n');
return input;
}
Now you can read into a list:
std::list<Record> database;
Record r;
while (infile >> r)
{
database.push_back(r);
}
The overloading of operator>> makes the code simpler and easier to read.
I'm trying to get inputs from a file by overloading the istream operator. For that, I declared it as friend of a class. Then, I take as input that same class like this:
file >> *(class pointer);
When I'm trying to debug the part of my code that need this to work, it goes as expected into this:
istream& operator>> (istream& in, MYCLASS& n)
{
string buffer;
while (!in.eof()) { // input is a file
in >> buffer;
// do stuff
}
return in;
}
The problem is that the buffer stays empty ("") and does not take what it's suppose to be taking from the file. Normally, the format of the file should not be a problem since I'm using a similar method elsewhere in my code without a problem, but here it is in case:
* Name Age
* Name Age
* Name Age
...
What should I put inside my istream overload function so i get inputs as intended?
This...
while (!in.eof()) {
...is broken. You should attempt to read and parse data into your variables, then check for failure/eof. Do not assume that you'll necessarily be at the end of file after reading the last MYCLASS. Instead:
string name;
int age;
while (in >> name >> age)
...do stuff...
If you've really got some kind of leading dot on each line, add a char and read into it too:
char dot;
string name;
int age;
while (in >> dot && dot == '.' && in >> name >> age)
...do stuff...
More generally, it's not a very scalable model to assume the rest of the input stream will contain one MYCLASS object. You could instead have a delimiter (e.g. when the first word on a line is not a name, but <END>), that terminates the loop.
I am trying to finish my lab, however I don't know how to allocate memory to a string. So I keep getting the error
warning: ‘_name’ is used uninitialized in this function [-Wuninitialized]
I don't also understand if my getline line is correct.
std::istream& operator>>(std::istream& is, Grade& RO){
int _mark;
char* _name;
std::cout<<"Subject name: ";
is.ignore();
is.getline(_name, (strlen(_name) + 1));
std::cout<<"Mark :";
is>> _mark;
RO=Grade(_name, _mark);
return is;
}
Ok #Jessica, (to general question and few info) I guess,
Grade is a class with two data members: int mark and string name. And you want to overload the insertion operator >> to populate these values.
(I recommend you leave all the cout expression outside this function). Here is one possible implementation:
istream& operator>> (istream& is, Grade& RO){
// declare local variables to insert the input values
int mark;
string name;
// extract values from input stream
is >> mark >> name;
// assuming you have member functions that set values for the object RO
RO.set_mark(mark);
RO.set_name(name);
return is;
}
I assigned myself some homework over the summer, and the project I am 98% finished with has come to a standstill due to this one problem.
I have a class called Mixed. It contains member data for a whole number, a numerator, and a denominator. I need to overload all of the common operators to allow multiplication, addition, comparison and streaming of objects of type Mixed. I have all the operators overloaded except for >> (the extraction operator).
All mixed numbers read in will be of format:
whole numerator/denominator
ex: 1 2/3, 0 7/8, -3 18/5, 0 -1/89
Header: friend istream& operator>> (istream &, Mixed);
CPP file: istream& operator>> (istream &in, Mixed m) {...}
For the assignment, I am limited to the iostream and iomanip libraries. My plan was to read in the values from the stream and assign them to temporary int variables (w, n, d) which I would then use with the Mixed constructor to create object m. Unfortunately, I cannot think of a way to separate the numerator and denominator. They are both ints, but they have a char (/) between them.
I cannot use getline() with its delimiter, because it assigns data to a char array, which I do not believe I can convert to an int without another library.
I cannot use a char array and then segment it for the same reason.
I cannot use a while loop with get() and peek() because, again, I do not think I will be able to convert a char array into an int.
I cannot use a string or c-string and then segment it because that requires external libraries.
Once again, I need to split a value like "22/34" into 22 and 34, using only iostream and iomanip. Is there some fairly obvious method I am overlooking? Is there a way to implicitly convert using pointers?
You could first extract the nominator, then the separating character, and then the denominator.
Example for illustration:
istream& operator>> (istream &in, Mixed &m) {
int num, denom;
char separ;
in >> num;
in.get(separ);
if (separ != '/')
in.setstate(ios::failbit);
in >> denom;
if (in) {
// All extraction worked
m.numerator = num;
m.denominator = denom;
}
return in;
}
Once again, I need to split a value like "22/34" into 22 and 34, using
only iostream and iomanip.
Couldn't you just read in the first integer, use get to get the next character, and then read the second integer? Something like this:
#include <iostream>
int main() {
using std::cin;
using std::cout;
int num;
int den;
while(cin) {
cin >> num;
if (cin.get() != '/') {
// handle error
}
cin >> den;
cout << num << "/" << den << std::endl;
}
return 0;
}
You can then make sure that the character read between the two integers was a '/' and handle appropriately if it isn't.
I have a class template, which is -- among other things -- supposed to overload istream so it accepts the user's input and adds (pushes) it into a vector, which holds type T elements.
friend istream &operator>> (istream &in, Set<T> &s)
{
int ctr = 0;
string tmp;
T val;
while (true) {
cout << "\tElement #" << ctr + 1 << ": ";
getline (in, tmp);
if (tmp == "\0") break;
// MISSING CODE HERE: "Convert" tmp into val
s.add(val);
ctr = s.size();
}
return in;
}
This works fine with Set<string>, but I need to find a way to make it function with any primitive data type, too, i.e. Set<integer>, for example.
I tried doing
stringstream(tmp) >> val
but that then doesn't work with Set<string>.
I guess the input needs to be read in as string; how, then, do I cast the input string into T type to pass it to the .add() function?
You could use a stringstream object (created from tmp) to extract the correct value if it is not a string. Note you will have to overwrite operator>> for ostream if you need more than just the built-in types (e.g. int)
Alternatively, you can define overloads of a convert() function for each T you encounter to provide proper conversion from the string tmp to the required type. The proper overload (if exists) will be selected based on your template parameter.
You can use boost::lexical_cast to convert strings to other types lexicographically.
template<class T>
friend istream &operator>> (istream &in, Set<T> &s) {
int ctr = 0;
string tmp;
T val;
while (true) {
cout << "\tElement #" << ctr + 1 << ": ";
getline (in, tmp);
if (tmp == "\0") break;
val = boost::lexical_cast<T>(tmp);
s.add(val);
ctr = s.size();
}
return in;
}
Alternatively, you can use std::istringstream.
std::istringstream sstream(tmp);
sstream >> val;
Note that boost::lexical_cast throws an exception if the cast wasn't successful, and std::istringstream does not (you need to check this manually).
There a few issues in the code that you may like to address:
getline (in, tmp);
That need to check the return value:
if(!getline (in, tmp))
// handle end-of-file or read failure
// read okay, tmp contains the read line
Next:
if (tmp == "\0") break;
It's not very common to have embedded zeros in text files. May be this piece is trying to detect the end-of-file condition, which should be handled by checking the return value of getline or if(in) statement.
stringstream(tmp) >> val;
That creates a temporary string stream and tries to invoke operator>> on it. Well, there are two kinds of operator>> for std::istream:
std::istream member functions that accept temporary (r-values) and l-values std::istream object
free standing ones that take std::istream by reference to non-const, which don't accept r-values. (In C++11 it can accept it also as std::istream&& allowing for r-values).
Hence, the above statement can only stream into built-in types for which there is a member operator>> in std::istream.
So, that statement can be replaced with:
std::istringstream ss(tmp);
ss >> val;
That would need to have error handling again to check whether entire str was parsed into val, so, as others have said here, it's easier to use boost::lexical_cast<> that does error checking for you:
val = boost::lexical_cast<T>(tmp);
If you don't insist on reading the full line, rather reading white-space separated tokens, then the loop can look like this:
template<class T>
friend std::istream &operator>>(std::istream &in, Set<T> &s) {
int ctr;
T val;
while(in >> val) {
ctr = s.size();
std::cout << "\tElement #" << ctr + 1 << ": ";
s.add(val);
}
// end-of-file or parsing failure
if(!in.eof()) {
// handle failuer to parse or read error
}
}