我有C ++类Question
来保存多选题和答案的文件questions.txt
中的数据:
更新 我更新了&运算符>>运算符重载我有一个:
文件 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>());
}
答案 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>>
会导致istream
到eof()
,将忽略您流式传输的最后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:空行标记问题结尾
想法是逐行阅读并分别解析每一行:
...
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中断的未完成的行将导致最后一个问题被忽略为失败。