Overloading operator help? - c++

as ever I'm fairly new to C++ and I'm not exactly up with the lingo yet either so I apologize for sounding vague in advance!
My problem is I'm struggling to see why my while loop seems to stop the rest of my methods in my overloaded operator function;
#include "sample.h"
#include <iostream>
#include <vector>
#include <cstdlib>
using namespace std;
sample::sample(vector<double> doubles){}
sample::sample() {}
ostream& operator<< (ostream &out, sample &sample)
{
out << "<" << sample.n << ":";
return out;
}
istream& operator>> (istream &in, sample &sample)
{
char firstChar;
in >> firstChar;
if(firstChar != '<'){
cout << "You've not entered the data in a valid format,please try again!1 \n";
exit(1);
}
int n;
in >> n;
sample.n = n;
char nextChar;
in >> nextChar;
if(nextChar != ':'){
cout << "You've not entered the data in a valid format,please try again!2 \n";
exit(1);
}
vector<double> doubles;
double number;
while (in >> number){
doubles.push_back(number);
cout << in << " " << number;
}
in >> lastChar;
return in;
}
int main(void)
{
sample s;
while (cin >> s){
cout << s << "\n";
}
if (cin.bad())
cerr << "\nBad input\n\n";
return 0;
}
My input would be something like;
<6: 10.3 50 69.9 >
I'm trying to get all the doubles after the ':' into a vector, which I can do if they're ints but once a '.' is entered it seems to stop.
If I only put integers in, it also seems to stop after the while(in >> number) has finished finding all the numbers, which is fine but the cout<< command in my main function doesn't seem to work!
Where have I gone wrong?

You have to obey the standard stream idioms: every stream is implicitly convertible to a bool (or void pointer) to allow a check like if (in >> n) to see if the operation succeeded. So first of all you have to make sure that your operator conforms to this (by ensuring that the stream is "good" if the extraction succeeded).
Second, when you write a loop like while (in >> x) { /*...*/ }, then after the loop terminates, you already know that your stream is no longer good. So you'll have to call clear() on it before returning it.
Maybe something like this:
std::istream& operator>> (std::istream &in, sample &sample)
{
char c;
int n;
double d;
std::vector<double> vd;
if (!(in >> c)) { return in; } // input error
if (c != '>') { in.setstate(std::ios::bad); return in; } // format error
if (!(in >> n)) { return in; } // input error
if (!(in >> c)) { return in; } // input error
if (c != ':') { in.setstate(std::ios::bad); return in; } // format error
while (in >> d)
{
vd.push_back(d);
}
in.clear();
if (!(in >> c)) { return in; } // input error
if (c != '>') { in.setstate(std::ios::bad); return in; } // format error
state.n = n;
state.data.swap(vd);
return in;
}
Note that we only modify the sample object if the entire input operation succeeded.

cout << in << " " << number;
you probably meant
cout << " " << number;
or something

Related

How do I write an array of contents into a text file? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 2 years ago.
Improve this question
The code is supposed to open an existing text file, transfer the contents into the array, then create a new text file and then write the array contents into the new text file. The problem I'm having is that the code only outputs the last line of the content from the new text file.
file.open("Patient.txt", ios::in);
while (!file.eof()) {
file >> arr[i].name >> arr[i].DOB >> arr[i].address >> arr[i].Dr_name >> arr[i].V_date;
/*cout << arr[i].name << arr[i].DOB << arr[i].address << arr[i].Dr_name << arr[i].V_date << endl;*/
}
file.close();
files.open("Patients_2.txt");
if (files.is_open()) {
for (i; i < 30; i++) {
files << arr[i].name << arr[i].DOB << arr[i].address << arr[i].Dr_name << arr[i].V_date;
}
}
files.close();
patientss.open("Patients_2.txt");
cout << "Patient 2: " << endl;
while (!patientss.eof()) {
getline(patientss, line);
cout << line << endl;
}
patientss.close();
system("pause");
return 0;
}
IMHO, you should overload the formatted insertion and extraction operators in your patient class:
struct Patient
{
std::string name;
std::string dob;
std::string address;
std::string dr_name;
std::string v_date;
friend std::istream& operator>>(std::istream& input, Patient& p);
friend std::ostream& operator<<(std::ostream& output, const Patient& p);
};
std::istream& operator>>(std::istream& input, Patient& p)
{
std::getline(input, p.name);
std::getline(input, p.dob);
std::getline(input, p.address);
std::getline(input, p.dr_name);
std::getline(input, p.v_date);
return input;
}
std::ostream& operator<<(std::ostream& output, const Patient& p)
{
output << p.name << "\n";
output << p.dob << "\n";
output << p.address << "\n";
output << p.dr_name << "\n";
output << p.v_date << "\n";
return output;
}
The above makes input and output easier:
std::vector<Patient> database;
Patient p;
while (input_file >> p)
{
database.push_back(p);
}
const unsigned int quantity = database.size();
for (unsigned int i = 0; i < quantity; ++quantity)
{
output_file << database[i];
}
The above code also supports the concepts of encapsulation and data hiding. The Patient struct is in charge or reading its members because it knows the data types of the members. The code external to the Patient is only concerned with the input and output of a Patient instance (doesn't care about the internals).
This loop has a few problems:
while (!file.eof()) {
file >> arr[i].name >> arr[i].DOB ....
You never increase i so the same arr[i] will be overwritten time and time again.
You use !file.eof() as a condition to stop reading. eof() does not get set until after you've tried to read beyond the end of the file, which means that if you had increased i as you should, the last arr would be empty / broken. Instead check if the extraction from the stream succeeded. Since the stream is returned when you do stream >> var and has an overload for explicit operator bool() const which returns !fail() you can do this check directly in your loop:
while(stream >> var) { extraction success }
Using formatted input (>>) for string fields that are likely to contain spaces is however not a good idea. Your name, nung khual, would be split so nung would go into name and khual would go into DOB. It's better to use a field separator that is very unlikely to be included in anyone's name. \n is usually good and works well with std::getline.
std::getline returns the stream that you gave as an argument which means that you can chain getlines similarly to stream >> var1 >> var2, except it's a little more verbose.
getline(getline(stream, var1), var2) will put the first line in var1 and the second line in var2.
To make input and output a little simpler you can add stream operators for your data type and make the input stream operator use getline for your fields.
Example:
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
struct data_t {
std::string name;
std::string DOB;
std::string address;
std::string Dr_name;
std::string V_date;
};
// input stream operator using chained getlines
std::istream& operator>>(std::istream& is, data_t& d) {
using std::getline;
return getline(getline(getline(getline(getline(is,
d.name), d.DOB), d.address), d.Dr_name), d.V_date);
}
// output stream operator
std::ostream& operator<<(std::ostream& os, const data_t& d) {
return os << d.name << '\n'
<< d.DOB << '\n'
<< d.address << '\n'
<< d.Dr_name << '\n'
<< d.V_date << '\n';
}
int main() {
std::vector<data_t> arr;
if(std::ifstream file("Patient.txt"); file) {
data_t tmp;
while(file >> tmp) { // remember, no eof() needed
arr.push_back(tmp);
}
}
if(std::ofstream file("Patients_2.txt"); file) {
for(const data_t& d : arr) {
file << d;
}
}
if(std::ifstream patientss("Patients_2.txt"); patientss) {
data_t tmp;
while(patientss >> tmp) {
std::cout << tmp;
}
}
}

Pushing back an object to a list in c++

I have an issue with pushing back object to a list of objects.
std::ostream& operator << (std::ostream& os, const Class1& sk) {
os << sk.X << 'x' << sk.Y << 'x' << sk.H << ';' << sk.a << ';' << sk.b << '\n';
return os;
}
std::istream& operator >> (std::istream& is, Class1& sk) {
char ch;
is >> sk.X >> ch >> sk.Y >> ch >> sk.H >> ch >> sk.a >> ch >> sk.b >> ch;
return is;
}
void Class1::GetSK_list(std::list<Class1>& SK_list) {
std::ifstream file("file.txt", std::ios::in);
std::list<Class1>::iterator iter = SK_list.begin();
while(file >> *iter) {
"std::cout << *iter"; // checking
SK_list.push_back(*iter);
iter++;
}
file.close();
}
void Class1::SaveSK_list(std::list<Class1>& SK_list) {
std::ofstream file("file.txt", std::ios::out);
for(std::list<Class1>::iterator iter = SK_list.begin(); iter != SK_list.end(); iter++)
file << *iter;
file.close();
}
When I'm using SaveSK_list function, it works correctly.
The problem is with GetSK_list function. If there's one line in file (e.g. 1x2x3;4;5) file >> *iter doesn't work. There's nothing printed (I believe std::cout << *iter is never reached).
If there's more than one line in file, only the very first one is being printed. After that std::bad_alloc error appears.
Btw. these functions are in Class1 in order to use operators overloading.
You can use an insert iterator, which will automatically call push_back() on the list:
void GetSK_list(std::list<Class1>& SK_list) {
auto iter = std::back_inserter(SK_list);
std::ifstream file("file.txt", std::ios::in);
Class1 tmp;
while(file >> tmp) {
*iter++ = tmp;
}
file.close();
}
You can do even better by combining with an istream_iterator and the copy algorithm:
void GetSK_list(std::list<Class1>& SK_list) {
std::ifstream file("file.txt", std::ios::in);
std::copy(std::istream_iterator<Class1>(file),
std::istream_iterator<Class1>(),
std::back_inserter(SK_list));
}
There's also no need to call file.close(); the file is closed in the ifstream destructor.

Data not being correctly read from input file

please see below c++ function. The point of it is to store variables in an input file ("is") to global arrays. I go through with the debugger with a watch on the "temp" variable (the first if statement seems to work fine), and after reading the first line of the input file, the variable temp no longer updates.
The file is in a specific format so it should be reading an int, although I did eventually put a char at the end of the KEYFRAME if statement to see if it was reading the endline character (it wasn't).
What are some possible reasons for this? Thank you so much!
void readFile(istream& is){
string next;
int j = 0;
int i = 0;
while (is){
for (int i = 0; i < F; i++){
is >> next;
if (next == "OBJECT")
{
int num;
is >> num;
string name;
is >> name;
objects[j].objNum = num;
objects[j].filename = name;
j++;
}
else if (next == "KEYFRAME"){
int k;
int temp;
is >> k;
int time;
is >> time;
objects[k].myData[time].setObjNumber(k);
objects[k].myData[time].setTime(time);
is >> temp;
objects[k].myData[time].setPosition('x', temp) ;
is >> temp;
objects[k].myData[time].setPosition('y', temp);
is >> temp;
objects[k].myData[time].setPosition('z', temp);
is >> temp;
objects[k].myData[time].setRotation('x', temp);
is >> temp;
objects[k].myData[time].setRotation('y', temp);
is >> temp;
objects[k].myData[time].setRotation('z', temp);
is >> temp;
objects[k].myData[time].setScaling('x', temp);
is >> temp;
objects[k].myData[time].setScaling('y', temp);
is >> temp;
objects[k].myData[time].setScaling('z', temp);
char get;
is >> get;
}
else {
cout << "Error reading input file";
return;
}
}
}
}
The most common reasons why std::istream's operator>> does not update the variable can be demonstrated using the following simple example:
#include <sstream>
#include <iostream>
int main() {
std::string sample_input="1 2 3 A 4 5";
std::istringstream i(sample_input);
int a=0, b=0, c=0, d=0;
std::string e;
int f=0;
i >> a >> b >> c >> d >> e >> f;
std::cout << a << std::endl;
std::cout << b << std::endl;
std::cout << c << std::endl;
std::cout << d << std::endl;
std::cout << e << std::endl;
std::cout << f << std::endl;
}
The resulting output from this program is:
1
2
3
0
0
This example forces a conversion error on the fourth parameter. Once operator>> encounters a formatting conversion error, an error bit is set on the stream, which prevents further conversions.
When using operator>>, it's necessary to check for conversion errors after every input format conversion, and reset the stream's state, if necessary.
So, your answer is that you have an input conversion error someplace. I generally avoid using operator>>. It's fine for simple situations where the input is known, and there is no possibility of bad input. But as soon as you get into a situation where you might need to potentially handle bad input, using operator>> becomes painful, and its much better to take some other approach for parsing stream-based input.

Overload ifstream in c++

I am trying to read complex numbers of the form x + y*i from a file which looks like this:
2 + 3i
4 + 5i
If I implement it like this it only works for the first line and I would like to be able to read again in the same manner the second line. Any ideas?
istream& operator>>(istream& in, Complex& c) {
in >> c.r >> c.i;
return in;
}
EDIT: Do not throw exceptions directly, as this is not the usual way of doing thing with iostreams.
EDIT: Process the sign character separately so that spaces in the input are allowed.
A quick and dirty solution:
istream& operator>>(istream& in, Complex& c)
{
char sign;
char suffix;
in >> c.r >> sign >> c.i >> suffix;
if ((sign != '+' && sign != '-') || suffix != 'i') {
in.setstate(ios::failbit);
}
if (sign == '-') {
c.i = -c.i;
}
return in;
}
You need to make sure that you read both the "+" and the "i" when processing the input.
The following implementation works:
struct complex
{
double r;
double i;
};
std::istream& operator>>(std::istream& in, complex& c)
{
char plus, i;
in >> c.r >> plus >> c.i >> i;
return in;
}
std::string initdata = "2 + 3i\n4 + 5i\n";
int main()
{
std::stringstream ss(initdata);
std::vector<complex> values;
std::istream_iterator<complex> begin(ss), end;
std::copy(begin, end, std::back_inserter<std::vector<complex>>(values));
}
I would do it like this :
#include <iostream>
struct complex
{
int r, i;
};
int main ()
{
complex co;
char c;
while ((std::cin >> co.r >> c /* '+' */ >> co.i >> c /* 'i' */))
{
std::cout << co.r << ' ' << co.i << "i\n";
}
}
This should work for you:
#include <iostream>
#include <sstream>
int main(int argc, char *argv[])
{
std::istringstream s(
"2 + 3i\n"
"4 + 5i\n"
);
char sgn;
double r;
double i;
while(s >> r >> sgn >> i) {
if(s.get() != 'i') {
std::cerr << "Missing i\n";
return -1;
}
std::cout << r << sgn << i << "i\n";
}
return 0;
}
Note: The space before the imaginary part and the trailing i are breaking the input.

istringstream not storing anything in variables

I'm having an issue with istringstream not storing the values it reads. Here is what I have:
if(inputFile.good()){ //Make sure file is open before trying to work with it
//Begin Working with information
cout << "\tIn File: " << input << endl;
cout << "------------------------------------" << endl;
int number_of_lines = 0;
std::string line;
while (std::getline(inputFile, line)){
++number_of_lines;
}
Time times[number_of_lines];
double math[number_of_lines];
std::string input;
int hh, mm;
for(int loop=0;loop<number_of_lines;loop++){
std::getline(inputFile, input);
std::istringstream(input) >> mm >> hh >> math[loop];
cout << "hours = " << hh << endl;
times[loop].setTimeHours(hh);
times[loop].setTimeMinutes(mm);
times[loop].show();
cout << "*" << math[loop] << endl;
}
std::cout << "Number of lines in text file: " << number_of_lines << "\n" << endl;
}else{
cout << "Could not open file!!!" << endl;
}
The file I'm reading looks like this:
90 1 3.0
1 1 100.0
2 34 5.1
And the output when I run:
In File: data04.txt
------------------------------------
hours = 0
Operation To Be Done = 0:2336552*1.15384e-317
hours = 0
Operation To Be Done = 0:2336552*1.58101e-322
hours = 0
Operation To Be Done = 0:2336552*1.15397e-317
Number of lines in text file: 3
Anyone know why its not storing the values?
There are several key problems in this code
It doesn't check if inputs are successful. You always need to make sure you verify that the input operations worked before you process the data you read. Failing so will cause random data to be processed.
You first read to the end of the stream and then hope that the stream magically restarted. That won't work. Read the stream just once and keep appending to a std::vector<Time> (or similar container). Aside from only traversing the file once, on UNIXes the file size can change while reading.
C++ doesn't have variable sized arrays although some compiler may offer an extension similar to C's variable sized array. In C++ you'd use a std::vector<Time> instead.
First and foremost, your program is wrong. After the while loop ends, there is nothing more to read in the file (unless you seekg() back to the beginning), so the std::getline() call in the for loop body basically does nothing.
A second problem is that concerns are not properly separated.
Here is how I would have implemented this program:
struct line_data
{
Time t;
double x;
};
// This handles reading a single Time value.
std::istream & operator >> (std::istream & is, Time & t)
{
int hh, mm;
if (is >> hh >> mm)
{
// Not happy with the following two lines, too Java-like. :-(
t.setTimeHours(hh);
t.setTimeMinutes(mm);
}
return is;
}
// This handles reading a single line of data.
std::istream & operator >> (std::istream & is, line_data & ld)
{
std::string s;
if (std::getline(is, s))
{
std::istringstream iss(s);
// Ensure errors are propagated from iss to is.
if (!(iss >> ld.t >> ld.x))
is.setstate(std::ios::failbit);
}
return is;
};
// This handles processing a single line of data.
struct line_manip // satisfies concept OutputIterator<line_data>
{
std::back_insert_iterator<std::vector<Time>> ti;
std::back_insert_iterator<std::vector<double>> xi;
line_manip(std::vector<Time> & ts, std::vector<double> & xs)
: ti(std::back_inserter(ts))
, xi(std::back_inserter(xs))
{
}
line_manip & operator = (const line_data & ld)
{
ti = ld.t;
xi = ld.x;
return *this;
}
line_manip & operator * () { return *this; }
line_manip & operator ++ () { return *this; }
line_manip & operator ++ (int) { return *this; }
};
int main()
{
std::ifstream ifs("input.txt");
std::vector<Time> ts;
std::vector<double> xs;
std::copy(std::istream_iterator<line_data>(ifs),
std::istream_iterator<line_data>(),
line_manip(ts, xs));
// ...
}