【问题标题】:Determining if file exists using c# and resolving UNC path使用 c# 确定文件是否存在并解析 UNC 路径
【发布时间】:2014-10-22 00:44:02
【问题描述】:

我正在尝试编写一个函数来确定文件是否存在。这两种方法被证明返回不一致的结果(与返回误报的 isFileFound() 相比,fileExists() 似乎提供了准确的结果 - 我在尝试创建实例时会出现异常)。

protected bool isFileFound(string path, string fileName)
    {
        System.IO.FileInfo fi = null;

        bool found = false;
        try
        {
            fi = new System.IO.FileInfo(path + fileName);
            found = true;
        }
        catch (Exception e)
        {
            baselogger.Fatal(e.Message + " " + e.StackTrace + " \n" + path + fileName);
        }

        return found;
    }

    protected bool fileExists(string path, string pattern)
    {
        bool success = false;

        try
        {
            success = File.Exists(path + pattern);
        }
        catch (Exception e)
        {
            baselogger.Warn(e.Message + " " + e.StackTrace + " " + e.Source);
        }

        return success;
    }

似乎都无法解析以下语法的 UNC 路径:\\abcserver\c$\xyzfolder\foo.bar

如果知道这些方法的 unc 路径为何失败,我们将不胜感激。

【问题讨论】:

  • 仅供参考 - 作为一般做法,我尝试使用 System.IO.Path.Combine(path, filename) 而不是字符串连接:路径 + 文件名
  • 警告:IO.Path.Combine("D:\Files", "\foo.xml") 会给你"\foo.xml",接下来你知道你有文件保存到根目录。

标签: c# filesystems


【解决方案1】:

您可以为不存在的文件创建 FileInfo。但是你可以检查 FileInfo.Exists 属性来确定文件是否存在,例如:

FileInfo fi = new FileInfo(somePath);
bool exists = fi.Exists;

更新: 在一个简短的测试中,这也适用于 UNC 路径,例如像这样:

FileInfo fi = new FileInfo(@"\\server\share\file.txt");
bool exists = fi.Exists;

您确定该帐户(您的应用程序在其下运行)有权访问该共享。我认为(默认情况下)需要管理权限才能访问共享“c$”。

【讨论】:

  • 好的,但是 UNC 路径呢?即使对此方法进行了修改,这两种方法都无法解析我原来问题中的 UNC 路径格式。
  • 尝试了一个带有 d$ 共享的备用文件夹,它成功了。一点点挖掘得出结论,UNC 路径是有效的,但需要更改一些权限。
  • 如果 UNC 路径不再可用,此方法是否存在超时?
  • 这对我帮助很大 - 感谢 M4N。在我的示例中,System.IO.File.Exists 返回 TRUE,但同一路径上的 FileInfo.exists 返回 FALSE!?而且我知道该文件不存在...
【解决方案2】:

看到这个问题:
how can you easily check if access is denied for a file in .NET?

这个问题的简短版本是你不知道,因为文件系统是易失的。尝试打开文件并在失败时捕获异常。

您的isFileFound 方法不起作用的原因是您使用的FileInfo 结构也可用于创建文件。您可以为不存在的文件创建一个包含所需信息的 FileInfo 对象,将其称为 .Create() 方法,然后您就可以一次设置所需的所有属性。

我怀疑 UNC 路径失败的原因是 1)从运行您的应用程序的用户访问管理共享的权限问题, 2)$ 符号正在抛出该方法,要么是因为输入不正确,要么是因为底层 .Exists() 实现中的错误。

更新:

当我发布此建议时,我几乎总是收到有关异常性能的投诉。让我们谈谈那个。是的,处理异常很昂贵:非常很昂贵。您可以在编程中做一些较慢的事情。但你知道那几样东西是什么吗?磁盘和网络 I/O。下面的链接演示了磁盘 I/O 和网络 I/O 的成本:

https://gist.github.com/jboner/2841832

延迟比较数字
--------------------------
L1 缓存参考 0.5 ns
分支错误预测 5 ns
L2 缓存参考 7 ns 14x L1 缓存
互斥锁/解锁 25 ns
主存参考 100 ns 20x L2 高速缓存,200x L1 高速缓存
使用 Zippy 3,000 ns 压缩 1K 字节
通过 1 Gbps 网络发送 1K 字节 10,000 ns 0.01 ms
从 SSD 随机读取 4K* 150,000 ns 0.15 ms
从内存中顺序读取 1 MB 250,000 ns 0.25 ms
同一数据中心内的往返 500,000 ns 0.5 ms
从 SSD 顺序读取 1 MB* 1,000,000 ns 1 ms 4X 内存
磁盘寻道 10,000,000 ns 10 ms 20x 数据中心往返
从磁盘顺序读取 1 MB 20,000,000 ns 20 ms 80x 内存,20X SSD
发送数据包 CA->Netherlands->CA 150,000,000 ns 150 ms

如果您不喜欢以纳秒为单位进行思考,这里有另一个链接将一个 CPU 周期标准化为 1 秒并从那里开始扩展:

http://blog.codinghorror.com/the-infinite-space-between-words/

1 个 CPU 周期 0.3 ns 1 s 一级缓存访问 0.9 ns 3 s 二级缓存访问 2.8 ns 9 s 3 级缓存访问 12.9 ns 43 s 主存储器访问 120 ns 6 分钟 固态磁盘 I/O 50-150 μs 2-6 天 旋转磁盘 I/O 1-10 毫秒 1-12 个月 互联网:SF 到 NYC 40 毫秒 4 年 互联网:SF 到英国 81 毫秒 8 年 互联网:SF 到 AUS 183 ms 19 年 操作系统虚拟。重启 4 s 423 年 SCSI 命令超时 30 秒 3000 年 硬件虚拟。重启 40 秒 4000 年 物理系统重启 5 m 32 千年

即使在例外情况的最佳情况下,您也可以在等待磁盘的第一个响应时访问内存至少 480 次,这是假设 SSD 速度非常快的情况。我们中的许多人仍然需要旋转硬盘,但情况会变得更糟。

这只是故事的开始。当您使用.Exists() 时,您会在 上产生此额外 成本(这是额外的:当您打开文件时必须再次执行相同的工作) > 尝试。无论文件是否存在,您都需要支付此费用,因为磁盘仍然需要在其文件表中查找它。使用异常方法,您只需支付失败情况下展开调用堆栈的额外费用。

换句话说,是的:例外的代价非常高昂。但与磁盘检查相比,它仍然更快:而且差距不大。值得庆幸的是,这不太可能推动您的应用程序的总体性能......但我仍然想放弃这个特定任务的“异常缓慢”的论点。

【讨论】:

  • 请注意,捕获抛出的异常可能非常昂贵。如果您要执行批处理文件操作,最好测试文件是否存在捕获异常。
  • @IfeanyiEcheruo 相比网络文件操作的开销,以及可能的 douboemprocessin 和时间窗口错误的成本,捕获异常的成本完全可以忽略不计。
  • 换一种说法:使用.Exists() 检查,无论文件是否存在,您总是支付网络和磁盘 i/o 成本来发现这一点。使用异常方法,您只需支付在失败情况下展开调用堆栈的额外成本。此外,尽管异常昂贵,但网络和磁盘 i/o 也非常、非常 更糟。事实上,我想我会把这个添加到答案中。
  • 操作系统甚至磁盘本身的缓存呢?
【解决方案3】:

这可能是,也可能不是,但您是否会为您的一个案例错误地加入路径文件名。

这个:

成功 = File.Exists(路径 + 模式);

对比:

成功 = File.Exists(Path.Join(path,pattern));

【讨论】:

【解决方案4】:

这可以帮助你:
http://www.codeplex.com/FileDirectoryPath
它是 NDepend.Helpers.FilePathDirectory,其中有一个“路径有效性检查 API”非常有用。

【讨论】:

    【解决方案5】:

    所以我选择了

    bool success = File.Exists(path + Filename);
    

    选项,而不是使用 FileInfo 路由。

    感谢所有建议!

    【讨论】:

    • 您可能希望使用 System.IO.Path.Combine(path, Filename) 而不是字符串添加。这需要处理极端情况,例如"a\" + "b" = "a\b" and Path.Combine("a\", "b") = "a\b" but "a" + "b" = "ab" and Path.Combine ("a", "b") = "a\b"
    【解决方案6】:

    编辑:我刚刚意识到 file.exists 工作正常。这绝对是首选方法。如果应在不同的域帐户下访问共享,下面的代码将为您提供让 Windows 提示用户进行身份验证的选项。有一天可能会对某人有所帮助,所以我将代码留在这里。

    如果您需要使用不同的凭据访问 UNC 路径或管理员共享:MSDN

    要引导使用 WNetAddConnection2,请使用以下代码:

    using System;
    using System.Runtime.InteropServices;
    
    namespace Win32Api
    {
        public enum ResourceScope
        {
            RESOURCE_CONNECTED = 1,
            RESOURCE_GLOBALNET,
            RESOURCE_REMEMBERED,
            RESOURCE_RECENT,
            RESOURCE_CONTEXT
        };
    
        public enum ResourceType
        {
            RESOURCETYPE_ANY,
            RESOURCETYPE_DISK,
            RESOURCETYPE_PRINT,
            RESOURCETYPE_RESERVED = 8
        };
    
        [Flags]
        public enum ResourceUsage
        {
            RESOURCEUSAGE_CONNECTABLE = 0x00000001,
            RESOURCEUSAGE_CONTAINER = 0x00000002,
            RESOURCEUSAGE_NOLOCALDEVICE = 0x00000004,
            RESOURCEUSAGE_SIBLING = 0x00000008,
            RESOURCEUSAGE_ATTACHED = 0x00000010,
            RESOURCEUSAGE_ALL = (RESOURCEUSAGE_CONNECTABLE |
                                 RESOURCEUSAGE_CONTAINER | RESOURCEUSAGE_ATTACHED),
        };
    
        public enum ResourceDisplayType
        {
            RESOURCEDISPLAYTYPE_GENERIC,
            RESOURCEDISPLAYTYPE_DOMAIN,
            RESOURCEDISPLAYTYPE_SERVER,
            RESOURCEDISPLAYTYPE_SHARE,
            RESOURCEDISPLAYTYPE_FILE,
            RESOURCEDISPLAYTYPE_GROUP,
            RESOURCEDISPLAYTYPE_NETWORK,
            RESOURCEDISPLAYTYPE_ROOT,
            RESOURCEDISPLAYTYPE_SHAREADMIN,
            RESOURCEDISPLAYTYPE_DIRECTORY,
            RESOURCEDISPLAYTYPE_TREE,
            RESOURCEDISPLAYTYPE_NDSCONTAINER
        };
    
        [StructLayout(LayoutKind.Sequential)]
        public class NetResource
        {
            public ResourceScope Scope;
            public ResourceType Type;
            public ResourceDisplayType DisplayType;
            public ResourceUsage Usage;
            public string LocalName;
            public string RemoteName;
            public string Comment;
            public string Provider;
        };
    
        [Flags]
        public enum AddConnectionOptions
        {
            CONNECT_UPDATE_PROFILE = 0x00000001,
            CONNECT_UPDATE_RECENT = 0x00000002,
            CONNECT_TEMPORARY = 0x00000004,
            CONNECT_INTERACTIVE = 0x00000008,
            CONNECT_PROMPT = 0x00000010,
            CONNECT_NEED_DRIVE = 0x00000020,
            CONNECT_REFCOUNT = 0x00000040,
            CONNECT_REDIRECT = 0x00000080,
            CONNECT_LOCALDRIVE = 0x00000100,
            CONNECT_CURRENT_MEDIA = 0x00000200,
            CONNECT_DEFERRED = 0x00000400,
            CONNECT_RESERVED = unchecked((int)0xFF000000),
            CONNECT_COMMANDLINE = 0x00000800,
            CONNECT_CMD_SAVECRED = 0x00001000,
            CONNECT_CRED_RESET = 0x00002000
        }
    
        public static class NativeMethods
        {
            [DllImport("mpr.dll", EntryPoint = "WNetAddConnection2")]
            public static extern int WNetAddConnection2(
                NetResource netResource, string password,
                string username, AddConnectionOptions options);
    
            [DllImport("mpr.dll")]
            public static extern int WNetCancelConnection2(string name, int flags,
            bool force);
    
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多