【问题标题】:File output reads first instance of results correctly, following inputs are incorrect文件输出正确读取结果的第一个实例,以下输入不正确
【发布时间】:2020-07-31 18:00:33
【问题描述】:

所以我有一个程序,它利用 Registration、UNIT、Result 和 Date 类在新文件中存储和输出一些细节。顺序如下:将文件读入寄存器,其中存储一些值,然后将其余输入发送到结果对象数组。当它被发送到结果时,它会读入一个 UNIT 对象,然后读入一个变量“mark”,最后读入一个日期对象。一旦发送到日期,程序就会读取日期月份和年份,这就是最终的输入。我的输出文件正确地生成了第一组单位,但是,在那之后一切都搞砸了。有人可以解释我在输入语法/逻辑中想象的哪里出错了吗?为了便于阅读,我只会发布 .cpp 输入和输出函数,因为一切都编译得很好,但是如果您确实需要所有类的 .h 和 .cpp 文件,请告诉我。提前致谢。

Result.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';

}

Result.cpp i/o 函数:

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 i/o:

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

【问题讨论】:

  • 可能你的一个输入调用失败了,那么 Steam 将不会读取任何其他内容。您需要在每次读取后检查流状态。如果合适的话,你可以使用 clear 和 ignore 让 steam 回到可读状态
  • 我想帮助你。但是我需要完整的代码,这样我就可以调试它并就如何改进提出建议。把它放在某个地方并发布链接。

标签: c++ file class input


【解决方案1】:

好的,即使没有看到您的完整源代码,我也会帮助您。

你有两个主要问题

  1. 您不检查提取操作 (>>) 是否失败
  2. 在没有std::getline 的情况下提取后,您的endline 字符有问题

那么,会发生什么。假设如下简单的测试程序

#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 &gt;&gt; i3 &gt;&gt; i4;。但是没有 3 和 4。记住:要读取的位置仍然在“string1”。因此提取操作将失败,并且将设置流的failbit

但是您从不测试failbit,因此程序的其余部分会静默失败。

您可以并且应该检查每个提取操作的结果。

这可以简单地完成

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

为什么会这样?链式提取操作在最后返回对流的引用。并且流有一个重载的布尔运算符。请参阅herehere

因此,要测试流的状态,您总是可以简单地编写 if (someStream)

好的,明白了。但是我们怎样才能摆脱字符串末尾的尾随'\n'。为此,我们有函数ignore。请参阅链接中的文档。

要修复avoe代码sn-p我们需要添加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;
}

玩得开心。 . .

【讨论】:

    猜你喜欢
    • 2021-09-01
    • 2015-07-02
    • 2013-07-02
    • 1970-01-01
    • 2016-02-26
    • 2019-06-16
    • 2012-01-24
    • 2019-07-18
    • 2018-09-06
    相关资源
    最近更新 更多