#pragma twice

KAB-studio > プログラミング > #pragma twice > 081 Version 5.16 API で読み取る!

#pragma twice 081 Version 5.16 API で読み取る!

前のページへ 表紙・目次へ 次のページへ

 Version 5.16 
API で読み取る!

現在 API でファイルアクセス中!
前回はファイルを開きました。今回は読み込んでみます。が、その前に

    char chAllLine[1024];
    if( 1024 < GetFileSize( hFile, NULL ) )
    {
        TRACE( "ファイルが大きすぎます!\n" );
        CloseHandle( hFile );
        return;
    }

 GetFileSize() で F1 っと、あ、ファイルサイズを調べるのなんだ
今回は〈一度にファイル全体を読み込む〉ってことをするんだけど、読み
込むためのサイズを1024バイトって限定してるから
 1024 って数字は例によって適当よね。あ、ってことは chAllLine って
変数に読み込むんだ
これを 1024 文字の配列にしてるから、ファイルサイズがそれ以上大きい
と入りきらないからね
……
 char ひとつが1バイトだから
あー
それよりも、メモリ想像した方がいいかも
メモリを?
そう、 char ひとつ1バイト、1バイトひとつ1マス、それが1024個
メモリに並んでる、ってね
そういうのやったねー。そっか、配列って考えない方がいいんだね
配列よりも、1024バイトの領域確保、ってことで
ファイルも、メモリと同じ感じになってるの?
そうだよ、コンピューターの世界のものはすべてオンとオフでできてるか
らね。このオンとオフが?
1ビット! それが8つ並んで1バイト
だからメモリもファイルも同じって考えていいかも
で、戻り値にファイルサイズが返ってくるからそれが1024より大きい
かどうかチェックね。そういえば、第2引数は? 型が LPDWORD ってへん
てこだけど
戻り値の型が DWORD でしょ
あ、ホントだ
まず DWORD について。これは API が作った型。前に〈 API の型はこの
ファイルで定義されてる〉って言ったよね
 Wtypes.h ! Ver 5.08 ( No.073 ) でやったね
そのときは LPCTSTR が typedef されてるっていうのを見たんだよね。今
回の DWORD もこのファイルにあるから
あった!

typedef unsigned long DWORD;

えーっと、 unsigned だから符号ナシ、 long は4バイト整数
つまり4バイト符号ナシ整数値としてファイルサイズが返ってくるってこ
とだね
サイズだからマイナスないんだもんね
で、同じく LPDWORD もあるから

typedef DWORD __RPC_FAR *LPDWORD;

あ、 LPCTSTR のときのと同じのだ
同じように、 __RPC_FAR は空白に置き換えられるから、つまり
よーするに DWORD のポインタね
そゆこと。ま、 LP が頭に付いてたらポインタだから
……で、ポインタなのは分かったけど、なんでそれを引数に渡すの?
そういうのがポインタの使い道にあったでしょ。変数のアドレスを渡す、
関数側はそのアドレスを使って変数にアクセスできる
そのアドレスで値を格納できる! 戻り値の他に値を返す方法!
ファイルサイズがものすごーく大きいときに、戻り値だけじゃその大きさ
を表せないから、そのためにもうひとつ値を返す部分を用意してるわけ
それが MSDN のリファレンスに書いてある〈上位ダブルワード〉とか
〈下位ダブルワード〉のこと?
そういうこと。ワードは2バイトサイズだから、ダブルワードで4バイト。
ファイルサイズが大きい場合、桁の小さい方の4バイトを戻り値で、大きい
方を第2引数から返す仕組みになってるってこと
なんで〈ワード〉は2バイトなの?
昔は2バイトが1番一般的だから〈単語〉サイズだったのかも。ちなみに

typedef unsigned short WORD;

って定義されてるから
あ、 WORD 型だ……
ちなみにリファレンスに書いてあるとおり、ホントは戻り値が 0XFFFFFFFF
かどうかのチェックもした方がいいかも
えっと、必要ないなら第2引数 NULL 渡していいけど、それで戻り値に入
りきらなかったりすると 0XFFFFFFFF が返ってきちゃうんだ
ま、 unsigned だから if に失敗することはないんだけどね
戻り値に入りきらないサイズって?
符号ナシ4バイトの最大値は?
 4294967295 だから……あ、これが4ギガなんだ。でかっ
そんなファイル滅多にないってことでここでは処理してるけど、仕事用な
らちゃんとエラーチェックした方がいいかも
はーい。で、ここでは chAllLine が1024バイトだからそれ以上なら
関数終了ね。あ、 CloseHandle() って fclose() みたいなの?
そう、 CreateFile() で開いたファイルは CloseHandle() で閉じなきゃ
ダメ
 fopen() と fclose() と同じってことね
というわけでファイルサイズの確認をしたら、次にファイル全体を読み込
んでしまいます

    DWORD dwReadedSize;
    BOOL bRes
        = ReadFile
            ( hFile, chAllLine, 1023
            , &dwReadedSize, NULL );
    if( bRes == 0 && dwReadedSize == 0 )
    {
        TRACE( "ファイルから読みとれませんでした。\n" );
        CloseHandle( hFile );
        return;
    }

またひとつの API 呼ぶのに4行も書いてる……
コーディングスタイルはいいから、リファレンスと見比べてみて
えっと、第1引数はお約束のファイルハンドル、第2引数は、さっきの
1024バイトサイズの配列、これにデータ受け取るんだね
この API で一気にファイル全体を読み込んじゃうから、その前にチェッ
クする必要があったから
第3引数の 1023 は限界サイズ。これがあるんならさっきサイズのチェッ
クする必要なかったんじゃない?
リファレンスにはその辺詳しく書いてないから、念のためってことで
ふーん。第4引数は……アドレス渡してるね
これはさっき言ったのとほとんど同じ。型が LPDOWRD でしょ
ホントだ。リファレンスには〈実際に読み込んだサイズ〉って書いてある

これを後で使うから。ま、 GetFileSize() の戻り値を使ってもいいんだ
けどね
ふと思ったんだけど、この部分とか、第3引数のサイズとかって4ギガま
でしか指定できないじゃん、そしたらさっきのおっきなファイルの場合には
どうすればいいの?
ポインタのサイズは?
4バイト
ってことは、確保できるメモリの領域も4バイトってこと
……ってことは、そのでっかいファイルの時って、メモリに全部コピーで
きないってこと!?
そうなるね。そういう場合には、ファイルから少しずつ取り出して処理し
てくことになるかな
はー、めんどそー
確かに面倒だけど、そのくらい大きなファイルを操作するってことはかな
りの仕事だから、ミスがないようにプログラムしないとね
なんかすごい……
最後の引数は、特別な形式でファイルを開いたときだけのものだから 
NULL 
戻り値と、さっきの〈読み込んだサイズ〉をチェックするのね
成功してればどっちも0以外が返ってくるからね。成功してれば chAllLine
にファイル全体がまるごとそのままコピーされることになります
ファイルが失敗したら CloseHandle() ……って、さっきのだ
面倒な話なんだけど、こういう風にエラー処理が頻繁に出てくる時には、
後始末用の処理をその都度呼ばなきゃいけないんだよね
げ、ホントだ、あと2回も出てきてる!
これはランタイムの fclose() を使う時も同じ
ってことは、それ以外の場合はいいってこと?
そう、 iostream や MFC の場合には〈クラス〉使ってたでしょ
 std::ifstream と CStdioFile ね
クラスには〈後始末が必要なときに自動的に関数を呼び出すシステム〉が
備わってて、それを利用してファイルを閉じてるから
だからその都度後処理をしなくていいんだ
だけど関数のランタイムや API はそういうことできないから、いちいち
こういうふうにしなきゃいけないんだよね
んー、でもなんとかする方法とかないの?
あることはあるよ。たとえば関数にまとめるとか
どゆこと?
 CreateFile() のあとの処理を関数に入れる方法。で、関数から抜けたと
きに CloseHandle() するようにする

エラー処理はただ return するようにすれば、いつ return しても関数か
ら抜けたときに閉じられるようになるでしょ
そっか、どのエラーも return 呼ぶんだもんね
そう、鍵はそこ。うまく後処理をする部分へジャンプすればいいわけ
クラスの〈後処理機能〉も、そんな感じだもんね
で、ジャンプするための最後の手段が、 goto 
ごーつー?
そう。たとえば、とある行に

LABEL__CLOSE:

って書いておきます。で、エラー処理の時に

    goto LABEL__CLOSE;

ってすると、上の LABEL__CLOSE まで一気に飛ぶことができます
へー、そんな便利な物があるんだ
でもこれ、使っちゃダメ
ええ!?
 goto は for とかの流れをぐちゃぐちゃにしちゃうから、できる限り使
わないように
ならなんで教えるのよ〜
他の人のプログラム読むときに困るでしょ
まーそりゃそうだけど
とりあえず今のうちは、その都度エラー処理した方がいいかもね


/*
    Preview Next Story!
*/
 API 長くて複雑ねー
あ、でも次回は API じゃないよ?
あらそなの
でもそのあとさらに複雑になるけど
イヤーっ!!
というわけで次回
< Version 5.17 iostream で文字列操作! >
につづく!
というわけで std::strstream が登場します
あらまた STL & iostream 入門の登場ねー
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。