Reputation: 539
I am writing a program that can do operations on complex numbers. I have a class called ComplexNumber that has the overloaded operators in it. My program takes input from a file in the form of complex *operator* complex
. So, for example an input would look like 3+4i + 2+3i
. I have written my >> operator so this works fine.
The issue arises when the input looks like 3i + 1+2i
. We have to validate the input so it works when the complex number is missing parts. It can be just a real number, or just an imaginary number.
The functions in the ComplexNumber class that relate to this issue are as follows:
ComplexNumber::ComplexNumber(double r,double i)
{
realNum = r;
imaginaryNum = i;
}
istream& operator>>(istream &input , ComplexNumber& other) //Overloaded >> operator
{
char filter = 0;
double r =0;
double i = 0;
input >> r >> i >> filter;
other.setR(r);
other.setI(i);
return input;
}
And the way I am reading in the input in my main class is as follows:
void input(ifstream &in)
{
ComplexNumber a,b;
in >> a;
in.get();
string op;
getline(in,op,' ');
in >> b;
cout << a << " " << op << " " << b << endl;
}
int main()
{
ifstream in("complex.txt");
if(!in) cout << "failed to open file." << endl;
while(!in.eof()){
input(in);
}
return 0;
}
For my operators to work, I need to set the missing part of the input as 0 in the object. So if the input was 3i
the variables in the object would be realNum = 0, imaginaryNum = 3
How can I achieve this?
How can I check the input on the line to decide how it should be read in? At the moment, it is expecting the complex number to have both a real and imaginary part to it.
I also wrote an overloaded constructor for cases where the complex number only has one of the parts to it, but I am unsure how to use it. The function is as follows:
ComplexNumber::ComplexNumber(double in, string r_i) //Overloaded constructor
{
if(r_i == "r"){realNum = in; imaginaryNum = 0;}
else{imaginaryNum = in; realNum = 0;}
}
Beyond this issue, we also have to check to make sure that the input has no invalid characters eg. j or !
but i feel that if I get help with this first problem, I can use the information given to solve this second problem.
I realize that this may not be worded in the best way, I just hope you understand what I am trying to achieve. I really appreciate any help with this. Thanks.
Upvotes: 2
Views: 2290
Reputation: 33952
Normally I'd do this with a state machine. Never done it with C++ streams before. Bit sneakier than it looked, but basically the same. Commentary on the whats and whys embedded as comments in the code.
#include <string>
#include <iostream>
#include <sstream>
#include <cmath>
#include <cctype>
// Made this really dumb for ease of writing example
struct ComplexNumber
{
double realNum;
double imaginaryNum;
};
// splitting the guts of the parsing off into its own function made writing
// operator>> dead easy
bool parsecomplex(std::istream &input,
double & real,
double & imag)
{
char filter;
double temp;
char next;
if (input >> temp)// read a double. No clue if it's the real or imaginary part yet.
{
next = input.peek(); // check the next character, but do not extract
if (next != 'i') // not imaginary
{
real = temp; // store as real
if (next == '+' || next == '-') // do we stop here or is there an imaginary?
{
if (input >> imag >> filter // read imaginary
&& filter == 'i') // and ensure trailing i
{
return true;
}
}
else
{
return true;
}
}
else
{ // just an imaginary
imag = temp;
input >> filter; // remove the i. we already know it's an i
return true;
}
}
return false;
}
std::istream& operator>>(std::istream &input,
ComplexNumber& other)
{
double real = 0.0;
double imag = 0.0;
if (parsecomplex(input, real, imag))
{ // OK so we got a good complex number.
other.realNum = real;
other.imaginaryNum = imag;
input.clear(); // may have read eof
return input;
/* This next bit is a deviation from normal stream parsing. Typically 3j
would be read and store of 3 as real and j stays in the stream for the
next read. OP sounds like they might need to be a bit more anal. If so,
replace the above with
char next = input.peek();
if (std::isspace(next) || next == std::char_traits<char>::eof())
{
other.realNum = real;
other.imaginaryNum = imag;
input.clear(); // may have read eof
return input;
}
The Law of Least Surprise says you should go with the expected parsing
behaviour so as to not leave a trail of confused and angry programmers
in your wake. */
}
input.setstate(std::ios::failbit);
return input;
}
// quick test harness
void test(const char * str)
{
ComplexNumber cnum;
std::stringstream input(str);
if (input >> cnum)
{
std::string remaining;
std::getline(input, remaining);
std::cout << str << " is " << cnum.realNum <<","<< cnum.imaginaryNum
<< " still in stream: " << remaining << std::endl;
}
else
{
std::cout << "Invalid: " << str << std::endl;
}
}
int main()
{
test("3-3i");
test("3");
test("-3i");
test(" 3-3i");
test("3-3i ");
test("3 ");
test("-3i ");
test("3-3i 3-3i");
test("3 -3i");
test("j3+3i");
test("3j3i");
test("3+3j");
test("3+3ij");
test("3j");
test("-3j");
test("-3ij");
test("");
test("DETHTONGUE!");
}
Output:
3-3i is 3,-3 still in stream:
3 is 3,0 still in stream:
-3i is 0,-3 still in stream:
3-3i is 3,-3 still in stream:
3-3i is 3,-3 still in stream:
3 is 3,0 still in stream:
-3i is 0,-3 still in stream:
3-3i 3-3i is 3,-3 still in stream: 3-3i
3 -3i is 3,0 still in stream: -3i
Invalid: j3+3i
3j3i is 3,0 still in stream: j3i
Invalid: 3+3j
3+3ij is 3,3 still in stream: j
3j is 3,0 still in stream: j
-3j is -3,0 still in stream: j
-3ij is 0,-3 still in stream: j
Invalid:
Invalid: DETHTONGUE!
Upvotes: 1