2016-11-12 8 views
1

У меня есть C++ класс Question для хранения данных из файла questions.txt многовариантных вопросов и ответов:Как использовать перегрузку потока ввода для вставки элемента для сопоставления элемента в классе?

обновление: Я обновил & оператор >> перегрузки оператора У меня есть один:

  1. он вставляет только первый вопрос с множественным выбором из двух вопросов с множественным выбором «прочитайте первый вопрос»

Данные в файле 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>> заставит 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() (мы, re done).

Итого:

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.

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

Вы могли бы рассмотреть аналогичный подход, для чтения на каждый вопрос, начиная с его идентификатором. К сожалению, использование operator>> на int не позволит нам обнаружить последний ответ: попытка чтения завершится неудачей с началом нечислового текста для следующего вопроса.

Проблема с форматом

формата, который вы используете имеют некоторые неясности:

  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