12

在我的服务器上有一些修改日期为 31/DEC/1979 的文件(不要问我为什么)。所以FileExists返回假。

Sysutils.FileExists看起来像这样:

function FileAge(const FileName: string): Integer;
var
  Handle: THandle;
  FindData: TWin32FindData;
  LocalFileTime: TFileTime;
begin
  Handle := FindFirstFile(PChar(FileName), FindData);
  if Handle <> INVALID_HANDLE_VALUE then
  begin
    Windows.FindClose(Handle);
    if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
    begin
      FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
      if FileTimeToDosDateTime(LocalFileTime, LongRec(Result).Hi,
        LongRec(Result).Lo) then Exit;
    end;
  end;
  Result := -1;
end;

function FileExists(const FileName: string): Boolean;
begin
  Result := FileAge(FileName) <> -1;
end;

我的问题是,为什么函数FileAge首先取决于?以下行不够吗?:

if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
  // Yes the file exists!

甚至基于文件属性:

function MyFileExists(const Name: string): Boolean;
var
  R: DWORD;
begin
  R := GetFileAttributes(PChar(Name));
  Result := (R <> DWORD(-1)) and ((R and FILE_ATTRIBUTE_DIRECTORY) = 0);
end;
4

3 回答 3

11

Delphi 的现代版本以FileExists与您的代码大致相同的方式实现。该实现对符号链接有额外的处理,但在其他方面与您的版本基本相同。

现代 Delphi 实现中有一个有趣的细微差别。如果调用GetFileAttributes返回INVALID_FILE_ATTRIBUTES,则代码不会立即退出。相反,它这样做:

LastError := GetLastError;
Result := (LastError <> ERROR_FILE_NOT_FOUND) and
  (LastError <> ERROR_PATH_NOT_FOUND) and
  (LastError <> ERROR_INVALID_NAME) and ExistsLockedOrShared(Filename);

并执行ExistsLockedOrShared使用FindFirstFile和检查 FILE_ATTRIBUTE_DIRECTORYon dwFileAttributes。这表明GetFileAttributes当文件存在但被锁定时可能会失败。但FindFirstFile在这种情况下,这可能会成功。这是合理的,因为FindFirstFile使用文件元数据而不是存储在文件本身中的数据。

很难说为什么代码是旧版本中的样子。我认为它很弱。就个人而言,我会FileExists使用代码挂钩替换为更好的版本。例如:delphi中的补丁例程调用

和往常一样,有一篇关于这个主题的Raymond Chen文章:迷信:为什么 GetFileAttributes 是老前辈测试文件存在的方式?

于 2012-12-19T19:14:43.827 回答
4

FileExists(它不使用FileAge并且也优化并且可以按照符号链接检查链接文件是否存在)的“现代”实现来判断:

  • 第一个变体((FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0)是可以的;
  • GetFileAttributes如果文件被锁定或共享,第二个变体 ( ) 可能会失败。
于 2012-12-19T19:25:35.310 回答
1

对于旧的 Delphi 版本,您可以下载 Jedi 代码库。它具有以下实现(除了许多其他有用的类和函数):

function FileExists(const FileName: string): Boolean;
{$IFDEF MSWINDOWS}
var
  Attr: Cardinal;
{$ENDIF MSWINDOWS}
begin
  if FileName <> '' then
  begin
    {$IFDEF MSWINDOWS}
    // FileGetSize is very slow, GetFileAttributes is much faster
    Attr := GetFileAttributes(Pointer(Filename));
    Result := (Attr <> $FFFFFFFF) and (Attr and FILE_ATTRIBUTE_DIRECTORY = 0);
    {$ELSE ~MSWINDOWS}
    // Attempt to access the file, doesn't matter how, using FileGetSize is as good as anything else.
    Result := FileGetSize(FileName) <> -1;
    {$ENDIF ~MSWINDOWS}
  end
  else
    Result := False;
end;
于 2012-12-20T07:42:00.543 回答