TFileStreamのランダム読み出しついて教えてください。

解決


武田  2009-02-28 11:56:56  No: 33498

環境はWindowsXP  Delphi6personalです。

目的は、ファイル名を検索して、フルパスをテキストで
HDDのファイルに書き込み(大抵は2〜5万件)
あとで、手動で100番目、101番目、50番目などとランダムに指定して
そこに書かれたファイル名を取得したいのです。
書き込みに関しては過去ログに「ファイルの入出力について」で見つけました。
書き込みは下記のコードでうまくいきました。
コレに対応する読む方法がうまくいきません。
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;
説明を探して読むのですが、先頭バイトを書くとか最後のヌルを書くときは+1とか
sizeofをつかうとかLengthをつかうとか、古いコードで今はあまり使わないとか、
なんだか書く方式と読む方式が一致していないと
いけないような感じを受けて説明がよくわかりません。
この目的に合う、ランダムリードの方法を教えて頂けませんか。
(今はListboxを使っています。リソースを消費するのでやめたいです)
よろしくお願いします。


KHE00221  2009-02-28 13:55:34  No: 33499

固定長レコードでない場合 Indexファイル作ったほうが無難 (DBと同じ)

procedure TForm1.Button1Click(Sender: TObject);
var
  fs,fs2:TFileStream;
  fn,fn2,s:string;
  I64: Int64;

  Size: Integer;

begin
  fn := 'c:\TestData.txt';
  fn2 := 'c:\TestData.idx';

  if FileExists(fn) then
  begin
    fs := TFileStream.Create(fn,fmOpenReadWrite or fmShareDenyWrite);
    fs.Seek(0,soFromEnd);// ファイルの末尾にシークして移動
  end
  else
  begin
    fs := TFileStream.Create(fn,fmCreate or fmShareDenyWrite);
  end;

  if FileExists(fn2) then
  begin
    fs2 := TFileStream.Create(fn2,fmOpenReadWrite or fmShareDenyWrite);
    fs2.Seek(0,soFromEnd);// ファイルの末尾にシークして移動
  end
  else
  begin
    fs2 := TFileStream.Create(fn2,fmCreate or fmShareDenyWrite);
  end;

  try

    s := 'data : '+IntToStr(counter);
    Inc(counter);

    (* Index *)
    I64 := fs.Position;
    fs2.Write(I64,SizeOf(I64)); //Index

    (* レコード長 *)
    Size := Length(S);
    fs.Write(Size,SizeOf(Size));

    (* データ *)
    fs.Write(PChar(s)^,Length(s));

  finally
    fs.Free;
    fs2.Free;
  end;

end;

function TForm1.LoadFromFile(Index: Integer): String;
var
  fs,fs2:TFileStream;
  fn,fn2,s:string;
  I64: Int64;
  Buffer: array[0..MAX_PATH+4] of Char;
  Size: Integer;
begin
    Result := '';
    fn := 'c:\TestData.txt';
    fn2 := 'c:\TestData.idx';
    fs := TFileStream.Create(fn,fmOpenRead or fmShareDenyRead);
    fs2 := TFileStream.Create(fn2,fmOpenRead or fmShareDenyRead);
    try
      fs2.Position := Index * SizeOf(I64);
      fs2.Read(I64,SizeOf(I64));
      fs.Position := I64;
      fs.Read(Size,SizeOf(Size));
      Buffer := '';
      fs.Read(Buffer,Size);
      Result := StrPas(Buffer);
    finally
      fs.Free;
      fs2.Free;
    end;
end;


うんと  2009-02-28 22:48:43  No: 33500

ファイルサイズを気にしなければ、シーク速度を稼いでランダムアクセスするためには
固定長のレコードサイズにしたらいいのでは? 最大で一レコードは MaxPath のサイズに
収まることは分かっていることだし。これなら、10000番目のデータにアクセス
するのにいくらシークしたらいいかすぐに分かりますし、高速にランダムアクセスできるはず。


武田  2009-03-01 00:20:42  No: 33501

KHE00221さん、うんとさん早速有り難うございました。
優しく解りやすく助かります。
うまくいきました。
また教えてください。<(_ _)>


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

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






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