File.Move不起作用-文件已经存在


86

我有一个文件夹:

c:\ test

我正在尝试以下代码:

File.Move(@"c:\test\SomeFile.txt", @"c:\test\Test");

我得到异常:

文件已存在

输出目录肯定存在,输入文件在那里。


2
如果输入文件已经在输出目录中,则该文件已经存在,因此可以解释该异常。您需要表明您希望原始文件新文件覆盖
科迪·格雷

9
听起来好像错误正在告诉您确切的问题。
乔什(Josh)

@Josh否。听起来像Windows具有非POSIX文件系统行为,这使得弄清简单的便携式事务性文件更新模式/例程变得不可能。
宾基

@binki POSIX无关紧要(您指的是原子操作吗?),NTFS确实支持真实的事务操作,如回滚并获取原始文件内容回滚一样。正如其他人回答的那样,Win32确实允许使用替换进行移动。我是.NET的File.Move,它不提供此功能。您可以使用AlphaFS之类的库获得带有替换的Move和事务操作
Panagiotis Kanavos,

2
在任何情况下,无论论坛讨论怎么说,@binki的行为都可以在不同的文件系统上很好地定义。File.Move之所以不调用Ex或Transacted方法的原因是,FAT(它仍然被存储卡使用)不能被忽略,它不是原子的并且行为也不相同。重命名不是元数据操作,需要实际的数据移动。不用担心交易和写时复制。恕我直言,这不是一个明智的决定
Panagiotis Kanavos '18

Answers:


62

您需要将其移动到另一个文件(而不是文件夹),这也可以用于重命名。

移动:

File.Move(@"c:\test\SomeFile.txt", @"c:\test\Test\SomeFile.txt");

改名:

File.Move(@"c:\test\SomeFile.txt", @"c:\test\SomeFile2.txt");

在您的示例中显示“文件已存在”的原因是因为C:\test\Test尝试创建Test不带扩展名的文件,但由于文件夹已存在且具有相同名称而无法创建文件。


138

您需要的是:

if (!File.Exists(@"c:\test\Test\SomeFile.txt")) {
    File.Move(@"c:\test\SomeFile.txt", @"c:\test\Test\SomeFile.txt");
}

要么

if (File.Exists(@"c:\test\Test\SomeFile.txt")) {
    File.Delete(@"c:\test\Test\SomeFile.txt");
}
File.Move(@"c:\test\SomeFile.txt", @"c:\test\Test\SomeFile.txt");

这将是:

  • 如果该文件在目标位置不存在,则成功移动该文件,或者
  • 如果该文件确实存在于目标位置,则将其删除,然后移动该文件。

编辑:我应该澄清我的答案,即使它是最受支持的!File.Move的第二个参数应该是目标文件而不是文件夹。您将第二个参数指定为目标文件夹,而不是目标文件名-这是File.Move所需的。因此,您的第二个参数应为c:\test\Test\SomeFile.txt


当然不需要检查文件是否不存在,因为他正在检查并且文件不存在。异常是由于在尝试将文件名移动到另一个文件夹时未将其添加到目标文件夹而引起的。
哈迪·埃斯坎达里

3
如果您的应用程序是多线程的(或者文件上有其他进程在运行),即使使用“ if(Exists)Delete”代码,您仍然可能会遇到相同的异常。由于仍有一段时间,在删除之后,另一个线程/进程可能会将另一个文件放回去,因此您进行了移动,然后无论如何都获得了Exception。值得牢记:-)
bytedev

11
对于大多数尝试覆盖现有文件后使用google的人来说,此答案仍然有效。这种困境中的大多数人都没有像OP这样的语法/类型问题。
WEFX 2013年

1
@ v.oddou有趣的是,如果该文件不存在,则File.Delete确实可以正常工作并且不执行任何操作。相反,如果路径中的任何目录都不存在,则您会收到DirectoryNotFoundException。
布兰登·巴克利

2
@JirkaHanika您可以将if(File.Exists)更改为while(File.Exists)。
布兰登·巴克利

38

我个人更喜欢这种方法。这将覆盖目标位置上的文件,删除源文件,并且还防止在复制失败时删除源文件。

string source = @"c:\test\SomeFile.txt";
string destination = @"c:\test\test\SomeFile.txt";

try
{
    File.Copy(source, destination, true);
    File.Delete(source);
}
catch
{
    //some error handling
}

4
这对于小文件(并且不需要原子移动)很好,但是对于大文件,或者在需要确保不会重复的情况下,这是有问题的。
萨蒂亚河

你为什么喜欢File.Copy , File.DeleteFile.Move
约翰·彼得拉

6
File.Move没有覆盖选项。
米切尔

1
根据您的用例,这可能会导致问题。“移动”是文件系统观察器中的真实事件。列出文件系统事件的内容将获得删除和创建事件,而不是移动事件。这还将更改基础文件系统ID。
安德鲁·朗多

1
大文件的性能会降低很多吗?如果源和目标在同一物理卷上,则无缘无故地创建第二个副本,然后删除原始副本,而如果源和目标在同一卷上,则File.Move()将避免做额外的工作。
布拉德·韦内尼斯

18

你可以做一个P / Invoke来MoveFileEx()-通过11flagsMOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH

[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
static extern bool MoveFileEx(string existingFileName, string newFileName, int flags);

或者,您可以致电

Microsoft.VisualBasic.FileIO.FileSystem.MoveFile(existingFileName, newFileName, true);

添加Microsoft.VisualBasic作为参考之后。


如果该应用程序仅在Windows上运行,则完全可以。对于大多数愿意尝试som P / Invoke的人来说,这可能是一个很好的答案。
托德(Todd)

9

如果文件确实存在,并且您要替换它,请使用以下代码:

string file = "c:\test\SomeFile.txt"
string moveTo = "c:\test\test\SomeFile.txt"

if (File.Exists(moveTo))
{
    File.Delete(moveTo);
}

File.Move(file, moveTo);

4

根据File.Move的文档,没有“如果存在则覆盖”参数。您试图指定目标文件夹,但必须提供完整的文件规范。

认为,再次阅读文档(“提供用于指定新文件名的选项”),可以在目标文件夹规范中添加反斜杠可能有效。


文档提到 ,如果尝试通过将同名文件移动到该目录来替换文件,则会抛出IOException。为此,请致电Move(String, String, Boolean)但这似乎是一个错误?
凯文·沙恩霍斯特

@KevinScharnhorst这个答案是2011年。文档现在包括.Net Core 3.0对覆盖移动的支持。
托德,

4

1)在.Net Core 3.0及更高版本上使用C#,现在有了第三个布尔参数:

参见https://docs.microsoft.com/zh-cn/dotnet/api/system.io.file.move?view=netcore-3.1

In .NET Core 3.0 and later versions, you can call Move(String, String, Boolean) setting the parameter overwrite to true, which will replace the file if it exists.

2)对于.Net的所有其他版本,https://stackoverflow.com/a/42224803/887092是最佳答案。复制并覆盖,然后删除源文件。这样做更好,因为它使其成为原子操作。(我尝试以此更新MS Docs)



1

如果您没有选择删除新位置中已经存在的文件,但仍需要从原始位置移动和删除,则此重命名技巧可能会起作用:

string newFileLocation = @"c:\test\Test\SomeFile.txt";

while (File.Exists(newFileLocation)) {
    newFileLocation = newFileLocation.Split('.')[0] + "_copy." + newFileLocation.Split('.')[1];
}
File.Move(@"c:\test\SomeFile.txt", newFileLocation);

这假定唯一的“。” 文件名中的扩展名之前。它将文件扩展名一分为二,并附加“ _copy”。在两者之间。这使您可以移动文件,但是如果文件已经存在或副本的副本已经存在,或者副本的副本存在,则创建一个副本...;)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.