1

次のコンソールアプリケーションは、TStringList.SaveToFileを使用して、テキストファイルに複数行を書き込みます。

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Classes;
var
  i: Integer;
  a,b,c: Single;
  myString : String;
  myStringList : TStringList;
begin
  try
    Randomize;
    myStringList := TStringList.Create; 
    for i := 0 to 1000000 do
    begin
      a := Random;
      b := Random;
      c := Random;
      myString := FloatToStr(a) + Char(9) + FloatToStr(b) + Char(9) + FloatToStr(c);
      myStringList.Add(myString);
    end;
    myStringList.SaveToFile('Output.txt');
    myStringList.Free;
    WriteLn('Done');
    Sleep(10000);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

1000001行の50MBを超えるファイルを書き込むには、約3秒かかり、正常に機能しているようです。ただし、多くの人がそのようなプロセスにストリームを使用することを提唱しています。ストリームと同等のものは何であり、TStringList.SaveToFileと比較してそれを使用することの長所/短所は何ですか?

4

2 に答える 2

4

ストリームに直接書き込む方が速い場合があります。またはそうではないかもしれません。試してみて、両方のオプションの時間を計ることをお勧めします。ストリームへの書き込みは次のようになります。

for i := 0 to 1000000 do
begin
  a := Random;
  b := Random;
  c := Random;
  myString := FloatToStr(a) + Char(9) + FloatToStr(b) + Char(9) + 
    FloatToStr(c) + sLineBreak;
  Stream.WriteBuffer(myString[1], Length(myString)*SizeOf(myString[1]));
end;

このバージョンが高速であることを期待するには、バッファリングされたストリームを使用する必要があります。これを試してください: Buffered files (for fast disk access)

上記のコードは、最新の Delphi で UTF-16 テキストを出力します。ANSI テキストを出力したい場合は、単純に として宣言myStringAnsiStringます。

タイミングはお任せしますが、私の推測では、このバリアントは文字列リストと同様に機能します。Randomと の呼び出しに時間が費やされているのではないかと思いFloatToStrます。文字列リストを使用したファイルの保存は、すでに非常に高速であることを期待しています。

速度はさておき、このアプローチには別の利点があります。文字列リストのアプローチでは、質問のコードに従って、テキスト ファイルの内容全体がメモリに格納されます。ファイルを保存すると、保存手順の一部として別のコピーが作成されます。したがって、ファイル全体の 2 つのコピーがメモリ内に作成されます。

対照的に、ストリームに直接保存する場合、唯一のメモリ要件は、ストリーム クラスが使用するバッファです。質問によると、50MBのファイルの場合、どちらのアプローチでも実際の問題はありません。はるかに大きなファイルの場合、ファイル全体をメモリに保持しようとすると、メモリ不足エラーが発生します。


TStreamWriter個人的には、クラスの利用を検討したいと思います。この便利なクラスは、データ (テキスト、値など) を書き込むことと、ストリームにプッシュすることとを分離します。コードは次のようになります。

Writer := TStreamWriter.Create(Stream);//use whatever stream you like
try
  for i := 0 to 1000000 do
  begin
    a := Random;
    b := Random;
    c := Random;
    Writer.WriteLine(FloatToStr(a) + Char(9) + FloatToStr(b) + Char(9) +
      FloatToStr(c));
  end;
finally
  Writer.Free;
end;

TStreamWriter1KB のバッファでバッファリングを実装しているためTFileStream、適切なパフォーマンスを期待して使用できます。


最も読みやすいコードにつながる手法を選択することをお勧めします。パフォーマンスが問題になる場合は、後で最適化できます。私の個人的な好みはTStreamWriter. これにより、非常にクリーンで読みやすいコードが得られるだけでなく、コンテンツ生成とストリーミングの優れた分離も実現します。パフォーマンスも完全に合理的です。

于 2013-01-30T11:21:08.917 に答える
3

ベースのTFileStreamソリューションは次のようになりますが、いくつかの重要なポイントがあります。

  • TFileStreamコードは遅いです。バッファリングがなくTFileStream、一度に20バイトをファイルに書き込むことは効果的ではありません。TStringListバッファはすべてをRAMにバッファリングし、一度にすべて保存します。これは最適ですが、大量のRAMを使用します。
  • -ベースのバリアントでは、実際に予想されるように、TStringList時間の50%がに費やされます。Random
  • ソリューションをより効果的にするにはTFileStream、バッファリングスキームをロールして、毎回適切な量をディスクに書き込む必要があります(例:4Kb)

コード:

program Project9;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  SysUtils,
  Classes,
  DateUtils;
var
  i: Integer;
  a,b,c: Single;
  myString : AnsiString;
  StartTime: TDateTime;
  F: TFileStream;
begin
  try
    Randomize;
    StartTime := Now;
    F := TFileStream.Create('Output.txt', fmCreate);
    try
      for i := 0 to 1000000 do
      begin
        a := Random;
        b := Random;
        c := Random;
        myString := FloatToStr(a) + Char(9) + FloatToStr(b) + Char(9) + FloatToStr(c);
        myString := AnsiString(Format('%f'#9'%f'#9'%f'#13#10, [a, b, c]));
        F.WriteBuffer(myString[1], Length(myString));
      end;
    finally F.Free;
    end;
    WriteLn('Done. ', SecondOf(Now-StartTime), ':', MilliSecondOf(Now-StartTime));
    ReadLn;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
于 2013-01-30T11:17:40.277 に答える