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

kx7yvsdv  于 2023-06-25  发布在  其他
关注(0)|答案(2)|浏览(198)

我正在编写一个方法,从格式如下的文件中读取项目:
[String] [Integer] [Double]
[String] [Integer] [Double]
我从文件中阅读,并使用以下循环将每行存储到一个vector中,作为用户定义的类型:

  1. while(ifs.good()){
  2. Car car;
  3. ifs >> car.carName;
  4. ifs >> car.yearBuilt;
  5. if(!ifs.good()) throw std::invalid_argument("Year must be a integer");
  6. ifs >> car.price;
  7. if(!ifs.good())
  8. throw std::invalid_argument("Price must be a double");
  9. to_return.push_back(car);
  10. }

然而,尽管文件格式正确,字段正确,函数仍不断抛出错误,说“价格必须是双精度”。下面是我使用的一个测试文件:
奔驰1998 2364.12
奥迪2012 32645.79
如果我删除if语句,文件将被正确解析,向量将按预期工作。我不明白为什么会抛出这个错误,以及为什么在解析double后文件状态处于fail()状态时会自行修复。

34gzjxbg

34gzjxbg1#

.good()在很多时候检查流是错误的,因为它还检查eof标志是否被设置,即文件的结尾是否已被某些先前的操作到达。
通常,阅读double不会设置eof标志,因为文本文件应该以\n结尾,所以我们在读取时会找到换行符而不是EOF。然而,并不是每个文本文件都被正确地终止(包括你的,显然),我们在阅读2364.12时遇到了EOF。
理想情况下,您的代码应该能够处理不以换行符结尾的文本文件。如果我们正确处理错误,这是可能的。大多数时候,你应该:
1.尝试执行某些I/O操作
1.检查.fail()是否设置成功
C++流可以上下文转换为bool,这隐式检查.fail()标志。因此,我们可以写:

  1. // while(true) because at this point, we have done nothing yet
  2. // and don't have any loop condition we can check.
  3. // do-while loops also don't make sense when files are properly terminated.
  4. while(true) {
  5. Car car;
  6. if (!(ifs >> car.carName)) {
  7. // no more cars in stream,
  8. // we end the loop gracefully
  9. break;
  10. }
  11. if (!(ifs >> car.yearBuilt)) {
  12. // we've failed reading in the middle of the car's information
  13. // this is not okay
  14. throw std::invalid_argument("Failed to read year built");
  15. }
  16. if (!(ifs >> car.price)) {
  17. throw std::invalid_argument("Failed to read car price");
  18. }
  19. to_return.push_back(car);
  20. }
展开查看全部
nkcskrwz

nkcskrwz2#

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

  1. struct car {
  2. std::string carName;
  3. int yearBuilt;
  4. double price;
  5. friend std::istream &operator>>(std::istream &is, car &c) {
  6. car temp;
  7. is >> temp.carName >> temp.yearBuilt >> temp.price;
  8. if (is)
  9. c = temp;
  10. return is;
  11. }
  12. };

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

  1. std::vector<car> cars{ std::istream_iterator<car>(ifs), {}};
展开查看全部

相关问题