this is my function
void global::readData(lifeform &p)
{
std::ifstream ifs("data.txt");
int i=0,i_str=0;
std::string str,str2;
std::getline(ifs,str);
std::stringstream ss(str);
if(i<31) {
std::getline(ss,str2,',');
std::istringstream toint(str2);
toint >> i_str;
if(i_str ==1) { //here i_str can get 1
p.newgame=1;
while(i<31) {
std::getline(ss,str2,',');
toint >> i_str;
if(i==0) {
p.setName(str2); //this also works
}
if(i==1) {
p.setMaxHp(i_str); //doesnt work
}
if(i==2) {
p.setQName("main",str2); //works
}
i++;
//....
}
}
this is data.txt
1,furkan,100,No mission,
i tried to get what is written on the data.txt to str.there is no problem so far.after than i wanted to convert string to int it worked in the first converting but not the rest
i changed 1 to 0 to see if it works or not and it works but the rest i_str doesnt work
1) std::istringstream get his own copy of str2. It don't watch changes of this variable.
2) And how you should do it:
char c;
ss>>i_str//read int value from stream
>>c;//and throw away separator
You don't need getline here.
Warning: this answer is kind of overkill for the specific job at hand. It uses a couple of fairly general-purpose utilities that make the job easy, but probably would be worth writing if this was the only time/place you had a use for them (but it's not--they turn out to be quite useful in general, once you have them). I keep them in an "input_utils.h", where I can use them from almost anything quite easily.
The first is an input operator for a constant string. That may sound utterly pointless (if it's constant, you obviously can't read into it). What it does is read some fixed input from a stream, and verify that what was in the stream matches what you expected. The code looks like this:
std::istream &operator>>(std::istream &is, char const *label) {
if (is.flags() & std::ios::skipws)
while (std::isspace((unsigned char)is.peek()))
is.ignore(1);
while (is.peek() == *label) {
is.ignore(1);
++label;
}
if (*label)
is.setstate(std::ios::failbit);
return is;
}
This lets us read (most of) the commas in your input quite easily--verify that they're present, but otherwise ignore them. So, to read three numbers separated by commas, we would do something like this:
input_stream >> number1 >> "," >> number2 >> "," >> number3;
At least to me, the meaning seems pretty simple, straightforward and obvious (and the applicability to the job at hand seems almost equally obvious).
The second has a clear and obvious intent, but the implementation is a little less trivial than I'd like (but I don't know a way to make it any simpler and still do the job). Its code looks like this:
class line_proxy {
std::string &s;
char delim;
public:
line_proxy(std::string &s, char delim) : s(s), delim(delim) {}
friend std::istream &operator>>(std::istream &is, line_proxy const &p) {
std::getline(is, p.s, p.delim);
return is;
}
};
line_proxy line(std::string &in, char delim = '\n') {
return line_proxy(in, delim);
}
We use it like this:
std::string s;
input_stream >> line(s); // equivalent to `std::getline(input_stream, s);`
Again, even though the implementation is a bit on the complex side, the use seems pretty obvious (at least to me).
Once we have these, your job borders on trivial. The one part that I'd do that might initially seem a little on the tricky side would be implementing the reading in a stream extraction operator for the lifeform class:
#include "input_utils.h"
struct lifeform {
int index;
std::string name;
int level;
std::string mission;
friend std::istream &operator>>(std::istream &is, lifeform &f) {
return is >> f.index
>> ","
>> line(f.name, ',')
>> f.level
>> ","
>> line(f.mission, ',');
}
We might want to be able to write out a lifeform as well:
friend std::ostream &operator<<(std::ostream &os, lifeform const&f) {
return os << "Index: " << f.index
<< ", name: " << f.name
<< ", level: " << f.level
<< ", mission: " << f.mission;
}
...and a little code to test them and see that they work correctly:
int main() {
std::istringstream input("1,furkan,100,No mission,");
lifeform f;
input >> f;
std::cout << f;
}
Related
I have the following struct:
struct Person{
std::string name;
std::string address;
std::string& personName(){ return name; }
std::string& personAddress(){return address;}
};
The exercise is to write a read function that will read name and address. For example, the function I first wrote was this:
std::istream &read(std::istream &is, Person &person){
is >> person.name >> person.address;
return is;
}
However this function fails to take more than a word for address. For example if input is:
Lee Goswell Road
The output will be person.name = "Lee" and person.address = "Goswell". What I want is the function to read the entire address basically. I did try solving this problem as follows, but I am not sure it is right because address is changed implicitly:
std::istream &read(std::istream &is, Person &person){
is >> person.name;
std::getline(std::cin, person.address);
return is;
}
Another thing to consider before saying I should write separate functions, the task is to write one function to take read both the name and address.
You can use operator>> in tandem with std::getline but you'll probably want to eat the white-space from the stream first.
Also rather than read, you should just create your own operator>>:
std::istream& operator>>(std::istream& is, Person& person){
is >> person.name >> std::ws;
std::getline(is, person.address);
return is;
}
You can then use this as follows:
std::istringstream foo("Lee Goswell Road\nJon Lois Lane");
Person bar;
foo >> bar;
std::cout << bar.name << std::endl << bar.address << std::endl;
Just read a word, skip leading whitespace, then read to a delimiter:
if (is >> person.name >> std::ws
std::getline(is, person.address)) {
// do something with the input
}
else {
// deal with input failure
}
std::ws simply skips leading whitespace and std::getline() reads to delimiter with '\n' being the default.
I want to read in from txt file into structure using fstream.
I save the data to the file in the way shown below:
To read the data i tried some cheeky stuff with getlines or tabsin<
struct tab{
int type,use;
string name, brand;
};
tab tabs[500];
ofstream tabsout;
tabsout.open("tab.txt", ios::out);
for (int i = 0; i < 500; i++){
if (tabs[i].use==1){
tabsout << tabs[i].type << " " << tabs[i].name << " " << tabs[i].brand << "\n";
}
}
tabsout.close();
//input part that fails me :(
int i=0;
ifstream tabsin;
tabsin.open("tab.txt", ios::in);
if (tabsin.is_open()){
while(tabsin.eof() == false)
{
tabsin >> tabs[i].type>>tabs[i].name>>tabs[i].brand;
i++
}
tabsin.close();
You usually want to overload operator>> and operator<< for the class/struct, and put the reading/writing code there:
struct tab{
int type,use;
string name, brand;
friend std::istream &operator>>(std::istream &is, tab &t) {
return is >> t.type >> t.name >> t.brand;
}
friend std::ostream &operator<<(std::ostream &os, tab const &t) {
return os << t.type << " " << t.name << " " << t.brand;
}
};
Then you can read in a file of objects like:
std::ifstream tabsin("tab.txt");
std::vector<tab> tabs{std::istream_iterator<tab>(tabsin),
std::istream_iterator<tab>()};
....and write out the objects like:
for (auto const &t : tabs)
tabsout << t << "\n";
Note that (like any sane C++ programmer) I've used a vector instead of an array, to (among other things) allow storing an arbitrary number of items, and automatically track how many are actually being stored.
For starters, do not use .eof() to control your loop: it doesn't work. Instead, use the stream's state after reading:
int type;
std::string name, brand;
while (in >> type >> name >> brand) {
tabs.push_back(tab(type, name, brand));
}
If your name or brand contain spaces, the above won't work and you will need to write a format where you can know when to stop abd read correspondingly, e.g., using std::getline().
You might also consider wrapping the logic to read or write an object by suitable operators.
istream& getline (istream& is, string& str, char delim);
Take a look at the third parameter, you can use std::getline to parse your line. But that is definitely not the best way to serialize objects. Instead of using a text file, you should use a byte stream.
I'm overloading the operator>> function. It should take a string in input, with some whitespaces needed, explode the string at whitespaces and do other operations not relevant for the topic.
I have this code:
std::istream& operator>>(std::istream &in, Foo &f) {
std::string str;
in >> str;
std::cout << "str = " << str << std::endl; // for testing
// ...
return in;
}
Assuming to put this string (a complex number) as input:
3 + 2i
the std::cout function prints only 3. I tried to put the flag std::noskipws, but the problem is still there.
Is there any way to solve this issue?
Use std::getline function to read complete input line:
std::getline(in, str);
I'm trying to get back into C++, and this is my second program in a long while. Everything compiles just peachy, until it gets to cin >> stopat; where it returns what seems to be a fairly common error: error: no match for 'operator>>' in 'std::cin >> stopat'
I've looked through a few things explaining what causes this, but nothing I actually understand (due to my relative inexperience in programming). What causes this error, and how do I fix it in case I come across it again?
#include <iostream>
#include "BigInteger.hh"
using namespace std;
int main()
{
BigInteger A = 0;
BigInteger B = 1;
BigInteger C = 1;
BigInteger D = 1;
BigInteger stop = 1;
cout << "How Many steps? ";
BigInteger stopat = 0;
while (stop != stopat)
{
if (stopat == 0)
{
cin >> stopat;
cout << endl << "1" << endl;
}
D = C;
C = A + B;
cout << C << endl;
A = C;
B = D;
stop = stop + 1;
}
cin.get();
}
EDIT: Somehow, I didn't think to link the libraries referenced. Here they are: https://mattmccutchen.net/bigint/
You haven't shown us the code for BigInteger, but there would need to be a function defined (either in BigInteger.hh or in your own code) like this:
std::istream& operator >>(std::istream&, BigInteger&);
This function would need to be implemented to actually get a "word" from a stream and try to convert it to a BigInteger. If you're lucky, BigInteger will have a constructor that takes a string, in which case it would be like this:
std::istream& operator >>(std::istream& stream, BigInteger& value)
{
std::string word;
if (stream >> word)
value = BigInteger(word);
}
Edit: Now that you have pointed out the library that's being used, here's what you can do. The library itself should probably do this for you, since it provides the corresponding ostream operator, but if you look into that you will see that general-purpose, library-quality stream operators are more complex than what I'm writing here.
#include <BigIntegerUtils.hh>
std::istream& operator >>(std::istream& stream, BigInteger& value)
{
std::string word;
if (stream >> word)
value = stringToBigInteger(word);
}
What you've left out here is details about your BigInteger class. In order to read one from an input stream with the >> operator, you need to define operator>> (often called a stream extractor) for your class. That's what the compiler error you're getting means.
Essentially, what you need is a function that looks like this:
std::istream &operator>>(std::istream &is, BigInteger &bigint)
{
// parse your bigint representation there
return is;
}
I'm having some trouble reading data from a file into a vector of Orders.
Code:
#include <string>
#include <vector>
#include <fstream>
#include <iostream>
#include <iterator>
using namespace std;
class Purchase;
class Order {
public:
string name;
string address;
vector<Purchase> items;
};
class Purchase {
public:
string product_name;
double unit_price;
int count;
Purchase() {}
Purchase(string pn, double up, int c) :product_name(pn), unit_price(up), count(c) {}
};
istream& operator>>(istream& in, Order& o)
{
string p_name;
double u_price;
int p_count;
getline(in, o.name);
getline(in, o.address);
getline(in, p_name);
in >> u_price >> p_count;
o.items.push_back(Purchase(p_name, u_price, p_count));
return in;
}
ostream& operator<<(ostream& out, const Purchase& p)
{
out << p.product_name << '\n'
<< p.unit_price << '\n'
<< p.count << '\n';
return out;
}
ostream& operator<<(ostream& out, const Order& o)
{
out << '\n' << o.name << '\n'
<< o.address << '\n'
<< o.item << '\n';
return out;
}
int main()
{
cout << "Enter file to read orders from: \n";
string file;
cin >> file;
ifstream is(file.c_str());
istream_iterator<Order> ii(is);
istream_iterator<Order> eos;
ostream_iterator<Order> oo(cout);
vector<Order> orders(ii, eos);
copy(orders.begin(), orders.end(), oo);
}
I have 3 main questions.
1) When I take out the o.item bug in the ostream overload to test output, it only outputs the first entry in the file. The txt file is structured in groups of 5 lines of data that are supposed to be read into vector orders.
Right now the txt file has 10 "orders", but it only reads the first one into the orders vector. I probably need to implement some kind of end of file operation, but I'm not sure how to do this with the istream overload and iterator. This is the biggest problem and if I can figure this out I think I'll probably be okay with the next 2 questions.
2) When that problem is fixed. I will need to deal with the output of o.item (the vector of Purchases in orders which currently can't be output because there is no element being specified). Obviously I need to specify the element to output and I've considered just using a static int and incrementing it, but this would need to be reset for every separate Order, which leads to question 3...
3) If the same name/address are read in as a previous read, I need the program to understand that it is the same "order" being read in and to simply add another object to that Order's Purchases vector rather than creating a new order. I'm thinking about using find() to check if that name already exists in order, and in that case doing nothing with the name/address inputs, but if there is a better way I'd like to know.
Sorry if this is kind of long. If more explanation is needed I'd be happy to elaborate. Any help is appreciated. Thanks.
P.S. Here is an example of input output at the moment if I specify the o.item output to be o.item[0].
Text file has:
John Smith
117 One Tree Hill
Trampoline
600.00
1
//... 9 more Orders like this
Output is:
John Smith
117 One Tree Hill
Trampoline
600.00
1
//... Nothing after this....
Regarding question #3, you could use a multimap instead of a vector.
First, assume you split your Order class up as follows:
class Customer{
public:
string name;
string address;
};
class Purchase {
public:
string product_name;
double unit_price;
int count;
Purchase() {}
Purchase(string pn, double up, int c) :product_name(pn), unit_price(up), count(c) {}
};
class Order {
Customer c;
std::vector<Purchase> p;
};
Now you can simply create a std::multimap<Customer, Purchase>. Adding a customer/purchase pair does exactly what you want: If the customer doesn't already exist, he is added, otherwise the purchase is just added to the existing customer.
Of course, for this to work, you need to define a comparer as well. Simplest way might just be to define operator < for the Customer class. Implement it by comparing the name and disregarding the address.
As for your other questions, avoid mixing getline and stream_iterators. It's not wrong per se, but it gets pretty tricky because getline reads a line at a time, and stream iterators just read to the next whitespace.
Honestly, the C++ IOStreams library is pretty awful to use in general. Since your data format is already cleanly line-separated already, I'd probably just ditch the stream iterators and use getline everywhere.
I haven't looked at your code in detail, but I will give one sentence of advice:
"Do not mix formatted and unformatted input. And in fact, do not use formatted input from files or user input at all."
OK, that was two sentences.
The problem you have is very simple. In fact your code is pretty clear :)
All what you have to add are those simple lines:
istream& operator>>(istream& in, Order& o)
{
string p_name;
double u_price;
int p_count;
getline(in, o.name);
getline(in, o.address);
getline(in, p_name);
in >> u_price >> p_count;
//
while(in.peek() == '\n' || in.peek() == ' ')
{
in.ignore();
}
//
o.items.push_back(Purchase(p_name, u_price, p_count));
return in;
}
The reason is that when using >> it leaves the newline character in the stream unlike getline. You can search Stackoverflow about streams in general there are a lot of great explanations about the issue.
Also, you don't have anything called item in Order. What you have is a vector of Purchase:
ostream& operator<<(ostream& out, const Order& o)
{
out << '\n' << o.name << '\n'
<< o.address << '\n';
//
for(vector<Purchase>::const_iterator i = o.items.begin();
i != o.items.end(); i++)
{
out << *i << '\n';
}
//
return out;
}