リストの保存と読み込み

解決


mugen  2004-04-30 18:02:09  No: 8773

下記のようにクラスでリストを作ってそれをファイルにバイナリで保存したり、読み込んだりしたいのですが。保存の仕方はこれでいいのでしょうか?また、読み込みかたがわからないのですが、どうしたらよいのでしょうか?

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;


るるとん@K  2004-04-30 18:58:31  No: 8774

ReadBufferじゃないすか?
備考:私るるとん@Kは38.5度の熱があり休んでおります


スタテツ  2004-04-30 19:53:06  No: 8775

こんにちわです。
とりあえずですが、
可変長のStringを扱うのであれば読み込みの効率的な方法は全く別物になります。固定長と考えてよろしいのでしょうか?


mugen  2004-04-30 20:21:31  No: 8776

返信ありがとうございます。

>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を扱うのであれば読み込みの効率的な方法は全く別物になります。固定長と考えてよろしいのでしょうか?

固定長というのはあらかじめ文字列の長さが決まっていてそれ以上は保持できないということでしょうか?メモコンポーネントに書いた文章なんかを保持したいので、可変長のほうがいいと思うのですがどうでしょう?


take  2004-04-30 20:31:46  No: 8777

リストが可変長と仮定して説明します。
ストリームに書き込む前にリストの長さ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でやろうとしたら
結構プログラム書く必要あるので投げ出しましたよ・・・


スタテツ  2004-04-30 20:33:23  No: 8778

可変長のデータで数メガバイト程度のデータでよければ
カンマ区切りテキスト形式をTStringListで全データを読み込むのが一番手っ取り早くて簡単です。(これなら簡単なサンプル付けられます)
ただしこの方法ですとファイルを丸ごとメモリーに取り込むので巨大なデータを扱うことはできません。
巨大データを扱うのであればデータベースを使用するのが簡単です。
ちなみに非データベースであれば個々のデータのサイズを別途保持する必要が出てきます。読み込みも書き込みも数行で書けるような方法は無いとおもいます。


mugen  2004-04-30 22:10:10  No: 8779

すいません、少し説明不足でした。

簡単に言うと、文章ごとに日付などの属性をつけて管理する、簡単なデータベースっぽいものを作ろうとしてまして。そんなに膨大なデータを扱うことは想定していないのでリストで簡単に作れないかと思ったのですが。もしかしてデータベースでやった方が簡単ですかね?


吉岡  2004-04-30 22:36:58  No: 8780

> もしかしてデータベースでやった方が簡単ですかね?

それはアプリケーションにもよると思います。
DBを使うメリットは必要に応じての登録・修正・削除・閲覧が比較的容易で、DBエンジンに任せっぱなしに出来る点に尽きると思いますが、反面、アプリケーションの配布を考慮するならライセンス等に気を配らないとならないと思います。
また、有料バージョンのDelphiじゃないとDBは使えないと思います。


mugen  2004-05-01 01:22:48  No: 8781

>また、有料バージョンのDelphiじゃないとDBは使えないと思います。

あー、僕が持ってるのはDelphi6だけですからだめですね。。。


mugen  2004-05-01 01:39:12  No: 8782

すみません、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;


スタテツ  2004-05-01 03:07:16  No: 8783

解決したそうで何よりですが…
後でログを見た人が混乱しないためにも。

上のソースで気になるところがあります。
結局テキストは固定長にしたということでいいのでしょうか?
読み込みのcntはどうやって求めてるのでしょうか?
フルソースで無いのでなんともいえませんが。
上記ソースでは未解決のように思われます。


mugen  2004-05-01 17:34:03  No: 8784

>結局テキストは固定長にしたということでいいのでしょうか?

えーと、よくわからないのですが、これは可変長ではないのでしょうか?僕がやろうとしていたことはこれでできそうなので問題はないのですが。。

>読み込みのcntはどうやって求めてるのでしょうか?

書き込むときに先頭にcntの値を書き込んでありますから、それを読み込んで求めてます。


にゃ〜  2004-05-01 21:22:40  No: 8785

文字列と日付だけ読み書きしたいんにゃら、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;


mugen  2004-05-03 17:46:55  No: 8786

いちお個人的には解決したんで解決にしときます。


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

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






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