1
std::istream & operator >>(std::istream & ins, Rational & target)
{
    int num, den;
    char symb;          
    std::cout << "Please enter a rational number: ";
    ins >> num >> symb >> den;
    std::cout << std::endl;

    if(validateInput(num, symb, den)){
        target = Rational(num, den);            
        return ins;
    }
    else{
        std::cin >> target;
    }
}

bool validateInput(int num, char symb, int den)
{
    if(symb != '/'){
        std::cout << "Error: Illegal format. Please use '2/4'." << std::endl;
        return false;
    }
    if((static_cast<int>(num) != num) && (static_cast<int>(den) != den)){
        std::cout << "Error: Not a valid rational number." << std::endl;
        return false;
    }
    if(den == 0){
        std::cout << "Error: Cannot divide by 0." << std::endl;
        return false;
    }

    return true;
}

它采用“x/y”格式的有理数,例如 2/4。如果我输入正确,它工作正常。如果我输入 2p4,它会给出正确的错误(我遗漏了一个“/”),然后要求输入一个新号码。如果分母中为0,也会报错并要求新的数。

但是检查它是否是一个有效的数字似乎不起作用。如果我输入“a/4”,它将无限循环直到崩溃。我不知道为什么。检查调试器,它返回到 ins >> 语句,但不向用户询问任何内容。

我假设我的逻辑在某个地方是错误的。请注意,我对 C++ 还很陌生,仍在学习。我之前尝试过异常处理,但仍然没有正确地学习它,所以我重新选择了我更熟悉的东西。

谢谢!

4

2 回答 2

5

该问题的基本要点是,如果流的状态变坏,C++ 流格式化的提取运算符将停止工作,您必须重置状态才能使它们再次工作。

你还有其他问题。

首先,您的验证功能揭示了您缺乏经验:static_cast<int>(intval) == intval将永远是真实的并且什么也不确认。其次,您无法验证您确实已成功从流中提取值(这是导致无限循环的原因:您所做的就是一遍又一遍地验证失败。)

因此,当您提取值时,您应该验证一切正常,如下所示:

int num, den;
char symb;
// Remember to flush unfinished lines
std::cout << "Please enter a rational number: " << std::flush; 
if (std::cin >> num >> symb >> den)
    // you extracted an integer, a character and an integer succesfully
    // perhaps check that the character is '/' and denominator is non-zero
else
    // there was an error: what should we do?

“我们应该做什么”部分远非显而易见:您可以重置流并从中删除第一个违规字节,然后再试一次,如果您认为这是合理的(并且直观)。但是,由于数字太大,提取也可能失败,在这种情况下,这可能会导致奇怪的行为:考虑以下输入(在具有共同long大小的实现上):

3111111111111111111111111111111111111111111111111/3

如果你没有任何实际的规范,需要考虑的是面向行的输入:首先读取一行并尝试解析它;如果它看起来不正常,请忽略它并尝试下一个。

于 2012-09-01T02:53:22.120 回答
0

输入流的一个重要概念是它们不依赖于任何特定设备。也就是说,str >> rational应该在什么时候工作,什么时候str是控制台,什么时候是文件,什么时候是字符串,等等。所以提取器应该提取。它不应该提示,也不应该解释它不喜欢什么。所有这些事情都应该在调用提取器的代码中完成。

提取器中的验证很好,但是当检测到错误时,通常的报告方式是抛出异常。你可以让你的异常做任何你喜欢的事情,持有一个错误代码或一个文本字符串,或者对不同的错误有不同的异常。调用提取器以捕获异常并确定适当的响应是代码的责任。

static_cast<int>(num) != num

因为num是一个 int,所以static_cast不做任何事情。这个测试永远不会失败。

int main() { try { std::cout << "请输入一个有理数:"; 有理 r; 标准::cin >> r; } catch(...) { std::cout << "出了点问题\n"; } }

出于测试目的,我将重写此版本main()以使用 stringstream 作为输入源,并使用"2/4"or"2^4"或我想要测试的任何内容对其进行初始化;这样可以省去每次都输入的麻烦。

于 2012-09-01T11:43:08.503 回答