Version 5.13
MFC でファイル操作!
「これまでに iostream とランタイムで、ファイルから読み込むプログラム
を作ってみました」
『で、今回は MFC を使って読み込む、と』
「そうです。 MFC には CFile とか CStdioFile っていうクラスがあるから、
この CStdioFile を使ってファイルを読み込んでみましょう」
『はーい』
「まず、この前のランタイムの例のように、 CFileTestDlg::OnBtnShow()
の最初にプログラムを書いて、 return で終了させて、そのあとに iostream
のプログラムを取っておく形にします」
『ランタイムのプログラムはどうするの?』
「削除しちゃって」
『え〜!?』
「……やっぱ、テストプログラムはテストプロジェクトに書いた方がいいね」
『そうよそうよー。まあとりあえず、プログラム他のとこに移しとこ』
「その移したところに、次のようなプログラムを書き込んで」
void CFileTestDlg::OnBtnShow()
{
// ファイルを開きます。
CStdioFile cStdFile;
BOOL bOpened
= cStdFile.Open( "Data.txt", CFile::modeRead );
if( !bOpened )
{
TRACE( "ファイルがない!\n" );
return;
}
int iError = 0;
int iTemp = 0;
char chOneLine[256];
while( cStdFile.ReadString( chOneLine, 255 ) )
{
iError = sscanf( chOneLine, "%d", &iTemp );
if( iError == 0 )
{
TRACE( "整数値じゃないです\n" );
break;
}
char chDest[256];
sprintf( chDest, "%d", iTemp );
m_cDataLstBox.AddString( chDest );
}
return;
// 以下、 iostream のプログラムを取っておきましょう。
『どっちかってゆーと、ランタイムの時のプログラムに近いね』
「うん、 sscanf() を使うからその辺でね。まずは最初の部分」
// ファイルを開きます。
CStdioFile cStdFile;
BOOL bOpened
= cStdFile.Open( "Data.txt", CFile::modeRead );
if( !bOpened )
{
TRACE( "ファイルがない!\n" );
return;
}
『まず CStdioFile クラスの変数 cStdFile を作ってるね。クラスだから、
iostream の cStrStrm に近い感じだよね』
「この辺は同じ。 CStdioFile は MFC のクラスだから MSDN に詳しく書い
てあるからね」
『んじゃ次の Open() 。これも std::ifstream::open() とほとんど同じだ
ね。でも微妙に違うとこも』
「まず MSDN で見てみようか。 CStdioFile のメンバ一覧を見て」
『ほい出した。……って、 Open() なんてメンバ関数ないよ? 全部で4つ
くらいしか載ってないし』
「かなり昔の話になるけど、 CWnd と CDialog の関係って憶えてる?」
『ちょっと待って思い出すから……あ、継承ってヤツ!』
「そう、 Ver 3.1 ( No.026 ) や Ver 3.2 ( No.027 ) でやったね。継承関
係にあるときには、子クラスは親クラスのメンバを使えるでしょ」
『だから CDialog は CWnd のメンバ関数も使えるんだよね。……ってこと
は、 CStdioFile にもなんかの親クラスがあって、 Open() はそのメンバ関
数なんだ!』
「そういうこと。その親クラスが、さっき言った CFile 。今のメンバ一覧
の下に【基本クラスのメンバ】ってあるでしょ」
『基本クラス?』
「【親クラス】のことを【基本クラス】とか【基底クラス】とかとも言うか
ら。まあ【 Base class 】の訳なんだけどね。実際は【基底】って訳される
方が多いよ」
『 MSDN ダメダメってことね。んじゃその基本クラス一覧! あ、 CFile
のクラスメンバって出た! ちゃんと Open() もある!』
virtual BOOL Open( LPCTSTR lpszFileName, UINT nOpenFlags,
CFileException* pError = NULL );
『……なんかむっちゃ難しいんですけど』
「まず virtual はかなり難しいから後回し。戻り値は BOOL 、これが
FALSE だと」
『ファイルなかった! だよね。 iostream は bool で MFC は BOOL』
「 MFC の方が古いから、 bool 型には対応してないから」
『第1引数は LPCTSTR 、つまり読み取り専用文字列、これがファイル名ね。
次の UINT って……?』
「 UINT は API でよく使われてる型のひとつで、 unsigned int のこと」
『略して UINT ね。〈〜のこと〉ってことは typedef されてるんだよね』
「そう、 API の Include\Windef.h ってファイルに書いてあるからね」
『あら、いつもは調べましょーってなるのに』
「 UINT みたいのはいっぱい引っかかっちゃうから探すの大変だからね。
API 関係の typedef はたいがいこのファイルに書いてあるから」
『ほー。で、この変数の意味はっと、 Flags って書いてあるからフラグに
使うんだね。あ、 fopen() の時の "r" の代わり?』
「そういうこと! このフラグで、読み取り専用とかを指定します」
『それが CFile::modeRead なんだ』
「 CFile::CFile() ってメンバ関数のページに書いてあるから見てみて」
『結構いっぱいあるねー。ところで CFile::modeRead って、名前空間?
それとも std::ifstream::open() っての?』
「 std::ifstream::open() の方。 CFile がこんな感じになってるでしょ」
『まって、今ファイル検索で探すから』
「 Afx.h ってファイルにあるよ」
『う〜』
class CFile : public CObject
{
DECLARE_DYNAMIC(CFile)
public:
// Flag values
enum OpenFlags {
modeRead = 0x0000,
modeWrite = 0x0001,
modeReadWrite = 0x0002,
shareCompat = 0x0000,
shareExclusive = 0x0010,
shareDenyWrite = 0x0020,
shareDenyRead = 0x0030,
shareDenyNone = 0x0040,
modeNoInherit = 0x0080,
modeCreate = 0x1000,
modeNoTruncate = 0x2000,
typeText = 0x4000,
typeBinary = (int)0x8000
};
// 以下略
『む、ちょっと難しそう』
「ぱっと見でそう考えるの悪いクセだよ〜」
『う”……あ、そういえば enum ってやったね』
「 Ver 4.09 ( No.059 ) でやったでしょ、定数を作る方法」
『こうすると const int みたくなるんだよね。 0x0001 とか 0x0002 とか
って整数をフラグにしてるんだよね』
「 0x01 と 0x02 を、ビットで表すと?」
『えーっと、 00000001 と 00000010 だよね。あ、ビットがずれてるのって
意味あったんだよね!』
「こうすれば | って演算子を使ってふたつのフラグをひとつの値に組み合
わせられるからね」
『 0x01 | 0x02 が 0x03 つまり 00000011 になるんだもんね。詳しくは
Ver 4.05 ( No.055 ) を参照!』
「この辺も MSDN に書いてあるから。ちなみにこうやって複数のフラグを重
ねられるから Flags って複数形」
『ほー。……ところでー、これ見るとやっぱただのメンバ変数っぽいけど?
だったら cStdFile.modeRead でもいけそうだけど』
「大丈夫だよ」
『ビルド、あホントだ。じゃーなんでわざわざ?』
「メンバ変数って、クラスの変数ごとに違うでしょ。たとえば cStdFile1
と cStdFile2 があったときに、別々のファイル名が入ってるわけでしょ」
『うん、入ってそう』
「でも enum で作られた定数値は、定数だから」
『どの変数でも値が同じ!! そういうのは CFile::modeRead ってした方が
いいんだ』
「どっちかって言うと習慣的なものかもね。あともうひとつ。 enum を使っ
てるメリットがないって気付いた?」
『どゆこと?』
「 UINT 型ってことは、どんな整数でも使えるってことでしょ」
『あ! そういえば、 enum しか使えないとかそういう話あったね』
「この UINT 型が OpenFlags 型になってれば、ここで enum されてるもの
しか使えないから安全。 UINT だとどんな値でも使えるからねー」
『やっぱ MFC ダメダメねー。そういえば、わざわざ unsigned な理由っ
て?』
「 Ver 4.06 ( No.056 ) でちょっとだけ触れたけど、ビットフラグの時は
unsigned にする事が多いかな」
『はじっこのビットが負のフラグになってるとなんかちょっと、ってのね』
「まぁ、基本的にはあんまり変わらないから、その変数が【ビットフラグに
使ってます】っていう意味にしたいときに使うくらいかな」
『えーっと、この第2引数関係はこれで終わり?』
「うん終わり。第3引数の CFileException* pError = NULL はパス」
『ええっ?』
「だって難しいから」
『まー難しいんならしゃーないか。あれ、でもこの部分って、さっきのプロ
グラムで書いてないよね』
「かなり昔教えたことだから忘れちゃったかもしんないけど、引数のあとに
= NULL とかって値を入れとくと、その引数を省略できるから」
『いつやった? それ』
「 Ver 2.11 ( No.022 ) 」
『うわぁ……。で、省略して書くと、その = の右側のが入れられるわけね』
「そういうこと。実は、 std::ifstream の時もこういうのがあったんです。
MSDN で open を検索してみて」
『ほい。お、いっぱいリストに出てきた……けど ifstream::open があった』
「このページに」
void open( const char* szName, int nMode = ios::in,
int nProt = filebuf::openprot );
『あとふたつは省略できるから std::ifstream のときは使わなかったんだ。
……もしかして、この省略してるのって "r" とか CFile::modeRead と同じ
意味!?』
「そういうこと。 std::ifstream の便利なとこのひとつだね」
『は〜』
/*
Preview Next Story!
*/
『なーんか今回、大復讐大会って感じね〜』
「だから字が違うって」
『もっと使い方とか色々教えてよー』
「それよりも、プログラムの理解力を深める方が大事だから」
『というわけで次回』
< Version 5.14 手探りと柔軟性 >
「につづく!」
『そーゆー地味〜な教えかたすると、人気出ないよ?』
「僕にとっては、火美ちゃんの成長の方が重要!!」
『うお!!』