C++ 如何验证与重载 >> 运算符一起使用的输入

C++ how to validate input for use with overloaded >> operator

我正在编写一个可以对复数进行运算的程序。我有一个名为 ComplexNumber 的 class,其中包含重载运算符。我的程序以 complex *operator* complex 的形式从文件中获取输入。因此,例如输入看起来像 3+4i + 2+3i。我已经写了我的 >> 运营商,所以这工作正常。

当输入看起来像 3i + 1+2i 时,就会出现问题。我们必须验证输入,以便在复数缺失部分时它能正常工作。可以是实数,也可以是虚数。

ComplexNumberclass中与此问题相关的函数如下:

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;
}

我在主 class 中读取输入的方式如下:

 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;
}

为了让我的运算符工作,我需要在对象中将输入的缺失部分设置为 0。因此,如果输入是 3i,则对象中的变量将是 realNum = 0, imaginaryNum = 3 我该如何实现?

如何检查行中的输入以决定应如何读入?目前,它期望复数同时具有实部和虚部。

我还为复数只有一个部分的情况编写了一个重载构造函数,但我不确定如何使用它。函数如下:

ComplexNumber::ComplexNumber(double in, string r_i)    //Overloaded constructor
{
    if(r_i == "r"){realNum = in; imaginaryNum = 0;}
    else{imaginaryNum = in; realNum = 0;}
}

除了这个问题,我们还必须检查以确保输入没有无效字符,例如。 j or ! 但我觉得如果我在第一个问题上得到帮助,我可以使用提供的信息来解决第二个问题。

我知道这可能不是最好的措辞,我只是希望你能理解我想要达到的目标。我真的很感激任何帮助。谢谢。

通常我会用状态机来做这件事。以前从未用 C++ 流做过。比看起来更偷偷摸摸,但基本相同。评论嵌入代码中的内容和原因。

#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!");
}

输出:

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!