创建和/或写入文件
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.Open
和 fmOpenOrCreate
开放模式,这正是您想要的;它返回一个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 文件哪个更快?