• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:C言語で.pgmの画像を作り、見る)

C言語で作成した.pgm画像の表示問題

このQ&Aのポイント
  • Windows10のコマンドプロンプトでC言語を使用して.pgm画像を生成したが、GIMPやIrfanViewで開けないという問題に直面している。
  • 作成したプログラムでは、正しい.pgm形式でファイルを出力しているはずだが、エラーメッセージが表示されてしまう。
  • 適切な.pgm画像の生成と表示方法についてのアドバイスを求めている。

質問者が選んだベストアンサー

  • ベストアンサー
  • luka3
  • ベストアンサー率72% (424/583)
回答No.1

MinGWで試してみましたが、IrfanView・GIMPともにエラーもなく正常に表示されました。 コンパイラの環境を確認してみてください。 こういった時のデバッグ方法はこんな感じでしょうか。 ・IrfanViewで適当にPGMファイルを保存し、自分のout.pgmとどのように違うかStirlingなどのバイナリダンプソフトで比べてみる ・テキストベースの P2 フォーマットを作成してみて内容を確認する あと個人的に気になったのは >fwrite(&j,sizeof(unsigned char),1,fp); j が int なのに対し、1バイト分の出力です。 もし同じプログラムをビッグエンディアンの環境で実行した場合は、真っ黒の画像になりますのでご承知おきください。

situmonn9876
質問者

お礼

デバッグ方法を教えてくださり、ありがとうございます。ネットを使いながら試していきたいと思います。

その他の回答 (2)

回答No.3

追記。 「バイナリモードでの書き込みはfwrite以外は使ってはいけません」と回答しましたが、ある条件を満たせばfprintfとfwriteを混ぜて使う事は可能です。 但し、混在させる場合は「ストリームバッファ」を意識して「おまじない」を行う必要があります。 その「おまじない」とは、fprintf使用後にfwriteを使う前に、fwrite使用後にfprintfを使う前に「fflushを呼び出す」という物です。 質問者さんのプログラムを修正すると以下のようになります。 #include<stdio.h> int main(void){ FILE *fp; unsigned char c; fp=fopen("out.pgm","wb"); fprintf(fp,"P5\n"); fprintf(fp,"#Image\n"); fprintf(fp,"256 256\n"); fprintf(fp,"255\n"); fflush(fp);/*おまじない*/ for(int i=0;i<256;i++){ for(int j=0;j<256;j++){ c=j; fwrite(&c,sizeof(c),1,fp); } } fclose(fp); return(0); } 以下、蛇足ですが。 fprintfがストリームバッファを使うか使わないか、fwriteがストリームバッファを使うか使わないか、は、決まりが無く、処理系に依存します。 どちらもストリームバッファを使わない実装をしている、どちらもストリームバッファを使う実装をしている、という場合、今回のような「実際の書き込み順序が前後してしまう」という現象は起きません(回答No.1の回答のように、何の問題も無く正常動作してしまいます) しかし、片方だけがストリームバッファを使う実装、もう片方が使わない実装をしているしている場合、fflushなどを使ってストリームバッファをファイルに吐き出すなど「おまじない」が必要になります。 このように「別のコンパイラでビルドしたらバグが出る」などの問題が起きるので「バイナリモードでの書き込みはfwrite以外は使ってはいけません」という話になるのです。ライブラリ関数を1種類だけしか使わなければ、どのコンパイラを使っても問題が起きることはありませんから。 今回は「コンパイラやライブラリが違うとバグが出たり出なかったりする」という、非常に厄介で発見し難い問題です。「他機種に移植したら何故かバグる」という、プログラマー泣かせの問題です。これで徹夜したプログラマーは大勢居ます。

回答No.2

バイナリモードでの書き込みはfwrite以外は使ってはいけません。 fprintf系の書き込みは、ストリーミング処理、テキストバイナリ変換が行われる可能性があり、思い通りのバイナリイメージで書き込まれる保証はありません。 また、データを1バイト書き込む際に fwrite(&j,sizeof(unsigned char),1,fp); と書くと、何が書き込まれるか保証できません。 intサイズが4バイト(32ビット)だった場合、&jのアドレスが指すメモリに何が入っているか判りません。 リトルエンディアンの場合とビックエンディアンの場合で、intの中のバイト並びが変わるからです。 リトルエンディアンの場合は「偶然、上手く行く」のですが、ビックエンディアンの場合「常に0しか書き込まない」です。 1バイトのデータを正しく書き込みたいなら unsigned char c; と、確実に1バイトサイズである事が保証された変数を用意し c=j; fwrite(&c,sizeof(c),1,fp); と書きましょう。 因みに、貴方が書いたプログラムでは 1.空のファイルが生成され、ファイルがオープンされる 2.fprintfで書かれたデータがストリームバッファに書き込まれる(ファイルには書き込まれない) 3.fwriteで書かれたデータがファイルに直接書き込まれる(しかも何が書き込まれるか保証されない) 4.fcloseによりストリームバッファに書き込まれたデータがファイルに書き込まれる 5.ファイルがクローズされる という処理がされ、最初に書かれるべきデータが、画像データの後ろに書き込まれてしまい、pgmファイルとは似ても似つかない物が出来上がります。 出来上がったpgmファイルを「バイナリダンプ」してみて、想定したバイナリデータが想定した通りに並んでいるか確認して下さい。最初に256×256のデータがあって、その後ろに P5 #image 256 256 255 というヘッダーが書かれている筈です。 fprintfとfwriteは絶対に混ぜて使わない、リトルエンディアンとビックエンディアンを意識してプログラミングして下さい。 久しぶりに「cで絶対にやっちゃいけないプログラム」を見ました(笑)

situmonn9876
質問者

お礼

多くの間違いを分かりやすく指摘して、解決策を教えていただきありがとうございます。

関連するQ&A