2

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.

Ryan Hosford
  • 479
  • 1
  • 7
  • 24
  • 1
    `while(!in.eof())` fails you. eof is set after you read and fail because of eof. Ergo you can't test for eof before reading. You have to test after reading but before using what you read in case you read nothing. More here: http://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-considered-wrong – user4581301 Sep 27 '16 at 18:54
  • Do you allow the complex numbers to be written as `(2i + 1)`, or will they always be in the `(1 + 2i)` format? – user2296177 Sep 27 '16 at 20:04
  • How would I get around this? If I use an if statement to get the line and check if it contains something, how can I get that line to check it's contents and then put the istream back at the start of that line so I can actually read the data for use? – Ryan Hosford Sep 27 '16 at 20:04
  • @user2296177 If both parts of the complex number are present, it will be in the format of `1+2i` – Ryan Hosford Sep 27 '16 at 20:05
  • Try parsing the input in `operator>>`. You check if it starts with a number that is postfixed by the character `i` or not, then decide how to initialize your type. – user2296177 Sep 27 '16 at 20:10
  • That's a good idea. I'll give that a shot. – Ryan Hosford Sep 27 '16 at 20:13

1 Answers1

1

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!
user4581301
  • 29,019
  • 5
  • 26
  • 45
  • Thanks a lot for this. This was perfect. Needed a few changes to work for the required style of input, but you nailed the part I needed help with. Thanks again. – Ryan Hosford Sep 29 '16 at 05:20