Windowsでfwriteを使ってバイナリ出力するとゴミデータが挿入される原因と対策

C言語

業務でMFCを使いながらC言語のfwriteを使わなければいけない機会があったのですが、その時少しハマったことがあったので備忘録として残しておきます。

この記事では、Windowsでfwriteを使ってバイナリ出力するとゴミデータが挿入される原因と対策について紹介します。

現象

例えば以下のようなバイナリデータをファイルに出力するとします。(1~20をバイナリにしただけのデータ)

12345678910
11121314151617181920

何も考えずに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)が挿入されていました。

1234567891310
11121314151617181920

原因

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で想定通り以下のデータが出力されました。

12345678910
11121314151617181920

終わりに

Windowsでfwriteを使ってバイナリ出力するとゴミデータが挿入される原因と対策について紹介しました。

法則を見つけられればそんなにハマらなかったかもしれませんが、仕様を知らないと原因の特定ができないのでこの記事がどこかの誰かの助けになればと思います。

この記事について誤っている点・不明な点などありましたらコメントまでお願いします。

コメント

タイトルとURLをコピーしました