Version 5.11
ランタイムでファイル操作!
「前回は iostream を使ってファイル操作をしました」
『 std::ifstream ってゆーの使ったんだよね』
「今回はCランタイムの関数を使って、ファイル操作をしようと思います」
『ランタイムにもそういう機能があるんだ』
「まずはいつもの CFileTestDlg::OnBtnShow() の最初をこう書き換えて」
void CFileTestDlg::OnBtnShow()
{
// ここにあとで追加します。
return;
int i;
// あとは前回のと同じ。
『ん? return したら関数終わっちゃうよ』
「その return の前にあとで紹介するプログラムを描き込むから」
『どゆこと?』
「これからランタイムの使い方を教えるんだけど、これはあくまで勉強用の
テストだから、あんまり CFileTestDlg::OnBtnShow() の中身は書き換えた
くないんだよね」
『つまり iostream 使ったのをメインにしたいんだよね』
「そういうこと。だから、テストは CFileTestDlg::OnBtnShow() の最初に
書き加える形にして、この前作った部分はとっておきます」
『 return ですぐ関数が終わっちゃうのは、 iostream 使った部分が実行さ
れないようにしてるから?』
「そう。ま、ホントはテスト自体はテスト用のプロジェクトでした方がいい
んだけどね」
『前もそんなこと言ってたね』
「でもそうすると Data.txt とかの用意が面倒だから、ここでしちゃうこと
にします」
『手抜き〜』
「う”」
『で、 return の前はどうするの?』
「とりあえず、 iostream を使ったときと同じことをランタイムですると、
こんな感じになります」
void CFileTestDlg::OnBtnShow()
{
// ファイルを開きます。
FILE *pstFile
= fopen( "Data.txt", "r" );
if( !pstFile )
{
TRACE( "ファイルがない!\n" );
return;
}
int iError = 0;
int iTemp = 0;
while( !feof( pstFile ) )
{
char chOneLine[256];
fgets( chOneLine, 255, pstFile );
iError = sscanf( chOneLine, "%d", &iTemp );
if( iError == 0 )
{
TRACE( "整数値じゃないです\n" );
break;
}
char chDest[256];
sprintf( chDest, "%d", iTemp );
m_cDataLstBox.AddString( chDest );
}
// ファイルを閉じます。
fclose( pstFile );
return;
// 以下略。
『うあ〜、長いし分かんないし難しそう!!』
「初めて出てくるのがいっぱいあるからねー。ひとつひとつ見ていくよ。ま
ずここから」
FILE *pstFile
= fopen( "Data.txt", "r" );
if( !pstFile )
{
TRACE( "ファイルがない!\n" );
return;
}
「まず fopen() から。これはランタイムの関数のひとつで、ファイルを開
くもの」
『 std::ifstream::open() と同じってことだよね』
「そう、この関数を呼ぶことで、ファイルが使えるようになります。第1引
数は開くファイルのファイル名、第2引数はファイルモードの指定」
『ファイルモード?』
「書き込みか、とか、読み込みか、とかを指定するための。 "r" だと読み
込み専用、 "w" だと書き込み専用。この辺は fopen() のリファレンスに書
いてあるから」
『文字列で指定って、 TRACE() とか sprintf() とかみたい。でも、なんで
std::ifstream のときにはこういうの使わなかったの?』
「 std::ifstream はそういうのを省略できるから、わざわざ書かなくてよ
かったわけ」
『へー』
「この〈省略できる〉っていうのは C++ 言語の機能。ランタイムは C 言語
の時代に作られたから、そういう機能がついてないわけ」
『それだけ使いづらいってこと?』
「そうなるんだよね。これから見ていくけど、そういうのいっぱいあるか
ら」
『げげ』
「さて、 std::ifstream はクラスだから、このクラスの変数を作って、
ファイルの情報を全部蓄えることができました」
『へー、そうだったんだ』
「ところが、 fopen() は関数だから、そういう情報を外に置かなきゃいけ
ない」
『もしかしてそれが pstFile って変数?』
「そういうこと。 FILE っていう構造体へのポインタが返ってくるから、そ
れを受け取って使います」
『? こうぞうたいってなに?』
「……クラスみたいなもの!」
『ホントにぃ?』
「基本的にはね。クラスと同じく、いろんな変数を格納できる型だから」
『その中にファイルの情報が入ってるんだ』
「注意して欲しいのは、ポインタとして返ってくること」
『それって変じゃない? 変数って関数から抜けるとなくなっちゃうんだか
ら、そういうことってできないはずでしょ』
「 Ver 4.13 ( No.063 ) でも見たけど、変数を自由に作れて自由に削除す
る方法があるから」
『あ、 new と delete とかゆーのだよね』
「ここで返ってくるポインタが指し示してる変数は、そういうふうに作られ
た変数ってことだね。あとでちゃんと削除するし」
『へー』
「で、この FILE のポインタを〈ファイルポインタ〉って言います。よく使
われる単語だから憶えといて」
『って、そのまんまじゃん』
「あ、でも〈ファイルポインタ〉って違う意味で使われることもあるから
ちょっと注意」
『はーい』
「で、もしファイルがなかったら、このポインタには NULL が返ってきます」
『ぬるってなに?』
「……あ、 NULL 教えてなかったか……。ポインタってアドレスを入れる変
数でしょ」
『そだよ、変数はメモリ上にあって、メモリの住所がアドレスで、それを取
っておくのがポインタ』
「で、アドレスは整数値でしょ。この整数値が0のアドレスを NULL ってい
います」
『??』
「つまり NULL == 0 って考えればOK。実はメモリ上の0の部分は使えな
いから、この 0 のポインタを使って操作することはできないから」
『つまりエラーのときはこの NULL ってのが返ってくるってことなんだよね』
「そういうこと。ポインタが戻り値として返ってくる場合には、こういうふ
うになってることが多いから。成功したら、使えるアドレス。失敗したら、
使えない NULL が返ってくる、ってこと」
『で、次の if( !pstFile ) はそれをチェックしてるんだ。 !pstFile だと
0のとき0以外になるから if の中に入ってエラー表示、だね』
「これが std::ifstream::fail() の代わりになってます」
『これって TRUE と FALSE にちょっと似てる感じ』
「うん、そういう意味合いのものだね。基本はただの整数なんだけど、それ
に別の名前を付けて意味を持たせてるって感じかな。ちなみに null は英語
でゼロとかって意味」
『でもなんで NULL って別名付けるの? 0 でもいーじゃん』
「難しいところだね。 NULL はポインタにしか使わないから、そういう意味
で分かりやすいってことはある」
『あ、つまり普通の整数値の 0 と、アドレスの 0 の違いみたいな?』
「なんだけど、実際には NULL ってまんま 0 と同じだから、見た目の違い
しかないんだよね」
『どゆこと?』
「つまり int i = NULL; ができるってこと。これがバグの原因になったり
もするから、 NULL じゃなく 0 を使う人も最近多いみたいだね」
『なんか複雑……』
「ま、この辺は C++ のことをもっと知らないとわかんないかも」
『やっぱ iostream の方が分かりやすいかなー』
「確かにファイルポインタが NULL かどうか、っていうのに比べればね。こ
こから先、ファイル操作を擦るときにはファイルポインタの pstFile を使
っていくことになります」
『この前の cIFStrm の変わりね』
「さて、ずばっと飛んで、この行を見て」
// ファイルを閉じます。
fclose( pstFile );
『あ、これがさっき言ってた、ファイルポインタを削除するってとこでしょ』
「そう、ファイルポインタが指し示す変数はローカル変数じゃない形で作ら
れたものだから、自動的に削除されないんだよね」
『だから使い終わったら削除する、と』
「削除したらそのファイルポインタは使えないから」
『また fopen() しなきゃいけないってことだよね』
「そういうこと。 fclose() したあと pstFile = NULL してもいいかも」
『その方が安全そうね』
「それと、ファイルポインタを削除するだけじゃなくて、実際にファイルに
アクセスできなくする機能もあるからね」
『どゆこと?』
「ちょっとあとで説明するけど、ファイル操作って基本的にはOSの機能」
『ウィンドウズに、ファイルを操作する機能が付いてるんだ』
「 fopen() はファイルを使えるようにする関数、 fclose() はもう使わな
くていいよう閉じる関数。このふたつは、間接的にウィンドウズのファイル
操作機能を呼び出してるもの」
『なんか MFC みたい』
「そういう感じのもの。で、ファイル操作をするためのデータを、動的に作っ
た変数に入れて、そのポインタを返す」
『それがファイルポインタなんだ!』
「 fclose() するときには、まずウィンドウズに〈このファイルは使いませ
んよ〉って宣言してから、動的に作った変数を削除する。なんか今の話だと
変数作って削除するってとこだけ注意が向いてるみたいだから」
『うん向いてた。つまり、変数を作って削除、も機能のうちだけど、ファイ
ルを開いて閉じる、っていうのの方がちゃんとした機能ってことね』
「そうじゃなきゃ std::ifstream::open() の意味ないでしょ」
『でもさ、 std::ifstream の方には fclose() に当たるのないでしょ』
「 std::ifstream::close() ってメンバ関数がそれに当たるの。でも、自動
的に呼ばれるから使ってないだけ」
『自動的に呼ばれる?』
「そう、これも C++ の機能のひとつで、デストラクタって機能を使ったも
の」
『これも C++ の便利さなんだ』
「こういうふうに C++ のいいところが使われてるから、 std::ifstream の
方が便利なんだよね。とういうわけで次回に続く!」
/*
Preview Next Story!
*/
『お、 while ループは来週?』
「またこーやって長くなる。こういうのって普通の本にも載ってるんだよね」
『でもあたし、水希ちゃんの解説で聞きたい!』
「それってもしかして……」
『というわけで次回』
< Version 5.12 ランタイムは面倒! >
「につづく!」
『だって水希ちゃんの解説って……』
「……解説って?」
『タダじゃん!』
「あう」