バイナリのBlockWrite方法

解決


yTake  2015-04-30 09:28:33  No: 47242

yTakeです。
度々お世話になりっています。
今回は基本的なことですが、バイナリーデータをファイルへ書き出す方法に付いてです。

"BlockWrite"を使いたいと思います。次ぎの2例を試しましたが、いずれも思う様に動作していません。

テスト1は、WordデータをByteに分割してバイト配列に格納し、"BlockWrite"で書き出します。

テスト2は、Wordデータをそのままワード配列に格納し、2Byte単位で"BlockWrite"で書き出します。

procedure test1();
var  
  wid, hei, dpx, dpy, cnt : Word;
  buf : Array of Byte;
  fname : String;
  bf : File;
begin
  wid := getTAG( $256 );
  hei := getTAG( $257 );
  dpx := getTAG( $258 );
  dpy := getTAG( $259 );
  SetLength( buf, 20 );
  AssignFile( bf, filename );
  ReWrite( bf, 1 );
  buf[  0 ] :=  0;             buf[  1 ]  :=  0;
  buf[  2 ] :=  $00FF and wid; buf[  3 ]  :=  $00FF and ( wid shr 8 );
  buf[  4 ] :=  $00FF and hei; buf[  5 ]  :=  $00FF and ( hei shr 8 );
  buf[  6 ] :=  0;             buf[  7 ]  :=  0;
  buf[  8 ] :=  $00FF and dpx; buf[  9 ]  :=  $00FF and ( dpx shr 8 );
  buf[ 10 ] :=  $00FF and dpy; buf[ 11 ]  :=  $00FF and ( dpy shr 8 );
  buf[ 12 ] :=  $00FF and wid; buf[ 13 ]  :=  $00FF and ( wid shr 8 );
  buf[ 14 ] :=  $00FF and hei; buf[ 15 ]  :=  $00FF and ( hei shr 8 );
  buf[ 16 ] :=  0;             buf[ 17 ]  :=  0;        //  EPSON MAXD
  buf[ 18 ] :=  0;             buf[ 19 ]  :=  0;

  BlockWrite( bf, buf, 20, cnt );
  CloseFile( bf );
end;

procedure test2();
var  
  wid, hei, dpx, dpy, cnt : Word;
  buf : Array of Byte;
  fname : String;
  bf : File;
begin
  wid := getTAG( $256 );
  hei := getTAG( $257 );
  dpx := getTAG( $258 );
  dpy := getTAG( $259 );
  SetLength( buf, 10 );
  AssignFile( bf, filename );
  ReWrite( bf, 2 );
  buf[ 0 ] :=  0;             
  buf[ 1 ] :=  wid; 
  buf[ 2 ] :=  hei; 
  buf[ 3 ] :=  0;
  buf[ 4 ] :=  dpx; 
  buf[ 5 ] :=  dpy; 
  buf[ 6 ] :=  wid; 
  buf[ 7 ] :=  hei; 
  buf[ 8 ] :=  0;             
  buf[ 9 ] :=  0;             

  BlockWrite( bf, buf, 10, cnt );
  CloseFile( bf );
end;

wid, heiは"592"、dpx, dpyは"150"なのですが、
この結果をバイナリエディタで確認すると、全く違うデータが書き込まれています。

何か使い方に間違いがあるからと思いますが、よく分かりません。

どなたか、ご教授願えればと思います。


通りすがり  2015-04-30 15:17:05  No: 47243

なんでStream系のクラスを使わないのか?というのは置いといて、
まずBlockWriteを呼び出す時点でbufに正しい値が入っているかどうか確認してみてはいかがですか?
あとはBlockWriteの第2パラメータはbufではなくbuf[0]ではないかと。
んでもってtest2のbufがarray of Byteになってますけどarray of Wordでは?

いずれにせよもう古いファイル入出力ルーチンを使うのはやめましょう。


通りすがり  2015-04-30 17:41:23  No: 47244

型無しvarパラメータに動的配列を渡すと、予期せぬ結果を引き起こす
ttp://srgia.com/docs/dbl/DelphiBugList0567.html


yTake  2015-05-01 16:32:50  No: 47245

通りすがりさん
ありがとうございました。
大変参考になりました。

動的配列を使っていると言う意識が希薄でした。
BlockWriteの第2パラメータはbufではなくbuf[0]で解決です。
また、test2のbufがarray of Byteになってますけどarray of Wordでした。
実は、元々TFileStreamで始めたのですが、うまく行かず、色々試した中で上記をアップさせて頂いた次第です。
TFileStreamでも、buf[0]とする事で取り合えずうまく行きました。
ただ、納得が行かない点がありました。

動的配列は
buf : Array of Word;
としました。
  wid := getTAG( $256 );
  hei := getTAG( $257 );
  dpx := getTAG( $258 );
  dpy := getTAG( $259 );
  buf[ 0 ] :=  0;             
  buf[ 1 ] :=  wid; 
  buf[ 2 ] :=  hei; 
  buf[ 3 ] :=  0;
  buf[ 4 ] :=  dpx; 
  buf[ 5 ] :=  dpy; 
  buf[ 6 ] :=  wid; 
  buf[ 7 ] :=  hei; 
  buf[ 8 ] :=  0;             
  buf[ 9 ] :=  0;             
  SetLength( buf, 10 );
  fs  :=  TFileStream.Create( filename, fmCreate );
  cnt :=  fs.Write( buf[ 0 ], 20 );
  fs.Free();
で、全てのbufデータがファイルに期待通りに書き出されます。

疑問は、fs.Write( buf[ 0 ], 20 )のところです。
bufはwordの配列として定義したので、fs.Write( buf[ 0 ], 10 )で良いはずと思うのですが、これではbufの半分までしか書き出されません。

最終的にBlockWriteでうまくいった時は、
buf : Array of Byte
の時は、
BlockWrite( bf, buf[ 0 ], 20 )
buf : Array of Word;
の時は、
BlockWrite( bf, buf[ 0 ], 10 )
で、全てのbufデータがファイルに下記出されています。

考えられる相違は、TFileStreamで書き出しの単位の指定の方法が分かっていません。
BlockWriteの時は、ReWrite( bf, "書き出し単位" )と明示的に指定されました。

お蔭様でストリームで動作としてはOKとなりました。

ストリームでの書き出し単位いついてご教授頂ければ幸いです。


通りすがり  2015-05-01 17:57:23  No: 47246

ストリームを使ってデータの読み書きを行う
http://docwiki.embarcadero.com/RADStudio/XE8/ja/%E3%82%B9%E3%83%88%E3%83%AA%E3%83%BC%E3%83%A0%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E8%AA%AD%E3%81%BF%E6%9B%B8%E3%81%8D%E3%82%92%E8%A1%8C%E3%81%86

少なくとも
Delphi 5,6 のヘルプにもWriteメソッドの説明に「Count バイトを書き込む」と
の記載がありますが、ヘルプとかは、ご覧になっておられないのでしょうか?


yTake  2015-05-03 10:01:13  No: 47247

通りすがりさん
ありがとうございます。

質問の意図が分かり難くてすみません。書き込みするバイト数の事ではありません。TFileStreamで書き込みの際の書き込み単位(表現がこれで良いか分かりません)

例えば、BlockWriteの例では、Byte配列の場合は書き込み単位はバイトになりますので、20バイトを書き込む場合には、BlocjWrite( buf[0], 20 )としますが、Word配列とした場合では、20バイトを書き込む場合にはBlockWrite( buf[0], 10 )となると思います。その際、ReWriteで、各々1、2を指定して書き込み単位を明示的に設定しています。

これと同様の書き込み最小単位の設定がTFileStreamでもある「のでしょうか?と言うのが、問い合わせの意図でした。
その様な設定はなく、書き込み最小単位は常にバイトと言う事ですね。

以下は本題とは関係ありませんが、
もちろん、ヘルプやweb上を探したりしています。
ただ、これまでヘルプはあまり役に立っていない様に感じます。
今回も、TFileStreamを当たってみましたが、"TFileStream"の他に"TFileStream.Create", "TFileStream.Destroy", "TFileStream.FileName"の3つしかみあたりません。
また、文字定数の意味を知りたいと思いヘルプを参照しても、例えば、TFileStream関連では、オープン時の"fmCreate"や"fmOpenWrite"などの種類と意味を調べたのですが、ヘルプには乗っていませんね。
XE3のメニューのヘルプから"DELPHIヘルプ"を呼び出しました。このヘルプとは別にあるのでしょうか?

参照にご案内頂けたオンラインヘルプが最も頼りになりそうです。

ネットなどはもっと良く調べればみつかったであろうとは思います。

ありがとうございました。


最初の通りすがり  2015-05-03 15:20:31  No: 47248

SizeOfを使いましょう。
fs.Write(buf[0],Length(buf) * SizeOf(Buf[0]));

あとステハンといえど紛らわしいのでそのあたりは配慮して>2番目以降の通りすがりさん


yTake  2015-05-03 20:41:07  No: 47249

yTakeです
閉じた後でスミマセン。

本スレッドでは通りすがりさんは二人おられたのですね。
気付きませんでした。(普通気が付きませんよね)

最初の通りすがりさん、2番目の通りすがりさん、ありがとうございました。

結論としましては、書き込みの最小単位はコーディング者が具体的に知る必要はなく、最小単位を取得して関数等に渡せられればOKと言う事ですね。

ありがとうございました


通りすがり  2015-05-08 09:03:23  No: 47250

> 例えば、TFileStream関連では、オープン時の"fmCreate"や"fmOpenWrite"などの種類と意味を調べたのですが、ヘルプには乗っていませんね。

???
http://docwiki.embarcadero.com/RADStudio/XE3/ja/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB_%E3%82%B9%E3%83%88%E3%83%AA%E3%83%BC%E3%83%A0%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9

http://docwiki.embarcadero.com/Libraries/XE3/ja/System.Classes.TFileStream.Create

XE3の環境は手元にないけれど、TFileStream.Createに説明ありますよ。

3人目の通りすがり?
ステハン使う時点で配慮もへったくれもありませんwww


最初の通りすがり  2015-05-08 10:00:48  No: 47251

俺にじゃなくて質問者にだっての。アホだな。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加