ストリームにデータを読み書きする

ども、Norimakiです。

データを保存するという場合、ストリームを使用することが多くなります。

以前、話題に挙げたTVirtulaTreeViewについてもノードデータの保存は
ストリームに対して行います。

ということで、ストリームに関するデータのやり取りの備忘録をば。

通常、ストリームに対してデータのやり取りをする場合は、

書き込みは

Strsam.Write(Value,SizeOf(integer));

読み込みは

Strsam.Read(Value,SizeOf(integer));

なんて使います。

で、文字列のやり取りをする場合は、こんな感じ。

procedure NP_SetStream(Stream:TStream;Value:string);
var Len:Int64;
begin
 //-------------------------------
 // ストリーム書き込み
 //-------------------------------
 Len:=Length(Value);
 Stream.Write(Len,SizeOf(Len));
 Stream.Write(PChar(Value)^,Len*SizeOf(Char));
end;

procedure NP_GetStream(Stream:TStream;var aPt:String;Cnt:integer=-1);
var Len:Int64;
begin
 //-------------------------------
 // ストリーム読込
 //-------------------------------
 try
  Stream.Read(Len,SizeOf(Len));
  if Cnt<>-1 then if Len>Cnt then Len:=Cnt;
  if Len<0 then Len:=0;

  SetLength(aPt,Len);
  Stream.Read(PChar(aPt)^,Len*SizeOf(Char));
 except
 end;
end;

まぁ、色々と問題のあるコードかもしれませんが、
とりあえずこんな感じ。

まずは文字数を保存して、文字数分読み出したり書き込んだりすると。
そういう感じになってます。

現時点で僕が使用しているDelphiはXE4ですのでstringはunicodeです。
というわけで、バッファサイズはLen*SizeOf(Char) という感じになってます。

ここら辺は良くわかりませんが動くので....。

ということで済ませるのも良くないのでヘルプを見てみました。
ヘルプには、

Unicode(WideString)文字列の場合、Length は、
バイト数を 2 で割った値を返します。

なんて書かれているので、Len*SizeOf(Char)は Len*2 でも
いいんじゃなかろうか。なんて思ったりしています。

たしか、SizeOf(Char)は2だったはず。

で、型に応じてSizeOfとか記述するのも面倒くさい。
ということで、クラスヘルパーとoverloadを使って、

Stream.ReadUserData(変数);

とか

Stream.WriteUserData(変数);

とか出来たら便利じゃない?

便利でしょう。

と思ったわけで。で、こちら。

【宣言部】

 type

  TStreamHelper = class helper for TStream
   private
    procedure WriteString(Str:string);
    procedure ReadString(var Str:string;Cnt:integer=-1);

   public

    //文字列
    procedure WriteUserData(Value:string);                  overload;
    procedure ReadUserData(var Value:string;Cnt:integer=-1);overload;

    //整数型
    procedure WriteUserData(Value:integer);                  overload;
    procedure ReadUserData(var Value:integer);               overload;

    //boolean型
    procedure WriteUserData(Value:boolean);                  overload;
    procedure ReadUserData(var Value:boolean);               overload;

    //日付型
    procedure WriteUserData(Value:TDateTime);                overload;
    procedure ReadUserData(var Value:TDateTime);             overload;

    //Float型
    procedure WriteUserData(Value:double);                   overload;
    procedure ReadUserData(var Value:double);                overload;

    //Stream
    procedure WriteUserData(Value:TMemoryStream);            overload;
    procedure ReadUserData(var Value:TMemoryStream;
                  DefCnt:int64=-1);                          overload;
 end;

【実装部】

//(文字列)
procedure TStreamHelper.WriteUserData(Value:string);
begin
 WriteString(Value);
end;

procedure TStreamHelper.ReadUserData(var Value:string;Cnt:integer=-1);
begin
 ReadString(Value,Cnt);
end;


//(整数)
procedure TStreamHelper.WriteUserData(Value:integer);
begin
 Write(Value,SizeOf(Value));
end;

procedure TStreamHelper.ReadUserData(var Value:integer);
begin
 Read(Value,SizeOf(Value));
end;


//(boolean)
procedure TStreamHelper.WriteUserData(Value:boolean);
begin
 //Write(Value,SizeOf(Value));
 WriteUserData(Ord(Value));
end;

procedure TStreamHelper.ReadUserData(var Value:boolean);
var i:integer;
begin
 //Read(Value,SizeOf(boolean));
 ReadUserData(i);
 Value:=(i=Ord(True));
end;


//(日付型)
procedure TStreamHelper.WriteUserData(Value:TDateTime);
begin
 Write(Value,SizeOf(Value));
end;

procedure TStreamHelper.ReadUserData(var Value:TDateTime);
begin
 Read(Value,SizeOf(Value));
end;


//(Float型)
procedure TStreamHelper.WriteUserData(Value:double);
begin
 Write(Value,SizeOf(Value));
end;

procedure TStreamHelper.ReadUserData(var Value:double);
begin
 Read(Value,SizeOf(Value));
end;


//Stream
procedure TStreamHelper.WriteUserData(Value:TMemoryStream);
var Cnt:Int64;
begin
 Cnt:=Value.Size;
 Write(Cnt,SizeOf(Cnt));
 CopyFrom(Value,0);
end;

procedure TStreamHelper.ReadUserData(var Value:TMemoryStream
                                                 ;DefCnt:int64=-1);
var Cnt:Int64;
begin
 //ストリーム(Self)から読み込みストリーム(Value)に書き込む
 Read(Cnt,SizeOf(Cnt));           //サイズ読込

 if DefCnt<>-1 then
  if Cnt>DefCnt then Cnt:=DefCnt;

 Value.Clear;                                    //クリア
 if Cnt<>0 then Value.CopyFrom(Self,Cnt);        //ストリームに読込
 Value.Position:=0;                              //読込位置リセット
end;

こんな感じでどうでしょう。動くと思うんだけどなぁ。
実はまだ動かしてなかったりする。
ReadUserDataのストリームの部分を修正しました(2013/09/13)
⇒ Cntが0の時には読み込まないようにしました。

実際に動かしてみると、boolean型が上手く処理できていなかったようです。
ということで、コメントアウトしてちょっと対処しておきました。

何故うまくいかないかはよくわかりませんが、現時点での対症療法ということで。
小賢しいかもしれませんが、booleanを一時的にinteger型に変換して処理しています。

都合、現時点では6つの型だけを用意していますが、
必要に応じて増やすなりすればよろしいかと。

ではでは。
Norimakiでした。

シェアする

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

フォローする