2016-11-12 56 views
1

我有C++ Question類從選擇題和答案文件questions.txt保存數據:如何使用輸入流重載將項目插入到地圖成員中?

更新: 我已經更新了&操作>>運算符重載我有一個:

  1. 它只插入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

有兩個問題與您的代碼:跳過第一個答案,並通過文件的末尾讀取。

在這對循環:

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; 
} 

現在也有點不完整的。如果你沒有閱讀answerscorrectIndex匹配的任何內容,或許你想指出錯誤?在這種情況下,您也可以設置ios::failbit

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。不幸的是,在int上使用operator>>將不允許我們檢測到最後一個答案:讀取嘗試將失敗,下一個問題將以非數字文本開始。

與格式

您使用的格式問題具有一定的模糊性:

  1. 空白行強制性的,標誌着開始和答案的結束?在這種情況下,最後一個問題是無效的:答案的結尾缺失)。
  2. 或者空行是可選的,必須忽略?在這種情況下,第一個字符確定它是否是新問題的開始(非數字)或者是否是新答案(數字)
  3. 或者是否總是預期問題的確有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中斷會導致最後一個問題被忽略,因爲失敗。

+0

嗨@Christophe我有更新的問題,你可以看看嗎? – NinjaDeveloper

+0

@NinjaDeveloper糟糕對不起,我有點快:第一個循環結束或者如果到達線的EOF不是空的。在後一種情況下(例如,您的答案爲「1. C」),您需要首先處理行,然後再獲取新行。我已經相應更新。 – Christophe

相關問題