第54章 ファイルのススメ3

 ファイルに書き込むときには主に2通りの方法が使われます。1つは文字列を書き込み、書き込んだあとのファイルを読めるようにする方法、そしてもう1つはメモリ上のデータをそのまま書き込む方法です。今回はそのうち文字列を書き込む方法について話したいと思います。

 今回はファイル操作の重要な関数 fprintf について話します。printf と同じ感覚でファイルにデータを書き込んでいくことが出来ます。書き込んだあとのファイルがテキストとして読めるようにしたいときには、この fprintf を使うことになります。よく使いますので、使い方をマスターしましょう。


 では、今回の要点です。


 では、いってみましょう。


 先ず、ある処理を行いたいと思います。そうですね。入力された文字列を16進数にしてファイルに書き込むものにしましょう。

 今回ファイルの書き込みに使う関数は fprintf です。fprintf の使い方はほとんど sprintf や printf と同じです。第1引数に FILE* を追加で渡す以外は printf と同じです。

 数値を16進数で表示するときは、printf 系関数では %x という記号を使います(printf 系関数の簡単な書式については第24章を参照)。今回は、もう少し記号を複雑にしてみましょう。

 これを %02X とします。「2」は、最低2文字を出力するという意味です。このとき、2文字以下の時に空白を0で埋めるというのが「0」になります。x が大文字になっているのは、a〜fを大文字で出力するという意味です。こうすると、見た目がきれいになります。

 では、プログラムを見てみましょう。

プログラム
// File3.cpp
#include <stdio.h>
#include <string.h>

int main()
{
    FILE* pFile;
    char  buffer[512];
    int   i;

    printf("何か文字列を入力して下さい > ");
    gets(buffer);

    pFile = fopen("fprintf.txt", "w");
    fprintf(pFile, "%d\n", strlen(buffer));
    for(i = 0; buffer[i]; i++)
        fprintf(pFile, "%02X ", (unsigned char)buffer[i]);
    fclose(pFile);

    return 0;
}
実行結果
何か文字列を入力して下さい > 1234567890
fprintf.txt の中身
10
31 32 33 34 35 36 37 38 39 30

 テキストモードでやってみました。となると改行も入れたいので、文字列の長さも書き込むことにしました。では、プログラムを順を追って見ていきましょう。

 先ず、文字列の入力です。gets を使いました。gets は一行入力するときに使う関数です。改行は含まれません。バッファ(第52章参照)のサイズは考慮してくれませんので、バッファのサイズは大きめにとっておきました。これで先ず文字列を入力しました。

 次に、ファイルを開きます。書き込みモード&テキストモードにしました。つまり、アクセスフラグは "w" になります。ここまでは別に問題ありませんね。

 では、次はいよいよ書き込みです。

fprintf(pFile, "%d\n", strlen(buffer));

 strlen は文字列の長さを取得する関数です(第24章参照)。先ずは、これを10進数で書き込みます(10進数の記号は %d ですね)。この時、ついでに改行(\n)も行っておきました。まさに printf の時と同じ感覚でファイルに書き込むことが出来ました。

for(i = 0; buffer[i]; i++)
    fprintf(pFile, "%02X ", (unsigned char)buffer[i]);

 次でいよいよ16進数の書き込みです。unsigned char でキャスト(第21章参照)しているのは、char のままだと負の値が出てくるので、FFFFFFD4 などのような値が表示されることがあるからです。

 このキャストの注意点以外では、あまり特筆すべき所はありませんね。非常に簡単にファイルに書き込むことが出来ました。

 で、最後には開けたら閉めるの原則通り、fclose でファイルを閉じて、終了です。どうでしょうか? 大体感じはつかめましたでしょうか。


 では、このファイルを読み込んでみたいと思います。読み込む方法もいろいろありますが、一応 fscanf を使ってみたいと思います。

プログラム実行結果
// File4.cpp
#include <stdio.h>

int main()
{
    FILE* pFile;
    int   nLength;
    int   temp;
    int   i;

    pFile = fopen("fprintf.txt", "r");
    if(pFile == NULL)
      return 0;

    fscanf(pFile, "%d", &nLength);
    printf("文字列の長さは %d バイトです。\n", nLength);

    for(i = 0; i < nLength; i++)
    {
        fscanf(pFile, "%x", &temp);
        putchar(temp);
    }

    fclose(pFile);

    return 0;
}
文字列の長さは 10 バイトです。
1234567890

 先ずは、読み出しモード&テキストモードでファイルを開きます。

 で、その次に、ここでは第51章で言っていたエラー処理を行っています。ファイルが開けなかった場合には pFile は NULL になると言いました。なので、pFile が NULL の時はプログラムを終了するようにしました。

 その次が、いよいよファイルからの読み出しです。

fscanf(pFile, "%d", &length);

 書式が fprintf に似ていますね。これは scanf 系の関数の1つです。scanf 系の関数はいまいちエラーチェックが難しいので使い勝手が悪いのですが、ここでは使わせてもらいます。

 fscanf はファイルから数字を読み出し、それを数値にして変数に代入します。数字とは、数字の文字コードからなる文字列を表します。一方数値とは、char とか int とかそういうものの値を表します。ファイルから数字を読み出すときに、その数字を数値に変換してくれるのです。

 もちろん、数字を数値に変えて読み出すというのは、記号が %d だからです。これを %s にすれば文字列を、%x にすれば16進数を、%c にすれば1文字を読み出すことが出来ます。

 そして、fprintf と違う点が1つあります。それは、変数 length を渡すのではなく、そのアドレスを渡しているところです。length の中に値を代入するのですから、アドレスを渡すのは当然といえます(第34章参照)。間違って値を渡してもコンパイルエラーが起きるわけではありませんので、注意して使って下さい。

 続いて、文字コードの値を保存していたものを読み出します。

for(i = 0; i < length; i++)
{
    fscanf(pFile, "%x", &temp);
    putchar(temp);
}

 fscanf では空白、タブ、改行は無視されます。なので、次の数字 31 が読み出されます。31 は 31 でも、これを16進数での 31 として読み出します。この点以外では上と何ら変わりがないので、特に問題はないでしょう。

 次に putchar で文字を出力しています。putchar は文字コードを入れると、その文字を表示する関数です。さっき 31 という数字が 31 という16進数の数値に直され、temp に代入されました。なので、文字コード 0x31 の文字、'1' が表示されるわけです。

 で、最後にファイルを閉じて終了です。


 最後に一言。fscanf を教えたあとでこんな事言うのも何ですが、scanf 系の関数は癖があり、あまり好んで使われません。第52章で話したfgets(1行単位で読み出す関数)を使い、自前で解析していくのが安全です。ファイルは別プログラムで書き換えられる危険性がありますから、期待通りの書式になっていない可能性があります。そのとき、fscanf を使っていると思わぬ動作をしてしまう可能性があるのです。エラーチェックに脆弱な fscanf を使うことは、あまりないかもしれません。


 では、今回のおさらいです。


 次回はバイナリモードでの読み書きをやってみたいと思います。それでは次回まで。


第53章 バイナリとテキスト | 第55章 ファイルのススメ4

Last update was done on 2000.6.24

この講座の著作権はロベールが保有しています