Delphi - 打开文本文件时出现共享冲突

6

我正在尝试在Delphi 7应用程序中打开一个文本文件进行阅读,但是由于另一个应用程序已经打开了该文件,因此出现了I/O错误32(共享冲突)。

我已经尝试将FileMode设置为“fmOpenRead or fmShareDenyNone”,但现在意识到这对文本文件没有任何作用。

有没有一种方法可以读取被另一个应用程序打开的文本文件?

var
  f: TextFile;
begin
  FileMode := fmOpenRead or fmShareDenyNone;   // FileMode IS NOT APPLICABLE TO TEXT FILES!!
  AssignFile(f, FileName);
  Reset(f);

你为什么如此热衷于文本文件?为什么不使用流类,这些类允许适当的文件访问和共享模式? - mghie
因为我想一次读取一行,而TFileStream没有相应的方法。我想我可以读取一个缓冲区并在CR/LF上分割。 - Simes
1
你可以使用 TStreamReaderTFileStream 中读取行。它具有 ReadLine() 方法,并为您内部缓冲。 - Remy Lebeau
@RemyLebeau - 我尝试使用TStreamReader,但当文件被另一个应用程序打开时,它似乎会导致访问冲突。 - lkessler
@lkessler,那不是访问冲突(访问无效的内存地址),而是共享冲突(以不被另一个打开同一文件的句柄允许的权限打开文件)。这是两回事。您仍然可以使用TStreamReader,只需使用具有足够共享权限的TFileStream首先打开文件(fmOpenReadfmShareDenyNone通常可以,除非其他句柄对该文件具有独占访问权限),然后将TFileStream传递给TStreamReader进行读取。 - Remy Lebeau
5个回答

13

使用 TStringList 的 LoadFromStream 方法,而不是 LoadFromFile 方法。这样可以控制锁定:

var
    slFile: TStrings;
    stream: TStream;
begin
   slFile := TStringList.Create;
   try
      stream := TFileStream.Create(filename, fmOpenRead or fmShareDenyNone);
      try 
         slFile.LoadFromStream(stream);
      finally
         stream.Free;
      end;

      //Use the stringlist
   finally
      slFile.Free;
   end;
end;

这个示例使用流将数据加载到TStringList中。如果你只想读取部分内容,也可以这样做。只需从流中读取即可。


这个方案可行并解决了问题。唯一的担忧是内存使用:整个文件都被加载到内存中,这可能会成为一个问题。 - Ghigo

3

这取决于其他进程打开文件的方式... 如果它独占了文件,你将无法成功。

而TextFile已经过时,我认为它会以独占模式打开,以兼容旧式DOS。你应该使用TFileStream或类似的东西。

TStringList也可能有效,再次取决于其他进程在做什么。但如果文件正在被写入(比如.log文件),fmShareDenyWrite就不起作用了。


我不知道其他进程是如何打开文件的,但记事本可以在文件正在被写入时成功地打开它。 - Simes
看起来你很幸运。只需放弃TextFile即可。 - H H
@Simes,我在这里概述了一个过程,向您展示了文件如何被打开:https://dev59.com/TkfRa4cB1Zd3GeqP6y3H#739328 - dan-gph
@Simes 使用Process Monitor来查找文件是如何打开以及使用了哪些共享权限。 - Remy Lebeau

2
也许就像这样:
  vFileList := TStringList.Create;
  try
    vFileStream := TFileStream.Create('myfile.txt', fmOpenRead or fmShareDenyNone);
    try
      vFileList.LoadFromStream(vFileStream);
    finally
      vFileStream.Free;
    end;
    // Use vFileList
  finally
    vFileList.Free;
  end;

1

这将立即解决您的问题。使用TStringList加载文件。只需调用:

...
var sl: TStringList;
begin
  sl := TStringList.create();
  try
    sl.loadFromFile(Filename);
    ...do your stuff here...
  finally
    freeAndNil(sl);
  end;
end;

我发现在处理文本文件时,最好使用TStringList。否则我会选择TFileStream,在那里您可以指定打开模式。


LoadFromFile()使用的是“fmOpenRead或fmShareDenyWrite”模式(至少在Delphi 5和2007中是这样)。这绝对不是OP想要的。 - mghie
这正是Simes想要的。他只想读取文件,而不关心其他正在使用它的进程。这正是他的问题。 - Peter Perháč
3
@MasterPeter,我相信mghie所谈的罪犯是fmShareDenyWrite。fmShareDenyWrite“锁定”文件,以便其他进程只能读取……但因此而对你的答案进行投票扣分有些严厉了。 - Lieven Keersmaekers
问题在于它会将整个文件加载到内存中。有些文件可能很大,因此我更喜欢逐行处理它们。 - Simes
1
@Lieven:你两点都说对了(我也没有给答案投反对票)。@MasterPeter:如果另一个进程正在以非独占方式打开文件进行写入,则打开文件将失败。在这种情况下,fmShareDenyNone是必需的。 - mghie
我刚试了一下,你们两个都是对的,如果另一个进程有 fmOpenWrite 访问权限,则无法运行。但是,我怀疑Simes没有这个问题。他 / 她只想逐行读取文件,而 TextFile(s) 似乎不适用于他 / 她。我从未在我的整个生命中使用过 TextFiles(每当我需要将某些东西视为文本文件时,我总是使用 TStringLists),因此我不能提供比我已经尝试过的更多帮助 :) - Peter Perháč

1

如果我没记错的话,还有一个仅适用于文本文件的Textfilemode变量。


那本来很完美,但它无法编译并且在帮助文件中找不到。 - Simes

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接