为什么在 C++ 中读取 Doubles 时 ifstream 会改变其状态?

Why does ifstream change its state when reading Doubles in C++?

提问人:Adam Smith 提问时间:6/21/2023 更新时间:6/21/2023 访问量:60

问:

我正在编写一个方法,用于从格式如下的文件中读取项目:

[字符串][整数][双人间]

[字符串][整数][双人间]

我正在从文件中读取并使用以下循环将每行存储为用户定义类型中的向量:

 while(ifs.good()){
    Car car;
    ifs >> car.carName;
    ifs >> car.yearBuilt;
    if(!ifs.good()) throw std::invalid_argument("Year must be a integer");
    ifs >> car.price; 
    if(!ifs.good()) 
      throw std::invalid_argument("Price must be a double");
    to_return.push_back(car);
  }

但是,尽管格式正确的文件具有正确的字段,但该函数仍会抛出错误,指出“价格必须是双倍”。这是我使用的测试文件:

梅赛德斯 1998 2364.12

奥迪 2012 32645.79

如果我删除 if 语句,文件将被正确解析,向量将按预期工作。我对为什么会抛出这个错误感到困惑,以及为什么文件状态在解析双精度后处于 fail() 状态时会自行修复。

C++ 输入 文件- IO

评论


答:

2赞 Jan Schultke 6/21/2023 #1

.good()很多时候检查流是错误的,因为它还会检查是否设置了标志,即是否已经通过以前的某些操作到达了文件的末尾。eof

通常,读取 a 不会设置标志,因为文本文件应该以 结尾,因此我们在读取时会找到一个换行符而不是 EOF。 但是,并非每个文本文件都正确终止(显然包括您的文本文件),我们在阅读时会遇到 EOF。doubleeof\n2364.12

理想情况下,您的代码应该能够处理不以换行符结尾的文本文件。 如果我们正确地进行错误处理,这是可能的。 大多数情况下,您应该:

  1. 尝试执行一些 I/O 操作
  2. 检查是否设置以查看是否成功.fail()

C++ 流可以在上下文中转换为 ,这隐式检查标志。 因此,我们可以写成:bool.fail()

 // while(true) because at this point, we have done nothing yet
 // and don't have any loop condition we can check.
 // do-while loops also don't make sense when files are properly terminated.
 while(true) {
    Car car;
    if (!(ifs >> car.carName)) {
        // no more cars in stream,
        // we end the loop gracefully
        break;
    }
    if (!(ifs >> car.yearBuilt)) {
        // we've failed reading in the middle of the car's information
        // this is not okay
        throw std::invalid_argument("Failed to read year built");
    }
    if (!(ifs >> car.price)) {
        throw std::invalid_argument("Failed to read car price");
    }
    to_return.push_back(car);
}
0赞 Jerry Coffin 6/21/2023 #2

至少在我看来,如果你想从文件中读取对象,最好为对象重载:caroperator>>car

struct car {
    std::string carName;
    int yearBuilt;
    double price;

    friend std::istream &operator>>(std::istream &is, car &c) { 
        car temp;
        is >> temp.carName >> temp.yearBuilt >> temp.price;
        if (is) 
            c = temp;
        return is;
    }
};

有了这个,你可以用这样的代码读取汽车对象的向量:

std::vector<car> cars{ std::istream_iterator<car>(ifs), {}};