Version 5.21
ファイル読み込みをさらにさらにブラッシュアップ!
「では前回の続き」
while( !cIFStrm.eof() )
{
cIFStrm
>> i;
if( cIFStrm.fail() )
{
MessageError( IDS_E_NONINT );
return;
}
char chDest[256];
sprintf( chDest, "%d", i );
m_cDataLstBox.AddString( chDest );
}
『この chDest を while の前で宣言した方がいいってことなんだよね』
「そう。まず復習すると、 while のすぐ後にある { と } の塊を〈ネスト〉
って言います」
『で、変数はネストの中でしか使えないんだよね』
「なんでだっけ」
『えーっと、ネストの中でしか寿命がないから、外に出るとなくなっちゃう
んだよね』
「そういうこと。この〈外に出る〉っていうのがクセモノで、実は while
のループ1回ごとにネストから外に出る形になります」
『げ、そうなの!?』
「これは、 while 自体はネストの外にある、って考えてもいいし、 Ver 2.7
( No.018 ) でやったように、ネストを省略すると〈次の行だけループ〉に
なるから」
『なるから?』
「なるから、ネスト全体がループの繰り返しの対象、って考えてもいいかな」
『なるほど。こう見ると、ネストが1行、ってみなされるのかな』
「そういうところあるかもね。で、なんで chDest は while の前で宣言し
た方がいいんだと思う?」
『えーっと、 while でループの外に毎回出るんだから、その度に……寿命
が切れちゃう、ってことは変数がなくなっちゃう!』
「そういうこと。ループの度に chDest が削除されて再び作られて、って繰
り返しになるからね」
『その繰り返しって結構無駄?』
「場合によるけど、そんな無駄じゃない」
『あらら』
「ただ、やっぱり場合によるからね。 std::ifstream 使ったときに〈削除
されるときに呼ばれる関数〉があるって言ったでしょ」
『そっか、それが何度も何度も呼ばれることになっちゃうんだ』
「そういうこともあるから、ある程度は気を付けておいた方がいいかな。あ
と付け加えるとすると」
『付け加えるとすると?』
「 int i; の部分も while の直前に置きましょう」
『えーっと、ずっと cIFStrm 作る直前にあったんだよね。あ、そういえば
while の中に入ってからだね、 i を使うの』
「そう、だから直前に作ることにします。ファイルがないときは作らずに済
むからね」
『結構細かいね』
「ま、それよりは〈使う直前に作る〉方が分かりやすいってのもあるかな。
さて次。今度はバグを直しましょう」
『ば、バグなんてあるの!?』
「あるんだよね。データが入ってる Data.txt を開いて」
『ほい。こーなってるんだよね』
100
200
300
「最後に改行加えてから実行してみて」
『ほい。……あ、 IDS_E_NONINT のエラーが出ちゃった!! なんでぇ!?』
「さて、なんでででょう」
『それを私に調べろと?』
「本気でプログラマーになるんなら、そういうことも調べられないとね」
『ううう……マニュアルに〈最後は改行しないで〉って書くのは』
「ダメ」
『やっぱり』
「ま、そうは言ってもこれは std::ifstream の関係だから、今の段階では
難しいよね」
『 std::ifstream の問題なんだ』
「そう、使いやすいってことで採用した std::ifstream だけど、仕組みは
難しいからこういう問題に出遭うとちょっときついかもね」
『そしたら fopen() とか使った方がいいってことになるのかな』
「それもいいし、 std::ifstream 一本で色々試していくのも悪くないだろ
うけどね。じゃ、この問題の理由について」
『うん』
「まず、 std::ifstream::eof() を呼び出した時に true が返ってくる条件
は?」
『ファイルの最後だったとき』
「そう。で、 cIFStrm >> i で整数値を読み込んだとき、最後に改行が入っ
てると、ちょうどファイルの最後に行かないわけ」
『どゆこと?』
「 cIFStrm の中に、次はどこから読み取るかが入ってるって言ったでしょ」
『 Ver 5.12 ( No.077 ) でやったね』
「最後の 300 を読み込んだとき、その位置が 300 の直後になるわけ」
『つまり、そのあとに改行が残ってるから、 std::ifstream::eof() でファ
イルの最後になんないんだ!』
「だから、改行じゃなくて 300 のあとにスペースを入れたりしても同じよ
うな結果になるから」
『スペース入れて実行、ホントだ』
「だから、改行やスペースがないときにはループは3回なんだけど、入って
るときは4回になるから」
『なければ 300 読み込んだときちょうど eof だから、4回目に入るときに
while で引っかかって終わるんだね』
「デバッグするときには、ブレークポイントを設置してこういった部分を調
べてるといいかな」
『なんか地道ね……しかもそれが分かっても解決方法分からないし』
「せかさないせかさない。重要なのは4回目のループ」
『4回目の cIFStrm >> i で読み込む時って、その改行やスペースしかない
んだもねんね、なに読み込んでるんだろぅ』
「ここで読み込めないから std::ifstream::fail() で true が返ってくる
わけ」
『そっか、これでエラー状態になっちゃうんだ!』
「でも実は、 cIFStrm >> i で読み込んだときに〈ファイルの終端が見つ
かった〉ってことで、 std::ifstream::eof() で true が返ってくる状態に
もなるんです」
『 std::ifstream::fail() かつ std::ifstream::eof() ってこと?』
「そういうこと。だから」
int i;
char chDest[256];
while( !cIFStrm.eof() )
{
cIFStrm
>> i;
if( cIFStrm.fail() )
{
if( !cIFStrm.eof() )
{
MessageError( IDS_E_NONINT );
return;
}
}
sprintf( chDest, "%d", i );
m_cDataLstBox.AddString( chDest );
}
「ってすれば解決」
『ビルドして実行! おーうまくいった!』
「この、 eof になってるってことに気付くかどうかなんだよね」
『気付くコツってあるの?』
「経験と試行錯誤」
『……。それと、なんか見にくくない?』
「そうだね、ネストが深くなってるし、 cIFStrm.eof() を2回呼んでるっ
ていうのはちょっと汚いかもね」
『ネスト深いのってまずいの?』
「プログラムを追うのが大変になることが多いね。この場合には」
if( cIFStrm.fail() )
{
if( cIFStrm.eof() )
{
break;
}
MessageError( IDS_E_NONINT );
return;
}
「って書くこともできるかな」
『これはこれでなんか……あ、別に fail() の必要ないんじゃない?』
「え?」
『たとえばさー、あ、これなら while の方も要らないから……』
while( 1 )
{
cIFStrm
>> i;
if( cIFStrm.eof() )
{
break;
}
if( cIFStrm.fail() )
{
MessageError( IDS_E_NONINT );
return;
}
sprintf( chDest, "%d", i );
m_cDataLstBox.AddString( chDest );
}
『ってするとか』
「うお! なぜこんな方法を……」
『なんか記憶の片隅に……そうそう、 Ver 2.9 ( No.020 ) で教わったよ』
「あ、そっか。確かにこれも方法のひとつ。でも……うーん」
『何迷ってんの?』
「この辺は色々人によってスタイル違うかな。 while に !cIFStrm.eof() を
残しておけば、そのループが〈ファイルの終端まで〉ってことで回ってるの
が分かるからね」
『無限ループだとどこで抜けるか分かりにくいかもね』
「ただ、そういうのにこだわると逆に解りにくくなるし、そういうのって個
人差が大きいからね」
『……で、どうしろと?』
「臨機応変で行きましょう」
『なんじゃそりゃ、それが教える立場の人間の言葉ぁ?』
「でもこれも大事なことだよ? チーム組んで〈無限ループは使わないよう
にする〉って決めたらこういうプログラムは書かないようにする」
『まわりに合わせろってこと?』
「じゃなくてー、コーディングスタイルくらい自由に変えられるようにしま
しょうってこと。どんなコーディングスタイルでもプログラムが書ける、そ
れがプロってもんよ」
『なんかそれプログラムのスキルと違う……』
/*
Preview Next Story!
*/
『でも確かに、掲示板とかでこういうのフレーミングしてるよね』
「僕としては、そういうのは不毛だなーと思うからね」
『確かになんでこんなことでってことで熱くなる人多いよね』
「そ、そういうのにはあんまこだわんない方がいいね」
『というわけで次回』
< Version 5.22 文字列クラス >
「につづく!」
『でもこだわらないのってなんか軟弱ぅ』
「う”」