C言語

【C言語】クイズゲーム作成(7)問題をファイルから読み込む

クイズゲーム作成(7)問題をファイルから読み込む

こんにちは、コンジ(@pippi_kon)です。

この記事では『C言語でのクイズゲームの作り方』をご紹介しています。

前回は、プログラムを実行するたびに問題をランダムで出題する方法をご紹介しました。

出題にランダム性を取り入れることでクイズっぽくなりましたね!

でも、現状のプログラムだとランダムといってもまだ3問しかありません。

よりクイズっぽくするためには、もっと問題を増やしてその中から10問出題するみたいな感じにしたいですよね。

ただ、単純に問題を増やすだけでは、プログラムの中がクイズの問題だらけになってしまいます。

そこで今回は、クイズの数を増やす前にクイズの問題を外部ファイル(テキストファイル)から読み込む方法をご紹介します。

OS:Windows 10(x64)
開発環境:Visual Studio 2019(Community)

今回の目標

今回作成するプログラムの出力結果です。

[第1問]
世界三大珍味はどれ?
1:イカスミ 2:キャビア 3:チーズ
>>> 1
不正解…
正解は 2:キャビア です。

[第2問]
リンゴは英語で何と言う?
1:apple 2:orange 3:banana
>>> 1
正解!

[第3問]
大正->昭和->○○->令和
○○に入る年号は?
1:慶応 2:明治 3:平成
>>> 3
正解!

–クイズ終了–
[第1問]…×
[第2問]…○
[第3問]…○
あなたは 3 問中 2 問正解でした (正答率:66 %)

出力結果は前回と変わっていません。

今回、クイズ問題が書かれた外部ファイルを読み込む処理を行っています。

ファイル読み込み処理に失敗した場合、エラーメッセージを出力するようにしています。

メッセージの内容はプログラムの解説と合わせて行います。

 

クイズ問題ファイルの記載形式

クイズ問題ファイルには以下の内容をリストアップします。

  • 問題文
  • 選択肢1~3
  • 正解の番号

ただし、適当にリストアップするのではダメです。

ファイルの内容をプログラム内で解析する必要があるので、記載方法をルール化しなければいけません。

今回は「1問ずつカンマ(,)区切りで記載」をルールとしました。

1問目の問題文,選択肢1,選択肢2,選択肢3,正解の番号
2問目の問題文,選択肢1,選択肢2,選択肢3,正解の番号
3問目の問題文,選択肢1,選択肢2,選択肢3,正解の番号

具体的には以下の通り。

 


プログラム全文

今回作成したプログラムのご紹介です。

のちほど、プログラムの解説を行います。

 

プログラムの解説

外部ファイル名用定数を追加

 

クイズの問題を記載してあるファイル名を定数で宣言します。

今回は「questions.txt」という名前を指定。

実際にこの名前でファイルを作り、プロジェクト関連のファイルがある場所に保存しておいてください。

通常だと、「.vcxproj」という拡張子のファイルや、ソースファイルがある場所です。

 

よくわからない場合や、別の場所にファイルを置きたい場合は、そのファイルパスを定数に指定すればOKです。

例えば、「D:\mydir\Program\quiz」のフォルダの中に「mondai.txt」というファイルを作りたい場合は、定数に『D:\\mydir\\Program\\quiz.txt』と指定します。

円マーク(\)は2つずつにすること。じゃないとエスケープ文字として認識されてしまいます。
変更例
項目 内容
フォルダパス D:\mydir\Program\quiz
ファイル名 quiz.txt
定数に指定する内容 D:\\mydir\\Program\\quiz.txt

 

クイズ問題ファイルのオープン

外部ファイルを読み込む大まかな流れは以下の通り。

  1. ファイルのオープン
  2. ファイルの読み込み
  3. ファイルのクローズ

まずはファイルオープン処理についてです。

 

ファイルのオープンは「fopen関数」で行います。

引数は2つあります。意味は以下の通り。

引数 意味
第一引数 オープンするファイルパス
第二引数 オープンするモード
↓よく使うモード
“r”:読み込み専用
“w”:書き込み専用
“a”:追加書き込み専用

今回は、第一引数に定数「QUESTION_FILE」を、第二引数に読み込み専用である”r”を指定します。

fopen関数の発行に成功(ファイルが開けた)したら、ファイルポインタがリターンされます。

以降のファイル操作系関数では、このファイルポインタを用いて制御を行いますので、変数fpに保存しておきます。

 

fopen関数の発行が失敗したら、リターン値はNULLとなります。

ファイルのオープンに失敗したときのためにエラー処理を行っておきましょう。

 

失敗した旨のメッセージとともに、fopen関数に指定したファイル(開けなかったファイル名)も表示しておくと、エラーとなったときに調査しやすいです。

エラー後は後続の処理をしても無駄なので、おとなしくreturnします。

今回の場合はあまり意味ないですが、プログラムを外部実行したときのために異常値を示す「-1」でリターンしておきます。

 



ファイルの読み込みと解析

 

ここが今回追加したメインの部分となります。

ここでやっていることは以下の通り

  1. ファイルから1行取得
  2. 取得した行を解析して問題文を取得
  3. 取得した行を解析して選択肢1を取得
  4. 取得した行を解析して選択肢2を取得
  5. 取得した行を解析して選択肢3を取得
  6. 取得した行を解析して正解の番号を取得
  7. クイズの構造体に追加
  8. 次の行へ

細かくみていきます。

 

 

この一文は「ファイルを1行ずつ読み込み続ける」ということを意味しています。

fgets関数はファイルを1行読み込みます。

読み込むデータがなくなったときはfgets関数のリターンがNULLとなります。

fgets関数の引数の意味は以下の通り。

引数 意味
第一引数 読み込んだデータを保存する変数
第二引数 読み込むデータ量
第三引数 読み込むファイルのファイルポインタ
(fopen関数で取得したもの)

今回は1000バイトの容量をもったlineという変数にデータを保存します。

読み込むデータ量はlineに保存できる最大数で、sizeof関数で指定しています。

 

 

テキストファイルから読み込むデータには「\n」が含まれているものがあります。

大正->昭和->○○->令和\n○○に入る年号は?,慶応,明治,平成,3

これをそのまま出力すると「\n」が文字列として認識されるため、改行処理が行われません。

そのため、文字列の「\n」を改行コードに変換する処理が必要です。

C言語の標準関数にはそのようなものはありませんので、関数を自作しました。

詳しくは以下の記事をご覧ください。

文字列「エンエヌ」を改行コードに変換する関数【自作関数】
【C言語】文字列「\n」を改行コードに変換する関数【自作関数】こんにちは、コンジ(@pippi_kon)です。 この記事では、C言語の『文字列「\n」を改行コードに変換する自作関数』をご紹介し...

 

 

読み込んだ行を解析して問題文を取得します。

strtok関数は、指定した文字列を指定した区切り文字で分解できます。

strtok関数の引数の意味は以下の通り。

引数 意味
第一引数 分解する文字列
NULL指定時は前回からの続き
第二引数 読み込むデータ量

今回は、ファイルから読み込んだ行(line)を区切り文字(,)で分解します。

 

strtok関数での分解に失敗した場合はNULLがリターンされますので、エラー処理も行っておきましょう。

  1. エラーメッセージの表示
  2. エラーフラグをON
  3. 解析処理のループから脱出

エラーメッセージには、問題文の解析に失敗した旨と、読み込んだファイル名・解析中の行番号を表示しておくと、エラー時に調査しやすいです。

set_quiz_error_flagは、後のファイルクローズ処理後にプログラムを中断させるためのフラグです。

 

 

問題文につづいて選択肢1を解析取得します。

strtok関数の第一引数に「NULL」を指定すると、前回の分解から継続して分解するということができます。

 

選択肢1と同様に、残りの選択肢2,3と正解の番号も分解します。

正解の番号の分解では、番号の最後にカンマがないですが、行の終端までの区切りとなるので問題ありません。

 

 

問題文・選択肢・正解の番号の解析が正常に終わったら、読み込んだ内容をクイズの構造体に登録します。

正解の番号は整数型で登録する必要があるので、以前も登場したatoi関数で整数型に変換して登録します。

登録後、カウンタiを進めておきましょう。

 

クイズ問題ファイルのクローズ

 

ファイルの読み込み処理が終わったので、オープンしていたファイルをクローズします。

開けたら閉じる。

 

ファイル読み込み失敗時はプログラム終了

 

ファイル読み込みと解析処理で何かしら失敗していたら、後続の処理は行わずにプログラムを終了させます。

 


最後に

今回は、『クイズの問題を外部ファイル(テキストファイル)から読み込む方法』をご紹介しました。

ファイル操作系の処理は、C言語でなにか大きなプログラムを作ろうとしたときには必須のテクニックとなります。

開いて、読み書きして、閉じる

といったシンプルな流れなのですが、詳しくみていくと実はけっこう奥が深いのです。

今回ご紹介したのは基本中の基本の操作なので、感覚だけでもつかんでいただけたらと思います!

 

では今回はここまで。おつかれさまでした!

【2020年版】定番の解説本!C言語入門におすすめの参考書_サムネ
【2021年版】定番の解説本!C言語入門におすすめの参考書『C言語初心者にオススメしたいプログラミング参考書』をご紹介します。...
created by Rinker
¥2,750
(2021/05/18 10:49:56時点 Amazon調べ-詳細)

created by Rinker
¥2,530
(2021/05/18 10:49:57時点 Amazon調べ-詳細)

ABOUT ME
コンジ
コンジ
自分のやりたいことでお金を稼げる生活に憧れて脱サラ。 アプリ開発やブログ運営をしながらのんびり暮らしています。