業務でMFCを使いながらC言語のfwriteを使わなければいけない機会があったのですが、その時少しハマったことがあったので備忘録として残しておきます。
この記事では、Windowsでfwriteを使ってバイナリ出力するとゴミデータが挿入される原因と対策について紹介します。
現象
例えば以下のようなバイナリデータをファイルに出力するとします。(1~20をバイナリにしただけのデータ)
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
何も考えずにfwriteを使って出力を行う場合以下のようなプログラムを書く人もいるかと思います。(VC++の記法で記載しています)
char data[20];
for (int i = 0; i < 20; i++) data[i] = i + 1;
FILE* fp = NULL;
::fopen_s(&fp, "test.dat", "w");
if (fp != nullptr)
{
::fwrite(data, 2, 20, fp);
::flose(fp);
}
上記のプログラムを実行後出力されたファイルを確認すると、何故かファイルサイズが21byrteになっていて、以下のように10の前にゴミ(13)が挿入されていました。
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
原因
Windowsでのプログラミングに詳しい方なら知っているかもしれませんが、Windows環境ではfopenする際にバイナリ指定(modeにb指定)をしないとfwriteするときに0x0a(10)の前に0x0d(13)が勝手につけられてしまうようです。
MS-DOSやWindowsの環境でバイナリのファイルをモードに”b”の指定をせずにテキストモードオープンしてfwriteすると、’\n’である0x0aのデータに’\r’である0x0dを付加するので注意が必要です。
http://www9.plala.or.jp/sgwr-t/lib/fwrite.html
対策
原因はバイナリモード指定をしていなかったことなので、対策としてはfopen時のmodeにwbを指定するようにします。
char data[20];
for (int i = 0; i < 20; i++) data[i] = i + 1;
FILE* fp = NULL;
::fopen_s(&fp, "test.dat", "wb");
if (fp != nullptr)
{
::fwrite(data, 2, 20, fp);
::flose(fp);
}
上記のプログラムを実行後出力されたファイルを確認すると、ファイルサイズは20byteで想定通り以下のデータが出力されました。
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
終わりに
Windowsでfwriteを使ってバイナリ出力するとゴミデータが挿入される原因と対策について紹介しました。
法則を見つけられればそんなにハマらなかったかもしれませんが、仕様を知らないと原因の特定ができないのでこの記事がどこかの誰かの助けになればと思います。
この記事について誤っている点・不明な点などありましたらコメントまでお願いします。
コメント