ども、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でした。