如何使用输入流重载将项插入到类中的映射成员?

时间:2016-11-12 22:44:55

标签: c++ operator-overloading iostream

我有C ++类Question来保存多选题和答案的文件questions.txt中的数据:

更新  我更新了&运算符>>运算符重载我有一个:

  1. 它只插入2个选择题的第一个多项选择问题“阅读第一个问题”
  2. 文件 questions.txt

    中的数据
    A programming language is used in this Course? 3
    
    1. C
    2. Pascal
    3. C++
    4. Assembly
    
    What compiler can you use to compile the programs in this Course? 4
    
    1. Dev-C++
    2. Borland C++Builder
    3. Microsoft Visual C++
    4. All of the above
    

    我正在尝试将多个答案插入地图中。我只是想问一下如何重载operator>>来迭代多个答案以将它们插入到地图中:

    #include <string>
    #include <iostream>
    #include <sstream>
    #include <map>
    using namespace std;
    class Question
    {
        string question;
        int correctIndex;
        map<int,string> answers;
    
    
        friend std::istream &operator>>(std::istream &is, Question &q) {
            getline(is, q.question, '?');   // stops at '?'
            is>> q.correctIndex;
            string line;
            while (getline(is, line) && line.empty())  // skip leading blank lines
                ;
            while (getline(is,line) && !line.empty())  // read until blank line
            {
                int id;
                string ans;
                char pt;
                stringstream sst(line);    // parse the line;
                sst>>id>>pt;               // take number and the following point
                if (!sst  || id==0 || pt!='.')
                    cout << "parsing error on: "<<line<<endl;
                else {
                    getline (sst, ans);
                    q.answers[id] = ans;
                }
            }
           return is;
        }
    };
    
    int main()
    {
        ifstream readFile("questions.txt");//file stream
        vector<Question> questions((istream_iterator<Question>(readFile)), istream_iterator<Question>());
    }
    

2 个答案:

答案 0 :(得分:2)

您的代码存在两个问题:跳过第一个答案并阅读文件末尾。

在这对循环中:

while (getline(is, line) && line.empty())  // skip leading blank lines
    ;
while (getline(is,line) && !line.empty())  // read until blank line
{

第一个非空line将终止第一个循环,但是然后立即再次调用getline()而不实际读取任何内容。这会跳过第一个答案选项。您希望确保第一次实际上不致电getline()。有点像...

// skip leading blank lines
while (getline(is, line) && line.empty()) {
    ;
}
for (; is && !line.empty(); getline(is, line)) {
    // ...
}

但是第二个也是更大的问题是,如果你仔细阅读文件的末尾(正如你的代码现在所做的那样),那么最后operator>>会导致istreameof(),将忽略您流式传输的最后Question。这很棘手,因为你有一个可变长度的输入流 - 我们不知道我们什么时候用完了输入,直到我们实际用完了输入。

值得庆幸的是,我们可以做得更简单一些。首先,我们不会读取输入的结尾来触发错误,而是使用第一次读取使我们停止:

friend std::istream &operator>>(std::istream &is, Question &q) {
    if (!getline(is, q.question, '?')) {   // stops at '?'
        return is; 
    }

这样,如果我们提前点击EOF,我们会提早停止。对于其余部分,我们可以使用skipws()大大简化阅读。我们可以让operator>>通过向前跳过来为我们执行此操作,而不是手动循环空行(这很难做到,根据您的初始错误)。

当我们没有阅读的内容时,我们只是退出错误标记 - 因为我们不想要fail()(如果我们尝试阅读下一个索引,那么它实际上是下一个问题)或eof()(我们已完成)已触发。

共:

friend std::istream &operator>>(std::istream &is, Question &q) {
    if (!getline(is, q.question, '?')) {   // stops at '?'
        return is; 
    }

    is >> q.correctIndex;

    int id; 
    char pt; 
    string ans;

    is >> skipws;
    while (is >> id >> pt && getline(is, ans)) {
        q.answers[id] = ans;
    }

    // keep the bad bit, clear the rest
    is.clear(is.rdstate() & ios::badbit);
    return is; 
}   

现在这也有点不完整。如果您没有阅读answers匹配correctIndex的任何内容,也许您想表明错误?在这种情况下,您也可以设置ios::failbit

答案 1 :(得分:0)

第一项改进

operator>>用于字符串时,它会在第一个空白分隔符处停止。因此,为了正确阅读您应该考虑的问题:

friend std::istream &operator>>(std::istream &is, Question &q) {
    getline(is, q.question, '?');   // stops at '?'
    return is>> q.correctIndex;
    ... // to do:  for the answers (see below)
}

您可以考虑采用类似的方法,从ID开始阅读每个问题。不幸的是,在operator>>上使用int将无法让我们检测到最后一个答案:读取尝试将失败并启动下一个问题的非数字文本。

格式问题

您使用的格式有些含糊不清:

  1. 是否必填空白行并标记答案的开头和结尾?在这种情况下,最后一个问题无效:缺少答案的结尾。
  2. 或者空白行是否可选,必须忽略?在这种情况下,第一个字符确定它是否是新问题的开头(非数字)或者它是否为新答案(数字)
  3. 或者一直期望问题有4个答案吗?
  4. 备选方案1:空行标记问题结尾

    想法是逐行阅读并分别解析每一行:

        ...    
        string line; 
        while (getline(is, line) && line.empty())  // skip leading blank lines
           ;  
        do                                        // read until blank line 
        {
            int id;
            string ans; 
            char pt; 
            streamstring sst(line);    // parse the line;
            sst>>id>>pt;               // take number and the following point
            if (!sst  || id==0 || pt!='.') 
               cout << "parsing error on: "<<line<<endl; 
            else {
               getline (sst, ans); 
               q.answers[id] = ans; 
            }
            getline(is,line);
        } while (getline(is, line) && !line.empty());
    

    注意: 根据假设:缺少的答案结束空行,将导致最后一个问题的读数失败。理想情况下,您会发出错误消息以澄清(例如,意外的文件结束)。使用空白行修正输入文件将起作用(空行以新行结束)。

    备选方案2:测试第一行字符以查看它是否仍然是下一个答案

    另一个替代方法是查看要读取的第一个字符,以便检查它是否是答案(以数字开头),空行(要跳过),如果不是,则退出循环。

        ...    
        string line, ans;
        int c, id; 
        char pt; 
        while ((c = is.peek())!=EOF && (isdigit(c) || c=='\n')) {  // test first char without reading it
            getline(is, line); 
            if (!line.empty()) {
                stringstream sst(line);  
                ...    // parse the line as above
                }
            }
        }
    

    使用此选项,要求是答案以换行符结束(即尾随'\ n')。用EOF中断的未完成的行将导致最后一个问题被忽略为失败。