【问题标题】:Shouldn't I need to unget an istream before I clear it?在清除 istream 之前,我不需要取消它吗?
【发布时间】:2020-05-05 18:30:05
【问题描述】:

问题的简短版本

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

    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&lt;double&gt; 的家庭作业成绩,其中包含以下内容:

    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

我很欣赏所有的变通方法,但考虑一下这个更集中的问题,为什么不是每一行都在此处或那里缺少一个字母?只有一些行。

【问题讨论】:

  • 读/写将文件指针向前推。
  • 所以当我因为读取的不是双精度而退出while循环时,如何将文件指针移回以便读取下一行数据?
  • 谢谢迈克尔。它部分回答了我的问题。但我仍然有点困惑,这是否是正确的做法。我在整个网络代码中都看到了与我在此处发布的内容相似的内容——当 istream 返回 False 时会中断的 while 循环,然后在 istream 上调用 clear。但是我没有看到人们移动 istream 的指针。
  • 一般情况下,当您确切地知道根据之前的读取会找到什么时,您就会从流中读取。如果你需要某种解析,你会首先在内存中读取它。

标签: c++ string iostream cin


【解决方案1】:

奇怪提取的原因是字母ABCDEFINP 可以出现在double 中,而其他字母不能。详情请见strtof spec

这是没有前瞻的流 I/O 的一个基本问题。该标准规定(粗略地说)提取继续进行,直到找到目标类型中不能出现的字符,然后尝试转换提取的内容。多年来,对规范进行了各种调整(包括将有效字符列表更改为双精度),但没有真正的解决方案。

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

【讨论】:

  • 您能否提供一个链接来解释这些字符在double 中的含义?
  • @EMiller OK,添加了 strtof 规范的链接。 0pABCDEF 来自 base 16 格式,可能有 INFNAN
  • 啊哈。谢谢你。
【解决方案2】:

通常不需要在标准流上使用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;
}

【讨论】:

    猜你喜欢
    • 2012-06-13
    • 2021-10-23
    • 2020-12-18
    • 2020-11-03
    • 1970-01-01
    • 1970-01-01
    • 2018-05-16
    • 2020-10-24
    • 1970-01-01
    相关资源
    最近更新 更多