首页 > 解决方案 > TStreamWriter 锁定文件以供读取

问题描述

在我们的项目中,我们使用 StreamWriter 写入日志文件。

在写作时,我想在文件中搜索一些特定的行。例如在单元测试期间。但不知何故,我无法从文件中读取,因为它被进程阻止了。我不明白它为什么被阻止,因为我认为我打开 FileStream 时没有任何阻塞。

我将所有内容从项目中提取到这个小例子中。在写入文件时,我必须更改什么以不阻止文件读取?

program Playground;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Classes;

var
  FileStream: TFileStream;
  FileStreamWriter : TStreamWriter;
  strList: TStringList;
  fileName: String;

begin
  try
    strList := TStringList.Create;
    fileName := 'TestFile.txt';

    FileStream := TFileStream.Create(fileName, fmCreate or fmShareDenyNone);
    FileStreamWriter := TStreamWriter.Create(FileStream, TEncoding.Unicode);
    FileStreamWriter.WriteLine('12345');
    strList.LoadFromFile(fileName); // Crashes because a proccess blocks the file

    strList.Free;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

  Readln;
end.

标签: delphi

解决方案


正如 Peter Wolf 在他的评论中所说的那样,造成您的麻烦的原因不在于您的代码,而在于StringList.LoadFromFile代码。

当字符串列表试图加载文件的内容时,您会看到它以一种会阻止其他应用程序读取其内容的方式打开它。

下面是StringList.LoadFromFile代码的样子:

procedure TStrings.LoadFromFile(const FileName: string);
var
  Stream: TStream;
begin
  Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    LoadFromStream(Stream);
  finally
    Stream.Free;
  end;
end;

因此,为了避免这个问题,您应该自己创建另一个文件流,然后使用该文件流将文件内容读入StringList.

program Playground;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils, System.Classes;

var
  FileStream: TFileStream;
  StrListFS: TFileStream;
  FileStreamWriter : TStreamWriter;
  strList: TStringList;
  fileName: String;

begin
  try
    strList := TStringList.Create;
    fileName := 'D:\TestFile.txt';

    FileStream := TFileStream.Create(fileName, fmCreate or fmShareDenyNone);
    FileStreamWriter := TStreamWriter.Create(FileStream, TEncoding.Unicode);
    FileStreamWriter.WriteLine('12345');
    //strList.LoadFromFile(fileName); // Crashes because a proccess blocks the file

    //Create a new file stream that we will use for reading the file contents into
    //our string list.
    StrListFS := TFileStream.Create(fileName, fmOpenRead or fmShareDenyNone);
    //Load contets from a file into a stream by using our newly created FileStream
    strList.LoadFromStream(StrListFS);
    //Free the file stream when we are done loading the data.
    StrListFS.Free;

    strList.Free;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;

  Readln;
end.

理论上,您可以使用流编写器使用的相同文件流将该文件的内容读入字符串列表,但请注意这样做会改变您的流位置,因此您必须保存之前的位置并在读取后恢复它将文件的内容添加到字符串列表中。

即使您以锁定模式打开流编写器文件流,这种方式也可以做到这一点,这将阻止多个进程/句柄打开文件。


推荐阅读