Разве мне не нужно отключать istream, прежде чем я его очищу? - PullRequest
2 голосов
/ 19 января 2020

Краткая версия вопроса

Если я читаю данные, например, так:

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

, где in - это std::istream, x - это double, а hw - это vector<double>. Разве мне не нужно было откладывать обратно любую попытку чтения, которая вызвала у меня всплеск, пока я oop? Иначе, не будет ли мое следующее чтение из 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 , НЕ Норман. 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

Я пытался добавить in.seekg(-1, in.cur); после выхода из следующего, пока l oop:

    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();

Думая, что это займет у меня вернуться к тому, что заставило меня вспыхнуть в то время как я oop. Но все же имена учеников по-прежнему не читаются правильно

Обновление 2

Я вижу, что здесь почти идентичный вопрос:

Почему istream.clear () удаляет часть моих строк при чтении парных и строковых значений?

Однако принятое решение не объясняет , почему то, что здесь делается, неправильно - он просто предлагает обходной путь.

Обновление 3

Я ценю все обходные пути, но рассмотрим этот более сфокусированный вопрос, почему не в каждой последующей строке отсутствует буква здесь или там? Только некоторые строки.

Ответы [ 2 ]

3 голосов
/ 20 января 2020

Причина странного извлечения состоит в том, что буквы ABCDEFINP могут встречаться в double, а другие - нет. Подробнее см. strtof spe c.

Это фундаментальная проблема с потоковым вводом-выводом без предварительного просмотра. Стандарт определяет (грубо говоря), что извлечение продолжается до тех пор, пока не будет найден символ, который не может встречаться в типе назначения, а затем попытается преобразовать то, что было извлечено. За прошедшие годы в спец. c были внесены различные изменения (включая изменение списка допустимых символов для двойного), но реального решения не было.

Нет никаких условий для возврата символов в случае сбоя преобразования. должны использовать другой метод извлечения. Как было предложено в другом ответе: поскольку ваш ввод ориентирован на строки (т. Е. Переводы строки значимы), было бы хорошо использовать функцию чтения с ориентацией на строки, чтобы прочитать строку, а затем проанализировать строку. Ваш метод использования >> до тех пор, пока ошибка не будет прервана на новых строках (этот оператор обрабатывает все пробелы как идентичные).

1 голос
/ 19 января 2020

Обычно нет необходимости использовать 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;
}
...