Выходные данные файла правильно читают первый экземпляр результатов, неправильные следующие данные - PullRequest
0 голосов
/ 18 апреля 2020

поэтому у меня есть программа, которая использует классы Registration, UNIT, Result и Date для хранения и вывода некоторых деталей в новый файл. Порядок следующий: файл считывается в регистрацию, где хранятся некоторые значения, а затем остальная часть входных данных отправляется в массив объектов результатов. Когда он отправляется в результаты, он считывает в объект UNIT, затем в переменную 'mark' и, наконец, в объект date. После отправки в программу программа считывает день, месяц и год, и это окончательный результат. Мой выходной файл правильно выдает первый набор юнитов, однако после этого он все испортил. Может кто-нибудь объяснить, где я ошибся, я бы вообразил в мой входной синтаксис / лог c? Я опубликую только функции ввода и вывода. cpp для удобства чтения, поскольку все компилируется просто отлично, но если вам нужны файлы .h и. cpp всех классов, дайте мне знать. Заранее спасибо.

Результат. cpp Функция ввода и вывода:

void Registration::readFile(istream &input){

    long studentid1;
    unsigned semester1;


    input >> studentid1 >> semester1 >> count;


    SetStudentID(studentid1);
    SetSemester(semester1);


    for(unsigned i = 0; i < count; i++)
    input >> results[i];

}

void Registration::writeFile(ostream & os) const{

    os << "Student ID: " << GetStudentID() << '\n'
     << "Semester:   " << GetSemester() << '\n';

  for(unsigned i = 0; i < count; i++)
    os << results[i] << '\n';


}

UNIT. cpp Функции ввода и вывода:

void UNIT::SetUnit(istream &input){

  string nam;
  string idd;
  unsigned cred;

  getline(input,nam, '\n');
  getline(input,idd,'\n');

  input >> cred;

  SetName(nam);
  SetID(idd);
  SetCredits(cred);

}

void UNIT::GetUnit(ostream & os) const{

  os << "  Unit ID:  " << GetID() << '\n'
     << "  Unit Name: " << GetName() << '\n'
     << "  Credits: " << GetCredits() << '\n';

}

Результат. cpp функции ввода / вывода:

void Result::SetResult(istream &input){

    UNIT unitobj1;
    unsigned marks1;
    Date date1;

    input >> unitobj1 >> marks1 >> date1;
    SetUnit(unitobj1);
    SetMarks(marks1);
    SetDate(date1);

}

void Result::GetResult(ostream &os) const{

    os << GetUnit() << "  Marks: " << GetMarks() << '\n' << GetDate();

}

Дата. cpp ввод / вывод:

void Date::SetDate(istream &input){

    unsigned day1;
    string month1;
    unsigned year1;


    input >> day1 >> month1 >> year1;
    SetDay(day1);
    SetMonth(month1);
    SetYear(year1);


}

void Date::GetDate(ostream &os) const{

    os << "  Date: " << GetDay() << " " << GetMonth() << " " << GetYear() << '\n';

}

rinput.txt (входной файл):

102234 962 3
Data Structures and Abstractions 
ICT283
3 90 30 June 2016
Applied ICT Research Skills 
BSC250 
3 92 29 April 1993
Games Technology
ICT292
3 76 4 August 1998

routputnew.txt (выходной файл):

Student ID: 102234
Semester:   962
  Unit ID:  ICT283
  Unit Name: Data Structures and Abstractions 
  Credits: 3
  Marks: 90
  Date: 30 June 2016

  Unit ID:  Applied ICT Research Skills 
  Unit Name: 
  Credits: 0
  Marks: 90
  Date: 3395525844  7402640

  Unit ID:  3 92 29 April 1993
  Unit Name: BSC250 
  Credits: 0
  Marks: 90
  Date: 3395525844  7402640

Number of units = 3
Total credits     = 3473594268

1 Ответ

0 голосов
/ 18 апреля 2020

ОК, даже не видя ваш полный исходный код, я вам помогу.

У вас 2 основные проблемы

  1. Вы не проверяете, выполняется ли операция извлечения (>> ) не удалось или нет
  2. У вас есть проблема с символом конечной линии после извлечения без std::getline

Итак, что произойдет. Предположим, что следующая простая тестовая программа

#include <iostream>
#include <sstream>

std::istringstream testDataStream{ R"(1 2
string1
3 4
string2)" };

int main() {

    int i1{}, i2{}, i3{}, i4{};
    std::string s1{}, s2{};

    testDataStream >> i1 >> i2;
    std::getline(testDataStream, s1);
    testDataStream >> i3 >> i4;
    std::getline(testDataStream, s2);
    // more code . . . 
    return 0;
}

Сначала мы прочитаем i1 и i2 с правильной информацией. Оператор экстрактора читает до следующего пробела, но не использует '\ n' в конце строки. Он прочитал i1 и i2, пропуская пробел. Тогда остановись. Нет больше активности. Ничто не будет использовать '\ n'.

Если вы сейчас позвоните std::getline, оно начнет читать после значения 2 и найдет '\ n'. Затем он прекращает чтение и возвращает пустую строку. Итак, после вызова std::getline std::string s1 будет пустым, будет использоваться '\ n' и следующая позиция для чтения будет "string1".

И теперь мы попробуем testDataStream >> i3 >> i4;. Но нет 3 и 4. Помните: позиция для чтения все еще в "string1". Таким образом, операция извлечения не удастся и будет установлен failbit потока.

Но вы никогда не тестируете failbit, поэтому остальная часть программы молча завершается сбоем.

Вы можете и должны проверять результат каждой операции извлечения.

Это можно просто сделать с помощью

if (testDataStream >> i1 >> i2) {
    // Good. Continue standard operation
}
else {
    // Bad. Show error message and do error handling
}

Почему это работает? Цепная операция извлечения возвращает ссылку на поток в конце. И у потока есть перегруженный оператор bool. См. здесь и здесь .

Итак, для проверки состояния потока вы всегда можете написать просто if (someStream).

ОК, понятно , Но как мы можем избавиться от конечного '\ n' в конце строки. Для этого у нас есть функция ignore . См. Документацию по ссылке.

Чтобы исправить фрагмент кода избежания, нам нужно добавить ignore 2 раза:

#include <limits>
#include <iostream>
#include <sstream>

std::istringstream testDataStream{ R"(1 2
string1
3 4
string2)" };

int main() {

    int i1{}, i2{}, i3{}, i4{};
    std::string s1{}, s2{};

    testDataStream >> i1 >> i2;
    testDataStream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    std::getline(testDataStream, s1);
    testDataStream >> i3 >> i4;
    testDataStream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
    std::getline(testDataStream, s2);
    // more code . . .
    return 0;
}

И чтобы показать вам, как все это можно реализовать, Я подготовил для вас полный рабочий пример.

Пожалуйста, смотрите:

#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <iterator>
#include <limits>
#include <array>

// Function to check, if we have a valid month
constexpr std::array<std::string_view, 12> Months{ "January", "February", "March","April", "May", "June", "July","August", "September", "October","November", "December" };
bool isInValidMonth(const std::string& m) { return std::find(Months.begin(), Months.end(), m) == Months.end(); }

struct Unit {
    // Data for one semester/class unit for a student
    std::string id{};
    std::string name{};
    unsigned int credit{};
    unsigned int mark{};
    unsigned int dateDay{};
    std::string dateMonth{};
    unsigned int dateYear{};

    // This will check, if the data that we have read is ok, and if not, set the fail bit of the stream
    std::istream& sanityCheck(std::istream& is) {

        // Check for non plausible data. If there is unplausible data anywhere
        if (!is || credit > 500 || mark > 100 || dateDay == 0U || dateDay > 31 || dateYear < 1920 || dateYear > 2100 || isInValidMonth(dateMonth)) {

            // Set the streams failbit
            is.setstate(std::ios::failbit);
            std::cerr << "\n*** Error: Problem while reading unit data\n";
        }
        return is;
    }

    // Overide extractor. Get Unit data
    friend std::istream& operator >> (std::istream& is, Unit& u)
    {
        if (std::getline(std::getline(is, u.name), u.id) >> u.credit >> u.mark >> u.dateDay >> u.dateMonth >> u.dateYear)
            // Eat up the trailing '\n'
            is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
        return u.sanityCheck(is);
    }
    // Overwrite inserter. Write unit data
    friend std::ostream& operator << (std::ostream& os, const Unit& u) {
        return os << "\tUnit ID:\t" << u.id << "\n\tUnit name:\t" << u.name << "\n\tCredits:\t" << u.credit << "\n\tMarks:\t\t"
            << u.mark << "\n\tDate:\t\t" << u.dateDay << " " << u.dateMonth << " " << u.dateYear << "\n";
    }
};

struct Student {
    // Student data
    unsigned long id{};
    unsigned long semester{};
    std::vector<Unit> unit{};

    // Override extractor: Read Student Data
    friend std::istream& operator >> (std::istream& is, Student& s) {
        s.unit.clear();
        // Here will store the number of units that we need to read
        size_t numberOfUnits{};

        // Read, id and semester and numberOfUnits and check, if that was successfull
        if (is >> s.id >> s.semester >> numberOfUnits) {

            // Eat up the trailing '\n'
            is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

            // Read all units. Call the extractor operator n times, but only if everything OK
            for (size_t i = 0U; i < numberOfUnits && is; ++i) {

                // Read a unit and check, if OK
                if (Unit temp{}; is >> temp) {

                    // If OK, append the new unit to the student
                    s.unit.push_back(std::move(temp));
                }
            }
        }
        // Sanity check. We expect to have numberOfUnits
        if (!is && s.unit.size() != numberOfUnits) {
            // Set the streams failbit
            is.setstate(std::ios::failbit);
            std::cerr << "\n*** Error: Problem while reading student data\n";
        }
        return is;
    }
    // Override inserter: Write student data
    friend std::ostream& operator << (std::ostream& os, const Student& s) {
        os << "\n\nStudent ID:\t" << s.id << "\nSemester:\t" << s.semester << "\n\n";
        std::copy(s.unit.begin(), s.unit.end(), std::ostream_iterator<Unit>(os, "\n"));
        return os;
    }
};

struct Register {
    std::vector<Student> students{};

    // Override extractor: Read Student Data
    friend std::istream& operator >> (std::istream& is, Register& r) {
        for (Student s{}; is && is >> s; ) if (is) r.students.push_back(s);
        return is;
    }


    // Override inserter: Write complete Register
    friend std::ostream& operator << (std::ostream& os, const Register& r) {
        std::copy(r.students.begin(),r.students.end(), std::ostream_iterator<Student>(os, "\n"));
        return os;
    }

};


std::istringstream inputStream{ R"(102234 962 3
Data Structures and Abstractions 
ICT283
3 90 30 June 2016
Applied ICT Research Skills 
BSC250 
3 92 24 April 1993
Games Technology
ICT292
3 76 4 August 1998
222222 22 2
Data Structures and Abstractions 2
ICT222
3 90 30 June 2016
Applied ICT Research Skills 2
BSC222 
2 2 2 February 1993
)" };

int main() {

    Register r;
    inputStream >> r;
    std::cout << r;

    return 0;
}

Веселитесь. , .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...