下記のようにクラスでリストを作ってそれをファイルにバイナリで保存したり、読み込んだりしたいのですが。保存の仕方はこれでいいのでしょうか?また、読み込みかたがわからないのですが、どうしたらよいのでしょうか?
type
DataCss = class(TObject)
Text: String;
date: TDateTime;
end;
var
i: integer;
fs: TFileStream;
dataitem: DataCss;
datatemp: DataCss;
datalist: TList;
begin
datalist:=TList.Create;
for i:=0 to 10 do
begin
dataitem:=DataCss.Create;
dataitem.Text:='test';
dataitem.date:=now;
datalist.Add(dataitem);
end;
fs:=TFileStream.Create('c:\temp\test.dat', fmCreate);
for i:=0 to datalist.Count-1 do
begin
datatemp:=Pointer(datalist.Items[i]);
fs.WriteBuffer(datatemp, SizeOf(datatemp));
end;
datalist.Free;
fs.Free;
ReadBufferじゃないすか?
備考:私るるとん@Kは38.5度の熱があり休んでおります
こんにちわです。
とりあえずですが、
可変長のStringを扱うのであれば読み込みの効率的な方法は全く別物になります。固定長と考えてよろしいのでしょうか?
返信ありがとうございます。
>ReadBufferじゃないすか?
こんな感じでやってみたんですが読み込み違反とか出て動きませんでした。。
datalist:=TList.Create;
fs:=TFileStream.Create('c:\temp\test.dat', fmOpenRead);
for i:=0 to 10 do
begin
dataitem:=DataCss.Create;
fs.ReadBuffer(dataitem, SizeOf(dataitem));
datalist.Add(dataitem);
end;
>可変長のStringを扱うのであれば読み込みの効率的な方法は全く別物になります。固定長と考えてよろしいのでしょうか?
固定長というのはあらかじめ文字列の長さが決まっていてそれ以上は保持できないということでしょうか?メモコンポーネントに書いた文章なんかを保持したいので、可変長のほうがいいと思うのですがどうでしょう?
リストが可変長と仮定して説明します。
ストリームに書き込む前にリストの長さdatalist.Countを
格納しておきましょう。
cnt := datalist.Count;
fs.WriteBuffer(cnt, SizeOf(cnt));
で読み込むときリスト長を取得しておいて
fs.ReadBuffer(cnt, SizeOf(cnt));
それを使用してループすれば
for i:=0 to cnt-1 do
begin
datalist.Add();
datatemp:=Pointer(datalist.Items[i]);
fs.ReadBuffer(datatemp, SizeOf(datatemp));
end;
ループ分読みますので全部読み込めるでしょう
ただしスタテツさんの指摘のようにDataCssに可変長のstringを
使用するのであればこの方法は使えません。
DataCssを
type
DataCss = class(TComponent)
private
FText : string;
FDate : string;
publised
property Text: String read FText write FText;
property Date: TDateTime read FDate write FDate;
end;
というように定義しておけば書き込むとき
cnt := datalist.Count;
fs.WriteBuffer(cnt, SizeOf(cnt));
for i:=0 to datalist.Count-1 do
begin
fs.WriteComponent(TComponent(datalist.Items[i]));
end;
読み込むとき
fs.ReadBuffer(cnt, SizeOf(cnt));
for i:=0 to cnt-1 do
begin
datalist.Add();
fs.ReadComponent(TComponent(datalist.Items[i]));
end;
というようにできます。
TComponentから継承するとCreate時に引数要るのでnilでも渡しておきましょう
難しいですけどこの辺を理解できれば絶対に役に立ちますので
挑戦してはいかがでしょうか?
>メモコンポーネントに書いた文章なんかを保持したいので
ええぇ!?それなら
Memo1.Lines.SaveToFile('test.txt');
だけですよ・・・
読み込むなら
Memo1.Lines.LoadFromFile('test.txt');
です。
DelphiとVCLだと1行だけどVS.NETでやろうとしたら
結構プログラム書く必要あるので投げ出しましたよ・・・
可変長のデータで数メガバイト程度のデータでよければ
カンマ区切りテキスト形式をTStringListで全データを読み込むのが一番手っ取り早くて簡単です。(これなら簡単なサンプル付けられます)
ただしこの方法ですとファイルを丸ごとメモリーに取り込むので巨大なデータを扱うことはできません。
巨大データを扱うのであればデータベースを使用するのが簡単です。
ちなみに非データベースであれば個々のデータのサイズを別途保持する必要が出てきます。読み込みも書き込みも数行で書けるような方法は無いとおもいます。
すいません、少し説明不足でした。
簡単に言うと、文章ごとに日付などの属性をつけて管理する、簡単なデータベースっぽいものを作ろうとしてまして。そんなに膨大なデータを扱うことは想定していないのでリストで簡単に作れないかと思ったのですが。もしかしてデータベースでやった方が簡単ですかね?
> もしかしてデータベースでやった方が簡単ですかね?
それはアプリケーションにもよると思います。
DBを使うメリットは必要に応じての登録・修正・削除・閲覧が比較的容易で、DBエンジンに任せっぱなしに出来る点に尽きると思いますが、反面、アプリケーションの配布を考慮するならライセンス等に気を配らないとならないと思います。
また、有料バージョンのDelphiじゃないとDBは使えないと思います。
>また、有料バージョンのDelphiじゃないとDBは使えないと思います。
あー、僕が持ってるのはDelphi6だけですからだめですね。。。
すみません、takeさんのを参考にしてみたのですが、よくわからなかったので過去ログを参考にメンバ変数を1つずつ取り出して保存していったら、うまくいきました。読み込みもできました。
書き込み
cnt := datalist.Count;
fs.WriteBuffer(cnt, SizeOf(cnt));
for i:=0 to datalist.Count-1 do
begin
datatemp:=Pointer(datalist.Items[i]);
ttext:=datatemp.Text;
tsize:=Length(ttext);
tdate:=datatemp.date;
fs.WriteBuffer(tsize, SizeOf(tsize));
fs.WriteBuffer(PChar(ttext)^, Length(ttext));
fs.WriteBuffer(tdate, SizeOf(tdate));
end;
読み込み
fs.ReadBuffer(cnt, SizeOf(cnt));
for i:=0 to cnt-1 do
begin
dataitem:=DataCss.Create;
fs.ReadBuffer(tsize, SizeOf(tsize));
SetLength(ttext,tsize);
fs.ReadBuffer(PChar(ttext)^, tsize);
fs.ReadBuffer(tdate, SizeOf(tdate));
dataitem.Text:=ttext;
dataitem.date:=tdate;
datalist.Add(dataitem);
end;
解決したそうで何よりですが…
後でログを見た人が混乱しないためにも。
上のソースで気になるところがあります。
結局テキストは固定長にしたということでいいのでしょうか?
読み込みのcntはどうやって求めてるのでしょうか?
フルソースで無いのでなんともいえませんが。
上記ソースでは未解決のように思われます。
>結局テキストは固定長にしたということでいいのでしょうか?
えーと、よくわからないのですが、これは可変長ではないのでしょうか?僕がやろうとしていたことはこれでできそうなので問題はないのですが。。
>読み込みのcntはどうやって求めてるのでしょうか?
書き込むときに先頭にcntの値を書き込んでありますから、それを読み込んで求めてます。
文字列と日付だけ読み書きしたいんにゃら、record配列の方が管理が簡単にゃ〜
にゃんで ClassとTList使うんかにゃ〜
type
TDataCss = packed record
Text : string;
Date : TDateTime;
end;
TAryDataCss = array of TDataCss;
//書き込みにゃ〜
procedure TForm1.SaveButtonClick(Sender: TObject);
var
fs: TFileStream;
i, Cnt, Len : Integer;
AryDataCss : TAryDataCss;
begin
for i:=0 to 10 do begin
SetLength(AryDataCss, Succ(i));
AryDataCss[i].Text := 'テストするにゃ〜';
AryDataCss[i].Date := Now + i;
end;
fs := TFileStream.Create('test.dat', fmCreate);
Cnt := Length(AryDataCss);
fs.Write(Cnt, SizeOf(Integer));
for i:=0 to Cnt-1 do begin
Len := Length(AryDataCss[i].Text);
fs.Write(Len, Sizeof(Integer));
fs.Write(AryDataCss[i].Text[1], Len);
fs.Write(AryDataCss[i].Date, Sizeof(TDateTime));
end;
fs.Free;
end;
//読み込みにゃ〜
procedure TForm1.LoadButtonClick(Sender: TObject);
var
fs : TFileStream;
i, Cnt, Len : Integer;
AryDataCss : TAryDataCss;
begin
fs := TFileStream.Create('test.dat', fmOpenRead);
fs.Read(Cnt, SizeOf(Integer));
for i:=0 to Cnt-1 do begin
SetLength(AryDataCss, Succ(i));
fs.Read(Len, SizeOf(Integer));
SetLength(AryDataCss[i].Text, Len);
fs.Read(AryDataCss[i].Text[1], Len);
fs.Read(AryDataCss[i].Date, Sizeof(TDateTime));
Memo1.Lines.Add(AryDataCss[i].Text);
Memo1.Lines.Add(DateTimeToStr(AryDataCss[i].Date));
end;
fs.Free;
end;
いちお個人的には解決したんで解決にしときます。
ツイート | ![]() |