创建和/或写入文件

Posted

技术标签:

【中文标题】创建和/或写入文件【英文标题】:Create and/or Write to a file 【发布时间】:2011-12-11 10:37:15 【问题描述】:

我觉得这应该很容易,但谷歌目前完全让我失望。我想打开一个文件,或者如果它不存在就创建它,然后写入它。

以下

AssignFile(logFile, 'Test.txt');
Append(logFile);

当文件尚不存在时,在第二行引发错误,我认为这是预期的。但我真的没能找到如何 a) 测试文件是否存在以及 b) 在需要时创建它。

仅供参考,在 Delphi XE 中工作。

【问题讨论】:

试试if FileExist('test.txt') then Append(logFile) else Rewrite(logFile); 【参考方案1】:

您可以使用FileExists 函数,如果存在则使用Append,如果不存在则使用Rewrite

    AssignFile(logFile, 'Test.txt');

    if FileExists('test.txt') then
      Append(logFile)
    else
      Rewrite(logFile);

   //do your stuff

    CloseFile(logFile); 

【讨论】:

您可以使用System.IOUtils.TFile.AppendAllText( 'test.txt', contents ); 使其更加简洁易读 - 另请参阅awmross's answer。保证完全符合您的要求:“如果 Path 参数指定的文件存在,则将文本附加到它;否则,将创建文件并用给定的文本填充。” - -来自official documentation【参考方案2】:

任何使用FileExists 选择如何打开文件的解决方案都存在竞争条件。如果文件的存在在您测试它和您尝试打开文件之间发生变化,您的程序将失败。 Delphi 没有提供任何方法来解决其本地文件 I/O 例程的问题。

如果您的 Delphi 版本足够新,可以提供它,您可以使用 TFile.OpenfmOpenOrCreate 开放模式,这正是您想要的;它返回一个TFileStream

否则,您可以使用 Windows API 函数 CreateFile 来打开您的文件。将dwCreationDisposition 参数设置为OPEN_ALWAYS,告诉它如果文件不存在就创建它。

【讨论】:

我想一旦我们添加了关键部分,竞争条件就会被消除。在 FileExists 调用之前获取它并持有它直到它完成写入。 TFile.Open 依赖于 FileExist,所以它不能解决竞争条件 关键部分无济于事,@User。关键部分仅在 all 代码尊重它们时才起作用。您不能保证所有其他程序都会尊重您的关键部分。 (事实上​​,他们不能尊重它们,因为临界区只适用于单个进程。)除了调用CreateFile之外,您无法解决其他进程的问题。【参考方案3】:

您应该改用TFileStream。这是一个示例,如果文件不存在则创建文件,如果存在则写入文件:

var
  FS: TFileStream;
  sOut: string;
  i: Integer;
  Flags: Word;
begin
  Flags := fmOpenReadWrite;
  if not FileExists('D:\Temp\Junkfile.txt') then
    Flags := Flags or fmCreate;
  FS := TFileStream.Create('D:\Temp\Junkfile.txt', Flags);
  try
    FS.Position := FS.Size;  // Will be 0 if file created, end of text if not
    sOut := 'This is test line %d'#13#10;
    for i := 1 to 10 do
    begin
      sOut := Format(sOut, [i]);
      FS.Write(sOut[1], Length(sOut) * SizeOf(Char)); 
    end;

  finally
    FS.Free;
  end;
end;

【讨论】:

如果您包含fmCreate 标志,则始终会创建该文件,即使存在。 相反,您可以使用类似FS := TFileStream.Create('test2.txt', IfThen(FileExists('test2.txt'),fmOpenReadWrite,fmCreate)); @Ken - 你为什么建议使用 TFileStream 代替?当我只是想快速写入文件时,它看起来要复杂得多。 (不挑剔。只是了解 Delphi 的来龙去脉。) @Eric 您首选的方法不支持 Unicode,并且已被高度弃用。 @DavidHeffernan Ken 的回答建议如果使用 fmOpenReadWrite or fmCreate 标志,将创建或打开文件以添加数据。 (检查FS.Position := FS.Size; 行)但这永远不会发生,因为如果存在fmCreate 标志,文件总是会重新创建。【参考方案4】:

如果你只是做一些简单的事情,IOUtils Unit 会容易得多。它有很多用于写入文件的实用程序。

例如

procedure WriteAllText(const Path: string; const Contents: string); 超载;静态的;

创建一个新文件,将指定的字符串写入文件,然后 关闭文件。如果目标文件已经存在,则覆盖。

【讨论】:

这个问题专门说“如果它不存在就创建”,并使用Append,所以我怀疑如果它已经存在,他们想追加。您的报价说“如果目标文件已经存在,它将被覆盖”,因此防止追加。 :) 没有downvote - 只是说...... 使用附加的(非工作)示例;给出的问题没有指定任何关于追加的内容。 “打开一个文件......并写入它”。不清楚他是想追加还是覆盖现有文件。 不。 “打开一个文件,或者如果它不存在就创建它,然后写入它”not 是否意味着截断;打开现有文件通常意味着附加到它。但是,由于不清楚,我确实没有否决您的回答;如果问题对截断/追加更清楚,我会相应地投票赞成或反对。 :) 还有AppendAllText【参考方案5】:

您还可以使用TStringList 中的加载/保存功能来解决您的问题。

这可能是一个糟糕的解决方案,因为整个文件将被加载到内存中,在内存中进行修改,然后保存回磁盘。 (与您直接写入文件的解决方案相反)。对于多用户情况,这显然是一个糟糕的解决方案。

但这种方法适用于较小的文件,并且易于使用且易于理解。

const
  FileName = 'test.txt';
var
  strList: TStringList;
begin
  strList := TStringList.Create;

  try
    if FileExists(FileName) then
      strList.LoadFromFile(FileName);

    strList.Add('My new line');

    strList.SaveToFile(FileName);
  finally
    strList.Free;
  end;
end;

【讨论】:

仅供参考,这种方法对堆非常不友好,日志文件可能非常大,加载和保存操作需要 2x 字节才能完成。

以上是关于创建和/或写入文件的主要内容,如果未能解决你的问题,请参考以下文章

将文件上传到 HDFS 或直接创建和写入 HDFS 文件哪个更快?

创建ini文件,在PHP中写入值

如何使用 VBA 创建和写入 txt 文件

使用 Windows 的防病毒软件或备份程序偶尔写入文件失败

创建ini文件,用PHP写入值

将 CSV 写入标准输出或文件名