yi01.memo
2008-01-14
最終更新:
yi01memo
-
view
C#でPGM形式の画像ファイルをバイナリで作成するときにつまずきやすい点
アスキーモードならすんなりいくのに・・・
PGMの画像形式はアスキーモードでは
P2\n#コメント\n
640 480\n
255\n
(データ 1行最大70字、区切り文字はホワイトスペース)
で、初心者でもすぐに書けると思います。
僕自身、授業でCでやったときはここでつまずくことはありませんでした。C#でやってみても、こっちはすんなりいきました。
僕自身、授業でCでやったときはここでつまずくことはありませんでした。C#でやってみても、こっちはすんなりいきました。
バイナリモード?
PGMの画像形式はバイナリモードでは
P5\n#コメント\n
640 480\n
255\n
(データ 区切り文字はいらない)
そもそも「バイナリモード」とか「バイナリファイル」って何?ってところから始まったわけですが、
Cならファイルのオープンの時に"r"→"rb"、"w"→"wb"みたいにbをつけるだけで、
C#ならStreamWriterのかわりにBinaryWriterを使うだけで、
ほとんど同じ操作でこの「バイナリファイル」なるものを作れるようです。
バイナリファイルはテキストエディタじゃ読めないので、バイナリエディタとかいうカテゴリに入るようなソフトをダウンロードしてくる必要があります。僕はStirlingとかいうのを使いました。
Cならファイルのオープンの時に"r"→"rb"、"w"→"wb"みたいにbをつけるだけで、
C#ならStreamWriterのかわりにBinaryWriterを使うだけで、
ほとんど同じ操作でこの「バイナリファイル」なるものを作れるようです。
バイナリファイルはテキストエディタじゃ読めないので、バイナリエディタとかいうカテゴリに入るようなソフトをダウンロードしてくる必要があります。僕はStirlingとかいうのを使いました。
バイナリモードの罠
C#のBinaryWriterのWriteメソッドにはいくつか厄介な点があります。そこではめられ、3時間を無駄にしてしまいました。
int→stringを勝手にするんじゃない
640 480\n
255\n
この部分は、普通に
BinaryWriter bw=new BinaryWriter(fstream);
...(略)
bw.Write(width + ' ' + height+'\n');
bw.Write(255);
とすればいけましたが、(というか、こうしないといけませんが)
データ部で
データ部で
bw.Write(val);
とすると、valが文字列として書き込まれてしまいます。
bw.Write(val.ToString());
として実行しても結果が変わらないことから、わかると思います。
今の場合、画素値は明らかに0~255なので、byteに収まる値なので、
bw.Write(val.ToString());
として実行しても結果が変わらないことから、わかると思います。
今の場合、画素値は明らかに0~255なので、byteに収まる値なので、
bw.Write((byte)val);
と単純にキャストするのが正解です。
bw.Write(BitConverter.GetBytes(val));
ではうまくいきません。intは32ビットなのでbyte[4]に変換されてしまいます。
bw.Write(BitConverter.GetBytes(val));
ではうまくいきません。intは32ビットなのでbyte[4]に変換されてしまいます。
stringの文字数を書き込むんじゃない!
意外と「P5」を書き込むのにコケます。
bw.Write("P5");
と普通に書き込むと、実際には「02P5」をエンコードしたものが書き込まれてしまいます。
Encoding.Unicode.GetBytesでもうまくいきません。これだと一文字おきに「01」(1文字であることを表すバイト)が挿入されてしまい、さらにたちの悪いことになります。
ただ、これを
Encoding.Unicode.GetBytesでもうまくいきません。これだと一文字おきに「01」(1文字であることを表すバイト)が挿入されてしまい、さらにたちの悪いことになります。
ただ、これを
bw.Write('P');
bw.Write('5');
とすればうまくいきます。ここからヒントを得るならば、一般にstring型の変数strを書き込む
暫定的な正解は以下のようなもののようです。
暫定的な正解は以下のようなもののようです。
for(int i = 0; i < str.Length; i++) bw.Write(str[i]);