CArchiveで余分な3bytes書き込みを防ぐには?

解決


Baby Chicken  URL  2009-05-07 10:59:42  No: 70112  IP: [192.*.*.*]

CArchive &arにおいて bytes dataを書き込もうとしています。
BYTE buffer[20]でbuffer[0]='P', buffer[1]='Q', buffer[2]=0x00と格納されている時に、
ar.WriteString((LPCTSTR)buffer)でファイルに書き込むと、
0x00 0x00 0x00 'P' 'Q'と何故か先頭に3bytesの0x00がついた5bytesが書き込まれます。
1)これはどうしてなのでしょうか?
2)これを防ぐにはどうすれば良いでしょうか?

編集 削除
tetrapod  2009-05-07 11:11:40  No: 70113  IP: [192.*.*.*]

結局のところ何がしたいのか、それ次第だったりするわけだけど・・・

やりたいこと=テキストファイルを作りたい、のであれば
そもそも CArchive を使うこと自体が間違い

やりたいこと=CArchive の動きを理解したい、のであれば
CArchive はデータをバイナリ形式で保存するためのクラスであるわけで
A1) それは CArchive の仕様
A2) CArchive を使う限り回避できない

編集 削除
Baby Chicken  2009-05-07 11:28:44  No: 70114  IP: [192.*.*.*]

早速のご回答ありがとうございます。やりたいことは、ある本を参考に、pgm or pbm (Portable Gray or Bit Map) fileという画像ファイルの表示・読み込み・書き込みソフトを作ることです。A2)ということであれば、仕方ありませんね。ただ、その場合、いちいちar << などとbyte毎に書き込まねばならないのでしょうか?
ご指導宜しくお願いします

編集 削除
tetrapod  2009-05-07 12:09:43  No: 70115  IP: [192.*.*.*]

CArchive は CArchive 形式(と便宜上呼ぶとして)でデータ保存・読み込みするためのクラス。
それ以外の形式でデータ保存するのであれば CArchive を使う理由は何一つ無い。
今回の話では、既に入出力フォーマットが決まっているわけなので CArchive は忘れてしまおう。

> byte毎に書き込まねばならないのでしょうか?
そうなるな。 ar<< という形式にはならないと思うけど。

PxM は実質ただのテキストなので (f)printf なり o(f)stream なりで出力できる

編集 削除
Baby Chicken  2009-05-07 15:49:37  No: 70116  IP: [192.*.*.*]

お忙しい中、度々ご回答ありがとうございます。ar << に拘っていすのは、MFCの isStoring() の部分で書き込もうとしているからですが、この部分でo(f)streamなどを用いることが問題無い、のでしょうか?  そうであれば、がんばってみたい思います。
それと、あの後、ひょっとして、と思い、試みました。0x00 3 bytesが付属するのは、WriteStringのためではありませんでした。どうやら、0x0Aを出力すると、その後の3 bytesが0x00が書き込まれるようです。ひょっとしてこれはUNICODEのためなのでしょうか?  今回の環境ではマルチバイトでプロジェクトを作成しているのですが・・・
何が何だか分からなくなりました。

編集 削除
Blue  2009-05-07 15:58:45  No: 70117  IP: [192.*.*.*]

>0x0Aを出力すると
とは?

ar << 0x0A;

ってこと? int型の値であれば、4バイト分出力されますけど。
(BYTEにキャストすれば1バイト分出力される)

編集 削除
Baby Chicken  2009-05-07 16:13:10  No: 70118  IP: [192.*.*.*]

早速のヒントありがとうございます。でも良く分からないのです。実際のコードは
char b[512];
_itoa_s(wColumns, b, 10, 10); // int wColumns = 256
// debuggerで見ると、b[0] = '2', b[1] = '5', b[2] = '6'
ar << 'P' << '5' << 0x0A << b[0] << b[1] << b[2];
でファイルに書き込んだところ、binary editorで見ると
'P' '5' 0x0A 0x00 0x00 0x00 '2' '5' '6'  というふうに書かれています。
宜しくお願いします

編集 削除
Baby Chicken  2009-05-07 16:16:51  No: 70119  IP: [192.*.*.*]

BLUE様、折角のヒントを理解せず申し訳ありませんでした。
ご指摘のように(BYTE)でキャストしたところ余分な0x00は書き込まれませんでした。ありがとうございます。
だとすると、WriteStringでもOKだと考えられます。
これからもご指導宜しくお願いします。

編集 削除
Blue  2009-05-07 16:31:50  No: 70120  IP: [192.*.*.*]

マルチバイトを前提に出力するのであれば、文字セットがどっちでも使えるように、WriteStringではなく、Writeを使って、2バイト出力とした方がベターだと思いますよ。
(char* を LPCTSTR にキャストするのはあまりいただけないので。)

ar.Write("P5", 2);

>_itoa_s(wColumns, b, 10, 10); 
も、sprintfの方が好きかも。

編集 削除
tetrapod  2009-05-07 17:02:07  No: 70121  IP: [192.*.*.*]

MFC を使う C++ ネイティブアプリケーションで SDI/MDI 形式、ということでおk?
開発環境は VC2003/2005/2008 それとも太古の VC++6 ?
っていうかこの手のことは最初に書いてほしいな。違っていると質疑応答全部無駄に終わってしまうので。

やり方ってのはいくつもあって、どれが正しいとか適切とかは状況次第だったりするわけだが

1) CArchive を使って保存・復元する方法
2) CArchive を使わず保存・復元する方法
俺なら 2) を選ぶと思うが 1) でも可能なわけで、その辺はお好きなように。

2) なら C***Doc::OnSaveDocument/OnOpenDocument をオーバーロードして入れ替えればいい。
CArchive を一切使う必要がなくなってむしろ簡単になると思われる。

1) にこだわるならまあそれでもいいけど・・・
でもシリアル化手順で PxM 形式出力してしまったのではスキーマの指定とかできなくなってしまい
シリアル化のうまみが失われてしまうと思うのだが。
この場合 PxM 形式の出力には WriteString だけを使うことになる。
int や long を << するとバイナリ出力されて PxM 形式にあわなくなる。

んで俺の手元の VS2005/VC++6 で CArchive::WriteString してみたけど
「それだけならば」ごみなバイトが勝手についたりはしないよ。
MFC ソースレベルでも余計なバイトを勝手に追記してたりはしないこと確認済み。
自分のプログラム中でなにか余計なこと・やってはならないことをしているに1票。

編集 削除
Baby Chicken  2009-05-07 23:41:20  No: 70122  IP: [192.*.*.*]

tetrapod様
数々のご指導ありがとうございます。正直内容が自分には高度すぎてついていけません・・・
最初の僕の質問がいなかったのだと思います。問題はar << 0x0A にあったのです。
0x0A という一バイトの値なのでarには一バイトが出力される、と誤認していたのです。でも実際にはBLUE様の言われるように、32 bitsのintが出力されていたのです。申し訳ありませんでした。僕の単なる早とちりでした。でも、これって初心者には紛らわしいと思いますが・・・・
何れにしても今後ともご指導宜しくお願いします

編集 削除