Version 5.14
手探りと柔軟性
「ではこの前の続き。今回は実際に読み取ってる部分を見てみましょう」
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 );
}
『つっても、この前のランタイム使ったときとほとんど同じだよね』
「結局は sscanf() や sprintf() 使ってるしね。ってゆーか実際に MFC
使ってるとこは」
cStdFile.ReadString( chOneLine, 255 )
『だけだねー。ぱっと見、 fgets() の代わり?』
「そういうこと。 CStdioFile::ReadString() は、ファイルから1行文字列
を取り込んで、第1引数にコピーします」
『 255 ってのは、1行がそれ以上だと入んないってことだよね』
「そう、それ以降はカットされます。で、重要なとこ。 CStdioFile には、
EOF を調べる方法がありません」
『げ、そなの。親クラスの CFile の方にも?』
「ありません。代わりに、 CStdioFile::ReadString() の戻り値が、最後ま
で行くと NULL が 返ってくるから」
『それを while で調べてるわけね。で、 while の中はランタイムと同じっ
と』
「あと最後に、この前のプログラム全体のを見て。 close() みたいなの呼
んでないでしょ」
『ホントだ、これって std::ifstream 使ったときと同じってこと?』
「そういうこと。クラスには〈後始末をする機能〉があるから、それを
CStdioFile も std::ifstream も使ってるわけ」
『クラスじゃない、ランタイムの方は fclose() 呼ばなきゃダメってことね』
「さてここで、もうちょっとブラッシュアップしていきましょう」
『どっか手を加えるとこある?』
「たとえば、 sscanf() して sprintf() してるとことか」
『そうよねー、整数値として取り出して、また文字列として書き込むってか
なりムダ入ってるよね』
「どんな感じにすればいいと思う?」
『こういうのわざとするのって、確か〈入ってる値が整数値かどうか〉を確
かめたかったからだよね。じゃ、その確かめるってのを直接すればいいん
じゃない?』
「そういうこと! そのチェックのために、 isdigit() っていうランタイ
ムを使います。そうすると、冒頭のプログラムの部分はこうなります」
char chOneLine[256];
while( cStdFile.ReadString( chOneLine, 255 ) )
{
for( int iF1 = 0; iF1 <= 255; iF1++ )
{
if( chOneLine[iF1] == '\0' )
{
break;
}
else if( chOneLine[iF1] == '\n' )
{
chOneLine[iF1] = '\0';
break;
}
else if( !isdigit( chOneLine[iF1] ) )
{
TRACE( "整数値じゃないです\n" );
return;
}
}
m_cDataLstBox.AddString( chOneLine );
}
『おー、 sscanf() と sprintf() の部分が、ごっそり for ループに置き換
わっちゃったね』
「べたな方法だけど、文字ひとつずつチェックする仕組みにしてるから。ま
ず for 」
『 255 ってことは文字サイズのチェックだよね』
「そう、配列を超えないようにするための for 。だから、実際にはこの for
の条件でループが終了することは少ないです」
『あ、だから for の中に break とか return とかいっぱいあるのねー。
for の iF1 は、配列のインデックスね』
「このインデックスナンバーを使って、文字配列内の文字ひとつひとつを見
ていく仕組みにしてます」
『1回のループで1文字、ね。最初の if は、終端文字かどうかチェック』
「もし終端文字なら break して for から抜けます」
『で、それを CListBox::AddString() でリストボックスに追加するわけね』
「終端文字じゃなかったら次の else if へ」
『今度は改行文字かどうかのチェック。あれ、改行文字を終端文字に置き換
えてるの?』
「そう、この置き換えの部分をコメントアウトして試してみて」
『ビルドして実行。あ、 100・ってなんかゴミが付いてる』
「改行文字が残ってるとそう表示されちゃうんだよね」
『なんで今まで表示されなかったんだろ』
「 sscanf() で整数値だけ取り出していたからね」
『あー、このときに改行文字がどっか消えちゃってたんだね』
「で、改行文字だった時にもその行の読み取りは終了だから、 break 」
『終端文字でも改行文字でもなかったら、最後の else if 。ここでやっと
isdigit() が出てくるのね』
「 isdigit() はランタイムの関数で、文字が整数値かどうかチェックしま
す」
『 MSDN にも載ってるね。整数値じゃないなら0が返ってくるから、それを
! で反転させて if でチェック、ね』
「で【整数値じゃないです】っていうエラーを表示」
『ひとつずつ見ていけば簡単ね』
「まーね。でも、何もないとこからいきなり書くと結構大変だと思うよ」
『たとえば?』
「たとえば for ループで文字数制限をしないとか、 100・って表示されて
もその原因が分かんないとか」
『う”、ありがちそうかも』
「あと、これだとかなり大きな数字まで入るから、あとで int とかに入れ
るときには」
『サイズのチェックしなきゃいけないんだ! めんどくさー』
「こういうことに気を回すためには、実際にプログラムを書くことと、解説
を良く読むこと、その両方が大事」
『どゆこと?』
「 100・の問題なんかは、単に〈こうすればいい〉って聞くだけじゃなくて、
実際に自分で解決する練習しないとね」
『つまり試行錯誤しろってこと?』
「そういうこと。特にプログラミングっていうのは、コンピューターの中身
っていう〈見えないもの〉を相手にするから、そういう手探りに慣れてない
とね」
『失敗してもいいから、とにかくいっぱいプログラム作ってけってゆーのね』
「でもそれだけじゃダメ。自分に都合のいいことしかしないから」
『自分に都合のいいこと?』
「たとえば〈入力される数字は8桁まで!〉とか」
『確かに、他の人に使ってもらうときにはそういうことしちゃまずいよね』
「手探りで調べていくと、どうしても知識に偏りができちゃうから、それを
補うように他の知識もどんどん入れるようにして、できればその知識につい
ても調べるようにして」
『要するに手も頭も動かせってことね。なんか結構大変……』
「何をいまさら。さて! 最後に、 std::ifstream を使った時ののブラッ
シュアップについて」
while( !cIFStrm.eof() )
{
cIFStrm
>> i;
if( cIFStrm.fail() )
{
TRACE( "整数値じゃないです\n" );
return;
}
char chDest[256];
sprintf( chDest, "%d", i );
m_cDataLstBox.AddString( chDest );
}
「ってなってたけど、これはこのままでいいかな」
『い、いいの? sprintf() が邪魔だなー』
「そういう場合には、 sprintf() の代わりに std::strstream っていうも
のがあります」
『あ、 STL & iostream 入門でやってたのだ!』
「でもこれ使うのは結構面倒だからパス」
『ええっ!?』
「それよりかは、 isdigit() 使った方が楽かな」
char chOneLine[256];
while( !cIFStrm.eof() )
{
cIFStrm.getline( chOneLine, 255 );
for( int iF1 = 0; iF1 <= 255; iF1++ )
{
if( chOneLine[iF1] == '\0' )
{
break;
}
else if( chOneLine[iF1] == '\n' )
{
chOneLine[iF1] = '\0';
break;
}
else if( !isdigit( chOneLine[iF1] ) )
{
TRACE( "整数値じゃないです\n" );
return;
}
}
m_cDataLstBox.AddString( chOneLine );
}
『 cIFStrm >> で取り出してたのが、 getline() ってメンバ関数に変わっ
てる……これって CStdioFile::ReadString() と同じよね』
「そう。実際には cIFStrm >> chOneLine ってできるんだけど、最大サイズ
の設定に std::ifstream::width() ってメンバ関数を呼ばなきゃいけないし、
こっちの方が分かりやすいかなと思って」
『そりゃ分かりやすいね、さっきのとほとんど同じだもん』
「でも、別にこっちじゃなくて、最初の sprintf() 使った方がいいと思う
けどね」
『そなの?』
「ま、この辺は好みの問題かも。重要な点はふたつ。ひとつは、内部的な仕
組みをちゃんと理解して、自由にプログラムが組めるようになること」
『内部的な仕組みって?』
「配列にどうアクセスしてるのかとか、文字がどうなってるのかとか」
『あー』
「もうひとつは、エラーチェックをちゃんとすること」
『ヘンテコな値を入れられてもちゃんと対処できるようにってことね』
「そういうこと」
/*
Preview Next Story!
*/
『なんか今回は意識論って感じ』
「それが一番大事なんだけどね」
『そなの?』
「倫理観のない医者に診られたくないでしょ」
『というわけで次回』
< Version 5.15 API でファイル操作 >
「につづく!」
『でもさ、医学部とかでそういうこと教えてないっぽいよね』
「だから日本はダメなんだ!!」
『なんだかなー』