如何使用输入流重载将项目插入class中的映射成员?
How to use input stream overloading to insert item to map member in class?
我有 C++ class Question
来保存来自文件 questions.txt
的多项选择题和答案的数据:
更新:
我已经更新了 &operator>> 运算符重载我有一个:
- 只插入2道选择题中的第一道选择题"read the first Question "
文件中的数据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>());
}
第一次改进
当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 开始阅读每个问题。不幸的是,在 int
上使用 operator>>
将不允许我们检测到最后的答案:阅读尝试将失败,因为下一个问题的非数字文本开始。
格式问题
您使用的格式有一些歧义:
- 空行是否是强制性的并标记答案的开始和结束?在这种情况下,最后一个问题是无效的:缺少答案的结尾)。
- 或者空白行是可选的并且必须被忽略吗?在这种情况下,第一个字符确定它是新问题(非数字)的开始还是新答案(数字)
- 或者是否总是期望一个问题恰好有 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 打断的未完成行将导致最后一个问题被忽略为失败。
您的代码有两个问题:跳过第一个答案并通读文件末尾。
在这对循环中:
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
。
我有 C++ class Question
来保存来自文件 questions.txt
的多项选择题和答案的数据:
更新: 我已经更新了 &operator>> 运算符重载我有一个:
- 只插入2道选择题中的第一道选择题"read the first Question "
文件中的数据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>());
}
第一次改进
当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 开始阅读每个问题。不幸的是,在 int
上使用 operator>>
将不允许我们检测到最后的答案:阅读尝试将失败,因为下一个问题的非数字文本开始。
格式问题
您使用的格式有一些歧义:
- 空行是否是强制性的并标记答案的开始和结束?在这种情况下,最后一个问题是无效的:缺少答案的结尾)。
- 或者空白行是可选的并且必须被忽略吗?在这种情况下,第一个字符确定它是新问题(非数字)的开始还是新答案(数字)
- 或者是否总是期望一个问题恰好有 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 打断的未完成行将导致最后一个问题被忽略为失败。
您的代码有两个问题:跳过第一个答案并通读文件末尾。
在这对循环中:
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
。