我是否需要在清除istream之前取消它?

问题描述 投票:2回答:2

问题的简短版本

如果我正在读取这样的数据:

    while (in >> x) {
      hw.push_back(x);
    }
    // clear the stream so that input will work for the next student
    in.clear();

其中instd::istreamxdoublehwvector<double>。我是否不需要回退任何导致我退出while循环的尝试?否则,我下次从in中读取的内容是否会跳过一些数据?

完整问题

我正在尝试从包含一个字符串和一系列数字的文本文件中读取。我正在将这些数据处理成一个包含该数据成员属性的结构。

输入数据如下:Moore 75 85 77 59 0 85 75 89

此数据代表学生的姓名,他们的期末考试成绩,他们的期中考试成绩以及一些作业。

要读取此类数据,我需要以下内容:

#include <boost/format.hpp>
#include <iostream>
#include <string>

using std::istream;
using std::vector;
using std::cout;
using std::string;
using std::cin;

struct Student_info {
  std::string name;
  double midterm, final;
  std::vector<double> homework;
};

istream& read(istream&, Student_info&);
istream& read_hw(istream&, vector<double>&);

istream& read(istream& is, Student_info& s) {
  // Read and store th studen's name and midterm and final exam grades
  is >> s.name >> s.midterm >> s.final;

  read_hw(is, s.homework); // read and store all the student's homework grades
  return is;
}

istream& read_hw(istream& in, vector<double>& hw)
{
  if (in) {
    // get rid of previous contents
    hw.clear();

    // read homework grades
    double x;
    while (in >> x) {
      hw.push_back(x);
    }
    // clear the stream so that input will work for the next student
    in.clear();
  }
  return in;
}

简而言之,如果我的理解是正确的,我先读这个名字,然后读两次(期末考试和期中考试),然后再写很多作业。

我知道何时停止读以下内容的作业成绩vector<double>

    while (in >> x) {
      hw.push_back(x);
    }
    // clear the stream so that input will work for the next student
    in.clear();

这对我来说似乎非常明智,但是当我读取一系列数据行时,数据无法正确读取。

例如,使用以下输入:

Moo 100 100 100 100 100 100 100 100
Moore 75 85 77 59 0 85 75 89
Norman 57 78 73 66 78 70 88 89

我得到以下输出:

Name: Moo, Midterm: 100, Final: 100, Num HW: 6
Name: Moore, Midterm: 75, Final: 85, Num HW: 6
Name: orman, Midterm: 57, Final: 78, Num HW: 6

注意,名称是orman,而不是Norman。 N丢失。这不是错别字,这是我要理解的问题。

在我看来,我需要“软硬件”,尽管当我尝试从字面上称呼in.unget()并不能改善情况时。

下面是一些完整的输入数据以及驱动程序的完整源,如果有人想尝试一下:

完整输入数据

Moo 100 100 100 100 100 100 100 100
Moore 75 85 77 59 0 85 75 89
Norman 57 78 73 66 78 70 88 89
Olson 89 86 70 90 55 73 80 84
Peerson 47 70 82 73 50 87 73 71

Russel 72 87 88 54 55 82 69 87
Thomas 90 96 99 99 100 81 97 97
Vaughn 81 97 99 67 40 90 70 96
Westerly 43 98 96 79 100 82 97 96


Baker 67 72 73 40 0 78 55 70
Davis 77 70 82 65 70 77 83 81
Edwards 77 72 73 80 90 93 75 90
Franklin 47 70 82 73 50 87 73 71

Jones 77 82 83 50 10 88 65 80
Harris 97 90 92 95 100 87 93 91
Smith 87 92 93 60 0 98 75 90
Carpenter 47 90 92 73 100 87 93 91

Fail1 45 55 65 80 90 70 65 60
Fail2 55 55 65 50 55 60 65 60

完整的驱动程序源

#include <boost/format.hpp>
#include <iostream>
#include <string>

using std::istream;
using std::vector;
using std::cout;
using std::string;
using std::cin;

struct Student_info {
  std::string name;
  double midterm, final;
  std::vector<double> homework;
};

istream& read(istream&, Student_info&);
istream& read_hw(istream&, vector<double>&);

istream& read(istream& is, Student_info& s) {
  // Read and store th studen's name and midterm and final exam grades
  is >> s.name >> s.midterm >> s.final;

  read_hw(is, s.homework); // read and store all the student's homework grades
  return is;
}

istream& read_hw(istream& in, vector<double>& hw)
{
  if (in) {
    // get rid of previous contents
    hw.clear();

    // read homework grades
    double x;
    while (in >> x) {
      hw.push_back(x);
    }
    // clear the stream so that input will work for the next student
    in.clear();
  }
  return in;
}

int main() {

  vector<Student_info> students;
  Student_info record;
  string::size_type maxlen = 0;

  while (read(cin, record)) {
    // find length of longest name
    cout << boost::format("Name: %1%, Midterm: %2%, Final: %3%, Num HW: %4%\n") % record.name % record.midterm % record.final % record.homework.size();
    students.push_back(record);
  }

  return 0;
}

使用完整的输入数据,输出看起来像这样(注意许多名称输入有误):

Name: Moo, Midterm: 100, Final: 100, Num HW: 6
Name: Moore, Midterm: 75, Final: 85, Num HW: 6
Name: orman, Midterm: 57, Final: 78, Num HW: 6
Name: Olson, Midterm: 89, Final: 86, Num HW: 6
Name: rson, Midterm: 47, Final: 70, Num HW: 6
Name: Russel, Midterm: 72, Final: 87, Num HW: 6
Name: Thomas, Midterm: 90, Final: 96, Num HW: 6
Name: Vaughn, Midterm: 81, Final: 97, Num HW: 6
Name: Westerly, Midterm: 43, Final: 98, Num HW: 6
Name: ker, Midterm: 67, Final: 72, Num HW: 6
Name: vis, Midterm: 77, Final: 70, Num HW: 6
Name: wards, Midterm: 77, Final: 72, Num HW: 6
Name: ranklin, Midterm: 47, Final: 70, Num HW: 6
Name: Jones, Midterm: 77, Final: 82, Num HW: 6
Name: Harris, Midterm: 97, Final: 90, Num HW: 6
Name: Smith, Midterm: 87, Final: 92, Num HW: 6
Name: rpenter, Midterm: 47, Final: 90, Num HW: 6
Name: l1, Midterm: 45, Final: 55, Num HW: 6
Name: l2, Midterm: 55, Final: 55, Num HW: 6

更新1

我打破了以下while循环后尝试添加in.seekg(-1, in.cur);

    double x;
    while (in >> x) {
      hw.push_back(x);
    }

    // Going to try and get the istream back to where it was when I broke out of the while loop.
    in.seekg(-1, in.cur);

    // clear the stream so that input will work for the next student
    in.clear();

认为这会使我回到导致我退出while循环的任何原因。但是,仍然没有正确读取学生姓名

更新2

我看到这里有一个几乎相同的问题:

Why is istream.clear() removing part of my strings while reading doubles and strings?

但是,公认的解决方案不能解释为什么在这里做错了,它只是提供了一种解决方法。

更新3

我感谢所有解决方法,但是请考虑这个更集中的问题,为什么随后的每一行都没有在此或该位置缺少字母?只有一些行。

c++ string iostream cin
2个回答
1
投票

进行奇怪提取的原因是,字母ABCDEFINP可以出现在double中,而其他字母则不能。有关详细信息,请参见strtof spec

这是流I / O的根本问题,没有提前。该标准指定(大致而言)提取将继续进行,直到找到目标类型中不会出现的字符,然后尝试转换提取的内容。多年来,对规范进行了各种调整(包括将有效字符列表更改为两倍),但没有真正的解决方案。

没有规定在转换失败时放回字符,您将不得不使用其他提取方法。如另一个答案中所建议的那样:由于您的输入是面向行的(即换行符很重要),因此最好使用面向行的读取功能来读取行,然后解析行。您使用>>直到出错为止的方法无法在换行符处中断(该运算符将所有空白视为相同)。


1
投票

通常不需要在标准流上使用unget()。您遇到的问题是,您需要知道何时停止读取行。为此功能std::getline。您可以遍历每一行,将该行存储在std::istringstream中,然后从那里解析出记录:

std::istream& read_hw(std::istream& in, std::vector<double>& hw) {
  hw.clear();
  std::string line;
  if (std::getline(in, line)) {
    hw.assign(
      std::istream_iterator<double>{std::istringstream{line} >> std::skipws}, {}
    );
  }
  return in;
}
© www.soinside.com 2019 - 2024. All rights reserved.