学校の宿題のプログラムがうまくいかなくて困っています。

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
c++初心者

学校の宿題のプログラムがうまくいかなくて困っています。

#1

投稿記事 by c++初心者 » 15年前

初めまして。大学でC++を習い始めたばかりです。

課題で、exams.datというファイルの中にある以下のデータを読み取り。
abcdefabcdefabcdefab
1234567
abcdefabcdefabcdefab
9876543
abddefbbbdefcbcdefac
5554446
abcdefabcdefabcdef
4445556
abcdefabcdefabcdefabcd
3332221
abcdefghijklmnopqrst

一番上の列を模範解答として読み込み。
残りは生徒のIDナンバーと生徒の解答として読み込みます。生徒の数はわからないことになってます。

abcdefabcdefabcdefab ←模範解答
1234567 ←IDナンバー
abcdefabcdefabcdefab ←1234567の解答
9876543 ←IDナンバー
abddefbbbdefcbcdefac ←9876543の解答
5554446 ←IDナンバー
abcdefabcdefabcdef ←5554446の解答
4445556 ←IDナンバー
abcdefabcdefabcdefabcd ←3332221の解答
3332221 ←IDナンバー
abcdefghijklmnopqrst ←3332221の解答

その後、scores.datというファイルの中に。
生徒の解答と模範解答を照合して
もし正解ならスコアを一点ずつ加算し、最終的にはIDとスコアを表示。
もし生徒の解答数が少なすぎる(20個に満たない)場合、IDとTwo few answersと表示。
もし生徒の解答数が多すぎる(20個より多い)場合、IDとToo many answersと表示。
もし生徒の解答にa,b,c,d,e,f以外のものがひとつでも含まれる場合。IDとInvalid answersと表示。

結果的にはこのように表示されます。
1234567 20
9876543 15
5554446 Too few answers
4445556 Too many answers
3332221 Invalid answer

さて僕のプログラムなのですが、ファイルの中身の結果がこのようになってしまいます。
1234567 20
9876543 15
5554446 Too few answers
4445556 Too many answers
3332221 Invalid answer
3332221 5

僕が思うに、データの読み込みの部分のループがうまく行われていないのだと思うのですが……

以下が僕のプログラムです。

#include<fstream>
#include<string>
#include<iostream>
#include<iomanip>

using namespace std;

void readInput(string&, int [/url], string [/url], int&); // read keys, ids,
  //and
   // answers
void gradeExam(string, int [/url], string [/url], int); // grade each
    // student's
    // answer

int main()
{
string key; // the key of the exam
string answers[20]; // answers from students
int id[20]; // ids of students
ofstream outFile; // keeps the exam result
int stc;

readInput(key, id, answers, stc);
gradeExam(key, id, answers, stc);

return 0;
}

// read data from file
void readInput( /* out */ string& key,
/* out */ int id[/url],
/* out */ string ans[/url], int& stc)
{
ifstream inFile; // contains the exam data to be processed
// Ask for the input file name
// Open input file and read input data into key, id, and ans
inFile.open("exams.dat");

int loopcount = 0;
inFile >> key;
inFile >> id[loopcount];
while(inFile)
{
inFile >> ans[loopcount];
loopcount++;
inFile >> id[loopcount];
}
stc = loopcount;

inFile.close();
}

// Process each student's answer and write the grade to a file
// Check if the length of student's answer is the same as the key length
// Check if each answer lies between 'a' and 'f'
void gradeExam( /* in */ string key,
/* in */ int id[/url],
/* in */ string ans[/url], int stc)
{
ofstream outFile; // output file to contain the grade report
int posit = 1;
int score = 0;
int count = 0;

// Ask for the output file name and open output file
outFile.open("scores.dat");
outFile << left;
// grade each answer and write the result to the file
while(count < stc)
{
if(ans[count].length() < 20)
outFile << fixed << setw(10) << id[count]
<< setw(10) << "Too few answers" << endl;
else if(ans[count].length() > 20)
outFile << fixed << setw(10) << id[count]
<< setw(10) << "Too many answers" << endl;
else
{
posit = 1;
score = 0;
while(posit < 21)
{
if(key.substr(posit, 1) == ans[count].substr(posit, 1))
score += 1;
else if(ans[count].substr(posit, 1) != "a"
&& ans[count].substr(posit, 1) != "b"
&& ans[count].substr(posit, 1) != "c"
&& ans[count].substr(posit, 1) != "d"
&& ans[count].substr(posit, 1) != "e"
&& ans[count].substr(posit, 1) != "f")
{
outFile << fixed << setw(10) << id[count]
<< setw(10) << "Invalid answer" << endl;
break;
}
posit++;
}
outFile << fixed << setw(10) << id[count]
<< setw(10) << score << endl;
}
count++;
}

outFile.close();
}

mainの部分は最初から先生が書いてありました。
どう直せばよろしいのでしょうか?
よろしくお願いします。

c++初心者

Re:学校の宿題のプログラムがうまくいかなくて困っています。

#2

投稿記事 by c++初心者 » 15年前

訂正です
例の部分
abcdefabcdefabcdefab ←模範解答
1234567 ←IDナンバー
abcdefabcdefabcdefab ←1234567の解答
9876543 ←IDナンバー
abddefbbbdefcbcdefac ←9876543の解答
5554446 ←IDナンバー
abcdefabcdefabcdef ←5554446の解答
4445556 ←IDナンバー
abcdefabcdefabcdefabcd ←3332221の解答(4445556の解答でした。)
3332221 ←IDナンバー
abcdefghijklmnopqrst ←3332221の解答

Ma

Re:学校の宿題のプログラムがうまくいかなくて困っています。

#3

投稿記事 by Ma » 15年前

int loopcount = 0;
inFile >> key;
inFile >> id[loopcount];
while(inFile)
{
inFile >> ans[loopcount];
loopcount++;
inFile >> id[loopcount];
}



inFile >> key;
int loopcount = 0;
while(inFile){
inFile >> id[loopcount];
inFile >> ans[loopcount];
loopcount++
}


これでどうですか?

あと、気になる点といえば、loopcount が19を超えた場合の例外処理をしなくていいのか。
*生徒の数がわからないのではなかったのか?なのに、先生は main で 20 と限定してしまっている・・・・。
今回の質問のエラーはたぶんこれでなるとおもうので、他の部分(ファイル名入力など)はスルーしますね。 画像

c++初心者

Re:学校の宿題のプログラムがうまくいかなくて困っています。

#4

投稿記事 by c++初心者 » 15年前

Ma さん返信ありがとうございます。

試してみた結果、
1234567 20
9876543 15
5554446 Too few answers
4445556 Too many answers
3332221 Invalid answer
3332221 5
32767 Too few answers
というアウトプットになりました。

補足:
先生が生徒の数は多くても20人くらい、ということでid[20]というのはすでに用意されていました。

もう少し考えてみます。
ありがとうございました。

Ma

Re:学校の宿題のプログラムがうまくいかなくて困っています。

#5

投稿記事 by Ma » 15年前

先ほどの修正に以下の修正を加えてください。
(すいません、さきほどは動作チェックしませんでしたが、今回はチェックしましたので確実です。)

修正1(if が増えた)
inFile >> key;
int loopcount = 0;
while(inFile){
inFile >> id[loopcount];
inFile >> ans[loopcount];
if(inFile)
loopcount++;
}

修正2(Invalid 判定)
posit = 1;
score = 0;
bool invalidFlag = false;
while(posit < 21)
{
if(key.substr(posit, 1) == ans[count].substr(posit, 1))
score += 1;
else if(ans[count].substr(posit, 1) != "a"
&& ans[count].substr(posit, 1) != "b"
&& ans[count].substr(posit, 1) != "c"
&& ans[count].substr(posit, 1) != "d"
&& ans[count].substr(posit, 1) != "e"
&& ans[count].substr(posit, 1) != "f")
{
outFile << fixed << setw(10) << id[count]
<< setw(10) << "Invalid answer" << endl;
invalidFlag = true;
break;
}
posit++;
}
if(!invalidFlag)
outFile << fixed << setw(10) << id[count]
<< setw(10) << score << endl; 画像

c++初心者

Re:学校の宿題のプログラムがうまくいかなくて困っています。

#6

投稿記事 by c++初心者 » 15年前

Maさん

できました!
ありがとうございました。

インプット時のループだけに問題があったと思ったんですけれど、アウトプットの方にもあったのですか?
できれば詳しく教えていただけないでしょうか?

Ma

Re:学校の宿題のプログラムがうまくいかなくて困っています。

#7

投稿記事 by Ma » 15年前

・インプット側の問題
実は、インプット側には問題がありませんでした。
最初、見たときは、IDの読み込み回数と解答の読み込み回数があっていなかったのでおかしいと思いこんでいたんですが、
そうではなく、最初の状態でも正常に動作します。

私の方法では、生徒単位(IDと解答)でよみこんで、(どちらかで)失敗か最後まで読み込めばカウンターをまわさず、終了。
あなたの最初の方法では、IDを先に読み込み、ループ内で解答→IDの順で読み込む事によって、
while文はID読み込み時にエラーが起きたかどうかを条件にしています。

どちらかというと、最初のままのほうが理想的だったかもしれません。



・アウトプット側の問題
本来、else{}スコープ内では、(これまで文字数が多すぎる/少なすぎるをifで判別したので)取り扱えない文字が入っていた場合と、正常な場合のみがあるんですよね?

そこで、取り扱えない文字があれば、エラーの表示、そうでなければ正常の表示をしなければなりません。
ただ、修正前のコードは、「そうでなければ」 の判定がありませんでした。

取り扱えない文字があればエラー表示、次に正常の表示 となっていたのです。

c++初心者

Re:学校の宿題のプログラムがうまくいかなくて困っています。

#8

投稿記事 by c++初心者 » 15年前

丁寧に教えていただき、本当にありがとうございました。

たいちう

Re:学校の宿題のプログラムがうまくいかなくて困っています。

#9

投稿記事 by たいちう » 15年前

冗長な所が多かったので作ってみました。ご参考までに。
(ファイル出力は省略)
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>

using namespace std;

string check(const string &answer, const string &str) {
    if (answer.size() > str.size())
        return "Too few answers";
    else if (answer.size() < str.size())
        return "Too many answers";

    int score = 0;
    for (size_t i = 0; i < answer.size(); i++) {
        if (str < 'a' || 'f' < str)
            return "Invalid answers";
        score += (str == answer);
    }
    stringstream ss;
    ss << score;
    return ss.str();
}

int main() {
    ifstream ifs("exams.dat");
    string answer;
    ifs >> answer;
    for (;;) {
        int id;
        string buf;
        ifs >> id;
        if (ifs.eof()) break;
        ifs >> buf;
        if (ifs.eof()) break;
        cout << id << " " << check(answer, buf) << endl;
    }
    return 0;
}

c++初心者

Re:学校の宿題のプログラムがうまくいかなくて困っています。

#10

投稿記事 by c++初心者 » 15年前

たいちうさん

もっと簡単にできるんですね。
ありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る