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;

为了希望这个版本很快,您需要使用缓冲流。试试这个:缓冲文件(用于更快的磁盘访问)

上面的代码将在现代 Delphi 上输出 UTF-16 文本。如果要输出 ANSI 文本,只需声明myStringAnsiString.

我会让你做计时,但我猜这个变体的性能类似于字符串列表。我怀疑时间都花在了调用RandomandFloatToStr上。我希望使用字符串列表保存的文件已经非常快了。

将速度放在一边,这种方法还有另一个好处。在字符串列表方法中,根据问题中的代码,文本文件的全部内容都存储在内存中。当您保存文件时,会在保存过程中制作另一个副本。因此,您将在内存中拥有整个文件的两个副本。

相反,当直接保存到流时,唯一的内存要求是流类使用的任何缓冲区。对于根据问题的 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 回答