【问题标题】:File.Move Does Not Work - File Already ExistsFile.Move 不起作用 - 文件已经存在
【发布时间】:2011-08-20 17:21:46
【问题描述】:

我有一个文件夹:

c:\测试

我正在尝试这段代码:

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

我得到异常:

文件已经存在

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

【问题讨论】:

  • 如果输入文件已经在输出目录下,那么该文件已经存在,从而解释异常。您需要表明您希望新文件覆盖原始文件。
  • 听起来错误正在告诉你到底出了什么问题。
  • @Josh 否。听起来 Windows 具有非 POSIX 文件系统行为,这使得找出简单的可移植事务文件更新模式/例程变得不可能。
  • @binki POSIX 是无关紧要的(你指的是 atomic 操作吗?),NTFS does 支持真正的事务操作,如回滚和-获取原始文件内容返回。正如其他人回答的那样,Win32 确实 允许使用替换移动。我的 .NET 的 File.Move 不提供该功能。您可以使用 AlphaFS 之类的库获得带有替换的 Move 和事务操作
  • @binki 在任何情况下,行为都是明确定义的在不同的文件系统上,无论论坛讨论说什么。 File.Move 不调用 Ex 或 Transacted 方法的原因是 FAT,不能被忽略,因为它仍然被存储卡使用,不是原子的,不是 行为相同。重命名不是元数据操作,需要实际的数据移动。忘记交易和写时复制。恕我直言,这不是一个好决定

标签: c# file-io


【解决方案1】:

根据the docs for File.Move,没有“如果存在则覆盖”参数。您尝试指定目标文件夹,但您必须提供完整的文件说明。

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

【讨论】:

  • 并且文档提到 请注意,如果您尝试通过将同名文件移动到该目录来替换文件,则会引发 IOException。为此,请致电 Move(String, String, Boolean) 但这似乎是一个错误?
  • @KevinScharnhorst 这个答案是 2011 年的。文档现在包括 .Net Core 3.0 对 Move with Overwrite 的支持。
【解决方案2】:

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

移动:

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,但由于已存在同名文件夹,因此无法这样做。

【讨论】:

  • 这个答案刚刚帮助我理解了为什么 Linux 有一种使用后缀 .d 命名目录的语法,其中可能存在同名文件。谢谢!
【解决方案3】:

你需要的是:

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 的第二个参数应该是目标文件 - 不是 文件夹。您将第二个参数指定为目标文件夹,not 目标文件名 - 这是 File.Move 所需要的。 所以,你的第二个参数应该是c:\test\Test\SomeFile.txt

【讨论】:

  • 当然不需要检查文件是否不存在,因为他正在检查并且文件不存在。该异常是由于尝试将文件移动到另一个文件夹时未将文件名附加到目标文件夹而引起的。
  • 如果您的应用程序是多线程的(或者有其他进程在处理您的文件),即使使用“if(Exists) Delete”代码,您仍然可能会遇到相同的异常。由于仍有一段时间,另一个线程/进程可能会在删除后将文件放回原处,因此您进行移动,然后无论如何都会获得异常。值得记住:-)
  • 这个答案对于尝试覆盖现有文件后谷歌的大多数人仍然有效。处于这种困境中的大多数人没有像 OP 这样的语法/类型问题。
  • @v.oddou 有趣的是,如果文件不存在,File.Delete 确实可以正常工作并且什么也不做。相反,如果路径中的任何目录都不存在,则会收到 DirectoryNotFoundException。
  • @JirkaHanika 您可以将 if(File.Exists) 更改为 while(File.Exists)。
【解决方案4】:

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

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

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

File.Move(file, moveTo);

【讨论】:

    【解决方案5】:

    您可以对MoveFileEx() 执行 P/Invoke - 为flags (MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH) 传递 11

    [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 的人来说,这可能是一个很好的答案。
    【解决方案6】:

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

    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
    }
    

    【讨论】:

    • 这对于小文件来说很好(并且不需要原子移动),但对于大文件,或者您需要确保不会出现重复的情况,这是有问题的。跨度>
    • File.Move 没有覆盖选项。
    • 根据您的用例,这可能会导致问题。 “移动”是文件系统观察程序中的真实事件。文件系统事件列表将获得删除和创建事件,而不是移动事件。这也将更改底层文件系统 ID。
    • 这对于大文件的性能会不会低很多?如果源和目标位于同一个物理卷上,您将无缘无故地创建第二个副本,然后删除原始副本,而如果源和目标位于同一卷上,File.Move() 将避免做额外的工作。
    • @Mitchell 在 .NET v3.0 他们添加了overwrite 参数。 仅作记录
    【解决方案7】:

    试试Microsoft.VisualBasic.FileIO.FileSystem.MoveFile(Source, Destination, True)。最后一个参数是覆盖开关,System.IO.File.Move没有。

    【讨论】:

    【解决方案8】:

    如果您无法选择删除新位置中已存在的文件,但仍需要从原始位置移动和删除,则此重命名技巧可能会奏效:

    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”。介于两者之间。 这使您可以移动文件,但如果文件已存在或副本的副本已存在或副本的副本存在,则创建副本... ;)

    【讨论】:

      【解决方案9】:
      1. 在 .Net Core 3.0 及更高版本上使用 C#,现在有第三个布尔参数:

      在.NET Core 3.0及以后的版本中,可以调用Move(String, String, Boolean)设置参数overwrite为true,如果文件存在就会替换文件。

      来源:Microsoft Docs

      1. 对于所有其他版本的 .Net,this answer 是最好的。使用覆盖复制,然后删除源文件。这更好,因为它使它成为原子操作。 (我试图用这个来更新 MS Docs)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-21
        • 1970-01-01
        • 2022-08-14
        • 2023-01-25
        相关资源
        最近更新 更多