ファイルの入出力について

解決


チャーリー  2004-11-12 19:49:30  No: 11740  IP: [192.*.*.*]

こんにちは。
TObjectListとTStringListを使って、ファイルを入出力したいのですが、
StringGridのデータが、以下の場合、*2の部分だけが、登録されてしまいます。
------------
*1
Bamei := 'はるうらら';
Comment := 'はるが好き';
-----------
*2
Bamei:= 'はいせいこー';
Comment := 'わからん';
------------
*1*2続けてファイルに入力するにはどうすればいいのでしょうか。


TComment=class(TObject)
  Bamei:string;//馬名
  Comment:string;//コメント
end;
------
var
  TC:TComment;
  sl:TStringList;
  nTosu,Code,i:integer;
begin
  TC:=TComment.Create;
  sl := TStringList.Create;
  try
    val(den1.TorokuTosu,nTosu,Code);
    for i:=0 to nTosu-1 do begin
      if(StringGrid2.Cells[10,i+1]<>'')then begin//コメントが空白でなければ
        TC.Bamei:=StringGrid2.Cells[3,i+1];
        //Showmessage(TC.Bamei);
        TC.Comment:=StringGrid2.Cells[10,i+1];
        //Showmessage(TC.Comment);
        ol.Add(TC);//登録
      end;
    end;
    for i := 0 to ol.Count-1 do begin
      sl.Add(TComment(ol[i]).Bamei);
      sl.Add(TComment(ol[i]).Comment);
    end;
    sl.SaveToFile('c:\TestBamei.txt');
  finally
    sl.Free;
    TC.Free;
  end;

end;

編集    削除
にしの  2004-11-12 21:45:00  No: 11741  IP: [192.*.*.*]

ol[0]とol[1]が同じインスタンスになっています。
forの中でcreateすべきです。
解放も、ol[0],ol[1]それぞれ解放しなければ行けません。
# 今の仕様だと、ol[0]を解放した後ol[1]を解放するときエラーになりませんか?

編集    削除
チャーリー  2004-11-12 23:34:35  No: 11742  IP: [192.*.*.*]

こんにちは。

for i
  TC:=TComment.create;
  *処理1
end;
sl:=TStringList.Create;
try
  *処理2
finally
  sl.free;
end;
解放はTStringListだけ記入したのですが、エラー出ずに登録できるようになりました。ただ、新しいデータを登録しようとすると、ボタンを押すたびにデータが削除されて蓄積されません。

編集    削除
HOta  2004-11-13 03:18:06  No: 11743  IP: [192.*.*.*]

チャーリーさん、こんにちは。

以前の質問と同じですね。
既にデータがある場合と、無い場合を分けて考えましょう。

編集    削除
チャーリー  2004-11-13 18:27:43  No: 11744  IP: [192.*.*.*]

同じところをうろうろしてます。同じくファイルを開けるところで以前と同じエラーが出ているのですが、TStringList.SaveToStreamで、TFileStream.Seekを加える場合どうしたらいいのでしょうか。
TStringListのSaveToFileでする場合、ファイルがある場合とない場合でできますか?
-----------------------------------
ファイルがあってもなくても最初は登録できますが、次登録しようとするとエラーが出ます。
if(FileExists(Filename))then begin
  sl.SaveToStream(TFileStream.Create(Filename,fmOpenReadWrite))
else
  sl.SaveToStream(TFileStream.Create(Filename,fmCreate));

編集    削除
HOta  2004-11-13 18:57:00  No: 11745  IP: [192.*.*.*]

チャーリーさん、こんにちは。

あれ?質問の最初の時はSaveToFileだったのに、変更したのですか?

編集    削除
つっか  2004-11-13 20:47:32  No: 11746  IP: [192.*.*.*]

データの追加に難儀してますね。

論理を単純にすると本質が見えてくることが多いです。

ファイルにデータを追加して行くのに最も効率がいいのは TFileStream を
使うことです。データが文字列だとして、一行づつ追加していく例を示します。

var
  counter:integer;

procedure TForm1.Button1Click(Sender: TObject);
var
  fs:TFileStream;
  fn,s:string;
begin
  fn := 'c:\TestData.txt';
  if FileExists(fn) then
    fs := TFileStream.Create(fn,fmOpenReadWrite or fmShareDenyWrite)
  else
    fs := TFileStream.Create(fn,fmCreate or fmShareDenyWrite);
  try
    s := 'data : '+IntToStr(counter)+#13#10; // 改行を入れておく
    Inc(counter);
    fs.Seek(0,soFromEnd);// ファイルの末尾にシークして移動
    fs.Write(PChar(s)^,Length(s)); // データを追加
  finally
    fs.Free;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Memo1.Lines.LoadFromFile('c:\TestData.txt');// モニタ
end;

Button1 を押すたびに1行ずつ増えていくのが Button2 を押すと見えるでしょう。

TStringList をつかうと、全部読み込んで1行追加し、それから全部上書きすること
になりコードは簡単ですが、効率は良くありません。こんな感じです。

procedure TForm1.Button3Click(Sender: TObject);
var
  sl:TStringList;
  fn,s:string;
begin
  fn := 'c:\TestData.txt';
  sl := TStringList.Create;
  try
    if FileExists(fn) then sl.LoadFromFile(fn);
    s := 'data : '+IntToStr(counter);
    Inc(counter);
    sl.Add(s); // データ追加。この場合は改行は自動的につく
    sl.SaveToFile(fn);
  finally
    sl.Free;
  end;
end;

やはり、Button3 を押してから Button2 を押して一行ずつ増えていくのが見てえるでしょう。


繰り返しますが、やりたいことの本質を見極めるには論理を単純化するといいです。

編集    削除
B  2004-11-16 09:00:12  No: 11747  IP: [192.*.*.*]

つっかさんこんにちは。
横からすいません。テキストファイルの入出力に自分は、readln,writelnを使っていますが、TFileStreamの方が効率が良いのでしょうか?

編集    削除
つっか  2004-11-16 10:07:59  No: 11748  IP: [192.*.*.*]

AssignFile, Reset, Readln, Writeln, CloseFile などは、古い Pascal
スタイルのファイルアクセス方法ですね。利点は、多分高速で、しかも
OS に依存しない事でしょう。でも事実上 Delphi は Windows 専用です
けどね。

これに対して、Windows API のファイルアクセスをラップした FileOpen,
FileCreate, FileRead, FileWrite, FileSeek, FileClose などの関数も
あります。これらはあまり使われませんが、THandleStream,TFileStream 
の実装に使われています。VCL のストリーミングも TFileStream を使って
います。Windows で便利にランダムアクセスするには、TFileStream の方
が圧倒的に便利です。そして、多分、AssignFile などから比較しても
そんなに速度が落ちるわけでもないと思います。

クラスベースのプログラミングでは TFileStream を使っていくべきだ、と
思っています。

編集    削除
つっか  2004-11-16 10:30:21  No: 11749  IP: [192.*.*.*]

テキストファイルに限定すると Readln Writeln は行単位にアクセスできて
便利ですね。これらに相当するメソッドは TFileStream にはありませんし。
簡単につくることは出来ます。

一方、TStringList のように全部読み込んでから操作する場合は、行単位に
アクセスするのも簡単ですし、3行目を削除する、7行目に1行挿入する、
といった行単位の変更が簡単にできます。

このスレッドの質問では既存ファイルの末尾に1行テキストを追加する場合
ですから、簡単にシークできる TFileStream を使いました。

要は、テキストファイルにどのような操作を行いたいかにより使い分けると
いいです。

一般論としては、TFileStream が最も低レベルで便利だと思います。

編集    削除
B  2004-11-17 09:20:04  No: 11750  IP: [192.*.*.*]

つっかさん丁寧な解説ありがとうございます。Append 手続で既存のテキストファイルの追加書き込みはできます。
またwriteln(F,値,値2,・・・値n)というふうに複数の文字列を一度に書き込みでき、改行も自動的に付与されるので、writelnは便利です。
スレッドの質問から多少外れてしまい失礼しました。

編集    削除
つっか  2004-11-17 14:43:51  No: 11751  IP: [192.*.*.*]

そうですね。できる、できないの比較では大差ないですね。
ファイルハンドルをつかって、個々の関数や手続きを使って操作するか、
それをラップしたクラスを使うか、の違いだと思います。

わたしにはクラスの方が使いやすいと。

編集    削除
チャーリー  2004-11-18 21:20:52  No: 11752  IP: [192.*.*.*]

解決しました。
分からないところも多々ありますが。。。

編集    削除